Skip to content

LCORE-1880: Refactor 400 response#1554

Merged
tisnik merged 2 commits into
lightspeed-core:mainfrom
asimurka:refactor_400_response
Apr 21, 2026
Merged

LCORE-1880: Refactor 400 response#1554
tisnik merged 2 commits into
lightspeed-core:mainfrom
asimurka:refactor_400_response

Conversation

@asimurka
Copy link
Copy Markdown
Contributor

@asimurka asimurka commented Apr 21, 2026

Description

This PR refactors 400 error responses:

  • Removes file_upload example that should be only under 413 status code
  • Adds prompt id validators and corresponding 400 error responses
  • Updates unit tests to use valid prompt id formats
  • Updates OpenAPI schema

Type of change

  • Refactor
  • New feature
  • Bug fix
  • CVE fix
  • Optimization
  • Documentation Update
  • Configuration Update
  • Bump-up service version
  • Bump-up dependent library
  • Bump-up library or tool used for development (does not change the final image)
  • CI configuration change
  • Konflux configuration change
  • Unit tests improvement
  • Integration tests improvement
  • End to end tests improvement
  • Benchmarks improvement

Tools used to create PR

Identify any AI code assistants used in this PR (for transparency and review context)

  • Assisted-by: Cursor

Related Tickets & Documents

Checklist before requesting a review

  • I have performed a self-review of my code.
  • PR has passed all pre-merge test jobs.
  • If it is a core feature, I have added thorough tests.

Testing

  • Please provide detailed steps to perform tests related to this code change.
  • How were the fix/results from this change verified? Please provide relevant screenshots or results.

Summary by CodeRabbit

Release Notes

  • Documentation

    • Updated API error response documentation with improved examples and clearer descriptions of invalid request formats.
  • Bug Fixes

    • Added request validation to catch invalid inputs and provide better error feedback to API consumers.
  • Tests

    • Enhanced test coverage for request validation and error response scenarios.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 21, 2026

Walkthrough

The changes standardize error response documentation across endpoints by replacing a file_upload 400 error example with a prompt_id error example. Additionally, a new prompt ID validation function enforcing the pmpt_ prefix and 48-character hex format is introduced and integrated into prompt endpoint handlers. Tests are updated to use valid IDs and verify the new validation behavior, while conversation endpoints explicitly reference the validation examples in their 400 responses.

Changes

Cohort / File(s) Summary
OpenAPI Schema Updates
docs/openapi.json, src/models/responses.py
Updated OpenAPI spec to add 400 response for invalid request format with prompt_id example; replaced previous file_upload example. Changed operationId for A2A Jsonrpc from _post to _get. Updated BadRequestResponse example from file_upload to prompt_id with corresponding error messages.
Conversation Endpoints
src/app/endpoints/conversations_v1.py, src/app/endpoints/conversations_v2.py
Added explicit examples=["conversation_id"] parameter to 400 BadRequestResponse calls for GET, DELETE, and PUT operations on conversation endpoints.
Prompt Endpoint Handlers
src/app/endpoints/prompts.py
Integrated check_suid_prompt() validation into GET, PUT, and DELETE handlers for prompt IDs; raises 400 BadRequestResponse on validation failure. Added corresponding 400 responses to OpenAPI schema with prompt_id example.
Prompt ID Validation Utility
src/utils/suid.py
Added new check_suid_prompt(suid: str) -> bool function validating prompt ID format: requires pmpt_ prefix, 48-character hex suffix, returns False for invalid formats.
Vector Stores Endpoint
src/app/endpoints/vector_stores.py
Removed BadRequestResponse import and 400 response entry from file upload endpoint.
Test Updates
tests/unit/app/endpoints/test_prompts.py, tests/unit/models/responses/test_error_responses.py
Updated prompt endpoint tests to use valid pmpt_ IDs with 48-hex suffix; added test cases asserting 400 responses for invalid prompt ID formats. Updated error response test assertions to expect prompt_id example instead of file_upload.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'LCORE-1880: Refactor 400 response' directly describes the main changes in the PR, which focus on refactoring HTTP 400 error responses across multiple endpoints.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
✨ Simplify code
  • Create PR with simplified code

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.


