Skip to content

LCORE-1239: better query parameter definition#1134

Merged
tisnik merged 3 commits intolightspeed-core:mainfrom
tisnik:lcore-1239-better-query-param
Feb 11, 2026
Merged

LCORE-1239: better query parameter definition#1134
tisnik merged 3 commits intolightspeed-core:mainfrom
tisnik:lcore-1239-better-query-param

Conversation

@tisnik
Copy link
Contributor

@tisnik tisnik commented Feb 11, 2026

Description

LCORE-1239: better query parameter definition

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

  • Assisted-by: N/A
  • Generated by: N/A

Related Tickets & Documents

  • Related Issue #LCORE-1239

Summary by CodeRabbit

Release Notes

  • New Features

    • Added optional model_type query parameter to the Models endpoint for filtering models by type (supports "llm" and "embeddings").
  • Documentation

    • Updated API documentation to document the new model_type parameter and filtering capability.
    • Updated operation identifiers in API specifications for consistency.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 11, 2026

Walkthrough

This PR refactors the /v1/models endpoint to use a structured ModelFilter query parameter instead of a raw optional string, enabling type-safe model filtering. Documentation is updated across OpenAPI specs and docstrings. The A2A endpoint operationId is also updated from POST to GET.

Changes

Cohort / File(s) Summary
OpenAPI Documentation
docs/openapi.json, docs/openapi.md
Added model_type parameter documentation to the /v1/models endpoint with description and examples. Updated A2A endpoint operationId from handle_a2a_jsonrpc_a2a_post to handle_a2a_jsonrpc_a2a_get.
ModelFilter Implementation
src/models/requests.py
Introduced new ModelFilter class with optional model_type field, description, examples, and extra="forbid" configuration.
Endpoint Handler
src/app/endpoints/models.py
Updated models_endpoint_handler signature to accept model_type: Annotated[ModelFilter, Query()] instead of Optional[str]. Modified filtering logic to check model_type.model_type and updated imports accordingly.
Tests
tests/unit/app/endpoints/test_models.py
Updated test cases to pass ModelFilter-wrapped values for model_type parameter. Added SubTests blocks for multiple model_type filtering scenarios (llm, embeddings, invalid values).

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • #1019: Modifies A2A operationId identifiers in openapi.json (inverse change from POST to GET).
  • #1126: Implements model_type-based filtering on the same /v1/models endpoint using a different parameter type approach.
🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly relates to the main change: refactoring how query parameters are defined (specifically the model_type parameter) to use a structured ModelFilter type instead of Optional[str].
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

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

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Tip

Issue Planner is now in beta. Read the docs and try it out! Share your feedback on Discord.


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.

Copy link
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 (1)
docs/openapi.json (1)

4349-4373: ⚠️ Potential issue | 🟠 Major

Make the /a2a POST operationId unique.
Line 4355 and Line 4373 currently use the same operationId (handle_a2a_jsonrpc_a2a_get). OpenAPI requires unique operationIds, and this will break client codegen and tooling. Set the POST operationId to a distinct value (e.g., _post).

🔧 Proposed fix
-                "operationId": "handle_a2a_jsonrpc_a2a_get",
+                "operationId": "handle_a2a_jsonrpc_a2a_post",
🤖 Fix all issues with AI agents
In `@src/models/requests.py`:
- Around line 526-531: The docstring for the ModelFilter class is inconsistent:
it calls model_type "Required" but the field model_type is Optional[str] with
default None and described elsewhere as an optional filter; update the class
docstring (ModelFilter) Attributes section so model_type is described as an
optional filter (e.g., "Optional model type, such as 'llm', 'embeddings', etc.")
and remove or replace the word "Required" to reflect the Optional[str] typing
and default None.

In `@tests/unit/app/endpoints/test_models.py`:
- Around line 52-57: The assertions inside the pytest.raises context are
unreachable; move the two asserts that check e.value.status_code and
e.value.detail outside the with block so they run after the exception is caught.
Keep the with pytest.raises(HTTPException) as e: wrapper around the await
models_endpoint_handler(...) call (which uses ModelFilter(model_type=None)),
then dedent the two assertions so they execute against e.value after the with
block completes.
- Around line 468-473: The two assertions that check the raised HTTPException's
status and detail are incorrectly placed inside the pytest.raises context and
therefore never executed; after calling models_endpoint_handler inside the with
pytest.raises(HTTPException) as e: block, move the asserts that reference
e.value (status_code and detail["response"]) to immediately after the with block
so they run against the captured exception (reference: models_endpoint_handler
and the pytest.raises usage in this test).
🧹 Nitpick comments (3)
src/app/endpoints/models.py (1)

80-80: Consider renaming the parameter to model_filter for clarity.

Currently model_type.model_type (line 121/125) reads awkwardly because the parameter and the field share the same name. Since FastAPI derives query-parameter names from the BaseModel field names (not the function-parameter name), renaming this won't change the API contract.

Proposed rename
 async def models_endpoint_handler(
     request: Request,
     auth: Annotated[AuthTuple, Depends(get_auth_dependency())],
-    model_type: Annotated[ModelFilter, Query()],
+    model_filter: Annotated[ModelFilter, Query()],
 ) -> ModelsResponse:

And update references at lines 121/125:

-        if model_type.model_type is not None:
+        if model_filter.model_type is not None:
             parsed_models = [
                 model
                 for model in parsed_models
-                if model["model_type"] == model_type.model_type
+                if model["model_type"] == model_filter.model_type
             ]
src/models/requests.py (1)

533-538: Confirm the strict query parameter validation is intentional and properly documented.

The extra: "forbid" configuration on ModelFilter is technically correct and will reject unexpected query parameters with a 422 response. However, this strict validation isn't documented in the OpenAPI schema (which shows only 200, 401, 403, 500, 503 for /v1/models) nor is there a test verifying this behavior.

If this strictness is intentional (and the "Reject unknown fields" comment suggests it is), either:

  1. Add 422 to the OpenAPI responses for the /models endpoint, or
  2. Add a test case explicitly verifying that extra query parameters are rejected

This prevents API consumers from being unexpectedly rejected with a 422 when they send unknown query parameters.

docs/openapi.md (1)

270-270: Missing type for model_type parameter.

The Type column is empty for the model_type parameter. Consider specifying string to match the underlying Optional[str] field in ModelFilter.

-| model_type |  | False | Optional filter to return only models matching this type |
+| model_type | string | False | Optional filter to return only models matching this type |

Comment on lines +526 to +531
class ModelFilter(BaseModel):
"""Model representing a query parameter to select models by its type.

Attributes:
model_type: Required model type, such as 'llm', 'embeddings' etc.
"""
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Docstring says "Required" but the field is Optional.

The Attributes section states "Required model type" but model_type is Optional[str] with a default of None. The field description also says "Optional filter." Update the docstring for consistency.

Proposed fix
-    """Model representing a query parameter to select models by its type.
+    """Model representing a query parameter to filter models by type.
 
     Attributes:
-        model_type: Required model type, such as 'llm', 'embeddings' etc.
+        model_type: Optional model type filter, such as 'llm', 'embeddings' etc.
     """
🤖 Prompt for AI Agents
In `@src/models/requests.py` around lines 526 - 531, The docstring for the
ModelFilter class is inconsistent: it calls model_type "Required" but the field
model_type is Optional[str] with default None and described elsewhere as an
optional filter; update the class docstring (ModelFilter) Attributes section so
model_type is described as an optional filter (e.g., "Optional model type, such
as 'llm', 'embeddings', etc.") and remove or replace the word "Required" to
reflect the Optional[str] typing and default None.

Comment on lines 52 to 57
with pytest.raises(HTTPException) as e:
await models_endpoint_handler(request=request, auth=auth)
await models_endpoint_handler(
request=request, auth=auth, model_type=ModelFilter(model_type=None)
)
assert e.value.status_code == status.HTTP_500_INTERNAL_SERVER_ERROR
assert e.value.detail["response"] == "Configuration is not loaded" # type: ignore
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Assertions inside pytest.raises block are unreachable.

Lines 56-57 are inside the with pytest.raises(HTTPException) block. When the await on line 53 raises HTTPException, the context manager catches it and control exits the with block — the assertions on lines 56-57 never execute. This is a pre-existing issue, but since you're modifying this block, consider moving the assertions outside:

Proposed fix
     with pytest.raises(HTTPException) as e:
         await models_endpoint_handler(
             request=request, auth=auth, model_type=ModelFilter(model_type=None)
         )
-        assert e.value.status_code == status.HTTP_500_INTERNAL_SERVER_ERROR
-        assert e.value.detail["response"] == "Configuration is not loaded"  # type: ignore
+    assert e.value.status_code == status.HTTP_500_INTERNAL_SERVER_ERROR
+    assert e.value.detail["response"] == "Configuration is not loaded"  # type: ignore
🤖 Prompt for AI Agents
In `@tests/unit/app/endpoints/test_models.py` around lines 52 - 57, The assertions
inside the pytest.raises context are unreachable; move the two asserts that
check e.value.status_code and e.value.detail outside the with block so they run
after the exception is caught. Keep the with pytest.raises(HTTPException) as e:
wrapper around the await models_endpoint_handler(...) call (which uses
ModelFilter(model_type=None)), then dedent the two assertions so they execute
against e.value after the with block completes.

Comment on lines 468 to 473
with pytest.raises(HTTPException) as e:
await models_endpoint_handler(request=request, auth=auth)
await models_endpoint_handler(
request=request, auth=auth, model_type=ModelFilter(model_type=None)
)
assert e.value.status_code == status.HTTP_503_SERVICE_UNAVAILABLE
assert e.value.detail["response"] == "Unable to connect to Llama Stack" # type: ignore
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Same unreachable-assertions bug as above.

Lines 472-473 are inside the with pytest.raises block and will never execute. Move them outside the with block, consistent with the correct pattern used in test_models_endpoint_handler_configuration_loaded (lines 121-126).

Proposed fix
     with pytest.raises(HTTPException) as e:
         await models_endpoint_handler(
             request=request, auth=auth, model_type=ModelFilter(model_type=None)
         )
-        assert e.value.status_code == status.HTTP_503_SERVICE_UNAVAILABLE
-        assert e.value.detail["response"] == "Unable to connect to Llama Stack"  # type: ignore
-        assert "Unable to connect to Llama Stack" in e.value.detail["cause"]  # type: ignore
+    assert e.value.status_code == status.HTTP_503_SERVICE_UNAVAILABLE
+    assert e.value.detail["response"] == "Unable to connect to Llama Stack"  # type: ignore
+    assert "Unable to connect to Llama Stack" in e.value.detail["cause"]  # type: ignore
🤖 Prompt for AI Agents
In `@tests/unit/app/endpoints/test_models.py` around lines 468 - 473, The two
assertions that check the raised HTTPException's status and detail are
incorrectly placed inside the pytest.raises context and therefore never
executed; after calling models_endpoint_handler inside the with
pytest.raises(HTTPException) as e: block, move the asserts that reference
e.value (status_code and detail["response"]) to immediately after the with block
so they run against the captured exception (reference: models_endpoint_handler
and the pytest.raises usage in this test).

@tisnik tisnik merged commit ce51490 into lightspeed-core:main Feb 11, 2026
20 of 22 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.

1 participant