file_responses: dict[int | str, dict[str, Any]] = {
200: FileResponse.openapi_response(),
400: BadRequestResponse.openapi_response(examples=["file_upload"]),
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not raised in the endpoint anyway.
File operation errors are raised under 413.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
src/app/endpoints/prompts.py (1)

188-219: ⚠️ Potential issue | 🟡 Minor

Refresh curl examples to use valid prompt IDs.

The docstrings still show pmpt_abc123, but Lines 216, 281, and 342 now reject anything other than pmpt_ plus 48 hex characters. These examples will return 400 if copied.

📝 Proposed documentation fix
-        curl http://localhost:8080/v1/prompts/pmpt_abc123?version=1
+        curl http://localhost:8080/v1/prompts/pmpt_0123456789abcdef0123456789abcdef0123456789abcdef?version=1
-        curl -X PUT http://localhost:8080/v1/prompts/pmpt_abc123 \\
+        curl -X PUT http://localhost:8080/v1/prompts/pmpt_0123456789abcdef0123456789abcdef0123456789abcdef \\
-        curl -X DELETE http://localhost:8080/v1/prompts/pmpt_abc123
+        curl -X DELETE http://localhost:8080/v1/prompts/pmpt_0123456789abcdef0123456789abcdef0123456789abcdef

Also applies to: 250-285, 312-345

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/app/endpoints/prompts.py` around lines 188 - 219, The docstring curl
examples in the GET /prompts/{prompt_id} handler use invalid IDs (e.g.,
"pmpt_abc123") which will be rejected by the check_suid_prompt validation;
update all examples in the prompt retrieval docstring (and the other occurrences
noted) to use a valid prompt identifier format such as "pmpt_" followed by 48
hexadecimal characters (match the validator used by check_suid_prompt) so that
copying the example won’t produce a 400; locate the examples in the prompts
handler docstring and any other docstrings around the GET/related prompt
endpoints (references: check_suid_prompt and the GET /prompts/{prompt_id}
docstring) and replace the placeholder IDs with a conforming example.
src/models/responses.py (1)

2200-2206: ⚠️ Potential issue | 🟡 Minor

Update prompt examples to the new 48-hex ID format.

These OpenAPI examples still use a 40-hex pmpt_... suffix. With the new validator, copied IDs from the success/not-found examples are rejected as 400 before they can represent valid success or 404 flows.

📝 Proposed documentation fix
-                            "pmpt_0123456789abcdef0123456789abcdef01234567 does not exist"
+                            "pmpt_0123456789abcdef0123456789abcdef0123456789abcdef does not exist"
-                    "prompt_id": "pmpt_0123456789abcdef0123456789abcdef01234567",
+                    "prompt_id": "pmpt_0123456789abcdef0123456789abcdef0123456789abcdef",
-                            "prompt_id": "pmpt_0123456789abcdef0123456789abcdef01234567",
+                            "prompt_id": "pmpt_0123456789abcdef0123456789abcdef0123456789abcdef",
-        examples=["pmpt_0123456789abcdef0123456789abcdef01234567"],
+        examples=["pmpt_0123456789abcdef0123456789abcdef0123456789abcdef"],
-                        "prompt_id": "pmpt_0123456789abcdef0123456789abcdef01234567",
+                        "prompt_id": "pmpt_0123456789abcdef0123456789abcdef0123456789abcdef",

Also applies to: 3007-3009, 3033-3035, 3050-3053, 3091-3100

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/models/responses.py` around lines 2200 - 2206, Replace the old 40-hex
example prompt IDs with the new 48-hex format wherever the sample string
"pmpt_0123456789abcdef0123456789abcdef01234567" (and its siblings in the example
response/cause fields) appears; update each example value to start with "pmpt_"
followed by 48 hex characters (e.g., extend the hex suffix to 48 characters) so
the examples pass the new validator—apply this change to all example blocks that
reference prompt IDs (including the other example groups noted in the comment).
docs/openapi.json (1)

9941-9959: ⚠️ Potential issue | 🟠 Major

Keep the POST A2A operationId distinct from GET.

Line 9959 duplicates the GET operation ID from line 9941. Both GET and POST methods on the /a2a endpoint share the same operationId ("handle_a2a_jsonrpc_a2a_get"), which violates OpenAPI specifications and causes OpenAPI code generators to overwrite or collide on generated client methods.

🛠️ Proposed fix
-                "operationId": "handle_a2a_jsonrpc_a2a_post",
+                "operationId": "handle_a2a_jsonrpc_a2a_post",
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/openapi.json` around lines 9941 - 9959, The POST operation on the /a2a
endpoint reuses the GET operationId "handle_a2a_jsonrpc_a2a_get"; update the
POST operationId to a distinct identifier (for example
"handle_a2a_jsonrpc_a2a_post") so GET and POST have unique operationIds; locate
the POST block in the OpenAPI fragment where "operationId":
"handle_a2a_jsonrpc_a2a_get" appears and replace it with the new unique
operationId.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/utils/suid.py`:
- Around line 130-140: The current validation accepts leading '+', '-', or
whitespace because int(hex_part, 16) is permissive; change the check in the SUID
validation (the block using prefix, hex_part, and int(hex_part, 16)) to ensure
hex_part is exactly 48 hex characters by validating against a strict hex pattern
(e.g., match against /^[0-9a-fA-F]{48}$/) or using str checks (all characters in
string.hexdigits and len == 48) before calling int(); return False for any
non-matching hex_part so only pure hexadecimal characters are accepted.

In `@tests/unit/app/endpoints/test_prompts.py`:
- Around line 26-29: Annotate the module-level constants VALID_PMPT_ID,
VALID_PMPT_ID_B, and VALID_PMPT_ID_NOT_FOUND with Final[str] to satisfy the
project's typing rule; import Final from typing if not already imported and
update the declarations to include the type hint (e.g., VALID_PMPT_ID:
Final[str] = "...") so these constants are explicitly typed as final strings.
- Around line 337-385: The tests call handlers (get_prompt_handler,
update_prompt_handler, delete_prompt_handler) which invoke
check_configuration_loaded(configuration) first, so patch the app configuration
as loaded before invoking these handlers to avoid the 500 branch; update each
test to use the existing fixture that marks configuration as loaded (e.g.,
patch_configuration_loaded) or explicitly set the configuration object used by
check_configuration_loaded to a loaded state (configuration.loaded = True) so
the handlers proceed to prompt-id validation and the 400 assertions become
deterministic.

---

Outside diff comments:
In `@docs/openapi.json`:
- Around line 9941-9959: The POST operation on the /a2a endpoint reuses the GET
operationId "handle_a2a_jsonrpc_a2a_get"; update the POST operationId to a
distinct identifier (for example "handle_a2a_jsonrpc_a2a_post") so GET and POST
have unique operationIds; locate the POST block in the OpenAPI fragment where
"operationId": "handle_a2a_jsonrpc_a2a_get" appears and replace it with the new
unique operationId.

In `@src/app/endpoints/prompts.py`:
- Around line 188-219: The docstring curl examples in the GET
/prompts/{prompt_id} handler use invalid IDs (e.g., "pmpt_abc123") which will be
rejected by the check_suid_prompt validation; update all examples in the prompt
retrieval docstring (and the other occurrences noted) to use a valid prompt
identifier format such as "pmpt_" followed by 48 hexadecimal characters (match
the validator used by check_suid_prompt) so that copying the example won’t
produce a 400; locate the examples in the prompts handler docstring and any
other docstrings around the GET/related prompt endpoints (references:
check_suid_prompt and the GET /prompts/{prompt_id} docstring) and replace the
placeholder IDs with a conforming example.

In `@src/models/responses.py`:
- Around line 2200-2206: Replace the old 40-hex example prompt IDs with the new
48-hex format wherever the sample string
"pmpt_0123456789abcdef0123456789abcdef01234567" (and its siblings in the example
response/cause fields) appears; update each example value to start with "pmpt_"
followed by 48 hex characters (e.g., extend the hex suffix to 48 characters) so
the examples pass the new validator—apply this change to all example blocks that
reference prompt IDs (including the other example groups noted in the comment).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 8e650af1-a5f7-4f42-a73e-35e9f63c6652

📥 Commits

Reviewing files that changed from the base of the PR and between ac428f3 and c43d01d.

📒 Files selected for processing (9)
  • docs/openapi.json
  • src/app/endpoints/conversations_v1.py
  • src/app/endpoints/conversations_v2.py
  • src/app/endpoints/prompts.py
  • src/app/endpoints/vector_stores.py
  • src/models/responses.py
  • src/utils/suid.py
  • tests/unit/app/endpoints/test_prompts.py
  • tests/unit/models/responses/test_error_responses.py
💤 Files with no reviewable changes (1)
  • src/app/endpoints/vector_stores.py
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (15)
  • GitHub Check: unit_tests (3.12)
  • GitHub Check: integration_tests (3.13)
  • GitHub Check: unit_tests (3.13)
  • GitHub Check: integration_tests (3.12)
  • GitHub Check: black
  • GitHub Check: Pylinter
  • GitHub Check: build-pr
  • GitHub Check: E2E: library mode / ci / group 3
  • GitHub Check: E2E: library mode / ci / group 2
  • GitHub Check: E2E: server mode / ci / group 1
  • GitHub Check: E2E: server mode / ci / group 2
  • GitHub Check: E2E: server mode / ci / group 3
  • GitHub Check: E2E: library mode / ci / group 1
  • GitHub Check: E2E Tests for Lightspeed Evaluation job
  • GitHub Check: Konflux kflux-prd-rh02 / lightspeed-stack-on-pull-request
🧰 Additional context used
📓 Path-based instructions (4)
**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

**/*.py: Use absolute imports for internal modules: from authentication import get_auth_dependency
Import FastAPI dependencies with: from fastapi import APIRouter, HTTPException, Request, status, Depends
Import Llama Stack client with: from llama_stack_client import AsyncLlamaStackClient
Check constants.py for shared constants before defining new ones
All modules start with descriptive docstrings explaining purpose
Use logger = get_logger(__name__) from log.py for module logging
Type aliases defined at module level for clarity
Use Final[type] as type hint for all constants
All functions require docstrings with brief descriptions
Complete type annotations for parameters and return types in functions
Use typing_extensions.Self for model validators in Pydantic models
Use modern union type syntax str | int instead of Union[str, int]
Use Optional[Type] for optional type hints
Use snake_case with descriptive, action-oriented function names (get_, validate_, check_)
Avoid in-place parameter modification anti-patterns; return new data structures instead
Use async def for I/O operations and external API calls
Handle APIConnectionError from Llama Stack in error handling
Use standard log levels with clear purposes: debug, info, warning, error
All classes require descriptive docstrings explaining purpose
Use PascalCase for class names with standard suffixes: Configuration, Error/Exception, Resolver, Interface
Use ABC for abstract base classes with @abstractmethod decorators
Use @model_validator and @field_validator for Pydantic model validation
Complete type annotations for all class attributes; use specific types, not Any
Follow Google Python docstring conventions with Parameters, Returns, Raises, and Attributes sections

Files:

  • tests/unit/models/responses/test_error_responses.py
  • src/app/endpoints/conversations_v2.py
  • src/utils/suid.py
  • src/models/responses.py
  • tests/unit/app/endpoints/test_prompts.py
  • src/app/endpoints/prompts.py
  • src/app/endpoints/conversations_v1.py
tests/{unit,integration}/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

tests/{unit,integration}/**/*.py: Use pytest for all unit and integration tests
Do not use unittest; pytest is the standard for this project
Use pytest-mock for AsyncMock objects in tests
Use marker pytest.mark.asyncio for async tests
Unit tests require 60% coverage, integration tests 10%

Files:

  • tests/unit/models/responses/test_error_responses.py
  • tests/unit/app/endpoints/test_prompts.py
src/app/endpoints/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

Use FastAPI HTTPException with appropriate status codes for API endpoints

Files:

  • src/app/endpoints/conversations_v2.py
  • src/app/endpoints/prompts.py
  • src/app/endpoints/conversations_v1.py
src/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

Pydantic models extend ConfigurationBase for config, BaseModel for data models

Files:

  • src/app/endpoints/conversations_v2.py
  • src/utils/suid.py
  • src/models/responses.py
  • src/app/endpoints/prompts.py
  • src/app/endpoints/conversations_v1.py
🧠 Learnings (3)
📚 Learning: 2026-04-06T20:18:07.852Z
Learnt from: major
Repo: lightspeed-core/lightspeed-stack PR: 1463
File: src/app/endpoints/rlsapi_v1.py:266-271
Timestamp: 2026-04-06T20:18:07.852Z
Learning: In the lightspeed-stack codebase, within `src/app/endpoints/` inference/MCP endpoints, treat `tools: Optional[list[Any]]` in MCP tool definitions as an intentional, consistent typing pattern (used across `query`, `responses`, `streaming_query`, `rlsapi_v1`). Do not raise or suggest this as a typing issue during code review; changing it in isolation could break endpoint typing consistency across the codebase.

Applied to files:

  • src/app/endpoints/conversations_v2.py
  • src/app/endpoints/prompts.py
  • src/app/endpoints/conversations_v1.py
📚 Learning: 2026-01-12T10:58:40.230Z
Learnt from: blublinsky
Repo: lightspeed-core/lightspeed-stack PR: 972
File: src/models/config.py:459-513
Timestamp: 2026-01-12T10:58:40.230Z
Learning: In lightspeed-core/lightspeed-stack, for Python files under src/models, when a user claims a fix is done but the issue persists, verify the current code state before accepting the fix. Steps: review the diff, fetch the latest changes, run relevant tests, reproduce the issue, search the codebase for lingering references to the original problem, confirm the fix is applied and not undone by subsequent commits, and validate with local checks to ensure the issue is resolved.

Applied to files:

  • src/models/responses.py
📚 Learning: 2026-02-25T07:46:33.545Z
Learnt from: asimurka
Repo: lightspeed-core/lightspeed-stack PR: 1211
File: src/models/responses.py:8-16
Timestamp: 2026-02-25T07:46:33.545Z
Learning: In the Python codebase, requests.py should use OpenAIResponseInputTool as Tool while responses.py uses OpenAIResponseTool as Tool. This difference is intentional due to differing schemas for input vs output tools in llama-stack-api. Apply this distinction consistently to other models under src/models (e.g., ensure request-related tools use the InputTool variant and response-related tools use the ResponseTool variant). If adding new tools, choose the corresponding InputTool or Tool class based on whether the tool represents input or output, and document the rationale in code comments.

Applied to files:

  • src/models/responses.py
🔇 Additional comments (7)
tests/unit/models/responses/test_error_responses.py (1)

87-98: LGTM — the 400 example assertions now cover prompt_id.

The test verifies both the new example label and the expected "Invalid prompt ID format" response.

src/app/endpoints/conversations_v2.py (1)

35-68: LGTM — 400 examples are correctly scoped to conversation IDs.

This keeps the OpenAPI docs aligned with the actual invalid-conversation-ID validation path.

src/app/endpoints/conversations_v1.py (1)

59-95: LGTM — conversation v1 now documents only the relevant 400 example.

Using examples=["conversation_id"] prevents prompt-specific 400 examples from appearing on conversation endpoints.

src/models/responses.py (1)

1833-1836: LGTM — the 400 example now targets invalid prompt IDs.

The renamed prompt_id example matches the new prompt endpoint 400 responses.

src/app/endpoints/prompts.py (1)

18-32: LGTM — prompt endpoints now document the new 400 response.

The prompt_id bad-request example is correctly wired into GET, PUT, and DELETE prompt response maps.

Also applies to: 55-77

tests/unit/app/endpoints/test_prompts.py (1)

112-249: LGTM — existing tests now use validator-compatible prompt IDs.

The updated success, not-found, list, and backend-error tests align with the new pmpt_ plus 48-hex format.

Also applies to: 286-333

docs/openapi.json (1)

2436-2455: Prompt ID 400 examples look consistent.

The new prompt_id BadRequest examples consistently document the invalid prompt ID format and align with the PR objective to remove the misplaced file_upload 400 example.

Also applies to: 2680-2699, 2921-2940, 11023-11026

Comment thread src/utils/suid.py
Comment thread tests/unit/app/endpoints/test_prompts.py
Comment thread tests/unit/app/endpoints/test_prompts.py
Copy link
Copy Markdown
Contributor

@tisnik tisnik left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@tisnik tisnik merged commit 30b4e9e into lightspeed-core:main Apr 21, 2026
35 of 36 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants