mirror of
https://github.com/meta-llama/llama-stack.git
synced 2025-12-04 10:10:36 +00:00
feat: Implement FastAPI router system (#4191)
# What does this PR do? This commit introduces a new FastAPI router-based system for defining API endpoints, enabling a migration path away from the legacy @webmethod decorator system. The implementation includes router infrastructure, migration of the Batches API as the first example, and updates to server, OpenAPI generation, and inspection systems to support both routing approaches. The router infrastructure consists of a router registry system that allows APIs to register FastAPI router factories, which are then automatically discovered and included in the server application. Standard error responses are centralized in router_utils to ensure consistent OpenAPI specification generation with proper $ref references to component responses. The Batches API has been migrated to demonstrate the new pattern. The protocol definition and models remain in llama_stack_api/batches, maintaining clear separation between API contracts and server implementation. The FastAPI router implementation lives in llama_stack/core/server/routers/batches, following the established pattern where API contracts are defined in llama_stack_api and server routing logic lives in llama_stack/core/server. The server now checks for registered routers before falling back to the legacy webmethod-based route discovery, ensuring backward compatibility during the migration period. The OpenAPI generator has been updated to handle both router-based and webmethod-based routes, correctly extracting metadata from FastAPI route decorators and Pydantic Field descriptions. The inspect endpoint now includes routes from both systems, with proper filtering for deprecated routes and API levels. Response descriptions are now explicitly defined in router decorators, ensuring the generated OpenAPI specification matches the previous format. Error responses use $ref references to component responses (BadRequest400, TooManyRequests429, etc.) as required by the specification. This is neat and will allow us to remove a lot of boiler plate code from our generator once the migration is done. This implementation provides a foundation for incrementally migrating other APIs to the router system while maintaining full backward compatibility with existing webmethod-based APIs. Closes: https://github.com/llamastack/llama-stack/issues/4188 ## Test Plan CI, the server should start, same routes should be visible. ``` curl http://localhost:8321/v1/inspect/routes | jq '.data[] | select(.route | contains("batches"))' ``` Also: ``` uv run pytest tests/integration/batches/ -vv --stack-config=http://localhost:8321 ================================================== test session starts ================================================== platform darwin -- Python 3.12.8, pytest-8.4.2, pluggy-1.6.0 -- /Users/leseb/Documents/AI/llama-stack/.venv/bin/python3 cachedir: .pytest_cache metadata: {'Python': '3.12.8', 'Platform': 'macOS-26.0.1-arm64-arm-64bit', 'Packages': {'pytest': '8.4.2', 'pluggy': '1.6.0'}, 'Plugins': {'anyio': '4.9.0', 'html': '4.1.1', 'socket': '0.7.0', 'asyncio': '1.1.0', 'json-report': '1.5.0', 'timeout': '2.4.0', 'metadata': '3.1.1', 'cov': '6.2.1', 'nbval': '0.11.0'}} rootdir: /Users/leseb/Documents/AI/llama-stack configfile: pyproject.toml plugins: anyio-4.9.0, html-4.1.1, socket-0.7.0, asyncio-1.1.0, json-report-1.5.0, timeout-2.4.0, metadata-3.1.1, cov-6.2.1, nbval-0.11.0 asyncio: mode=Mode.AUTO, asyncio_default_fixture_loop_scope=None, asyncio_default_test_loop_scope=function collected 24 items tests/integration/batches/test_batches.py::TestBatchesIntegration::test_batch_creation_and_retrieval[None] SKIPPED [ 4%] tests/integration/batches/test_batches.py::TestBatchesIntegration::test_batch_listing[None] SKIPPED [ 8%] tests/integration/batches/test_batches.py::TestBatchesIntegration::test_batch_immediate_cancellation[None] SKIPPED [ 12%] tests/integration/batches/test_batches.py::TestBatchesIntegration::test_batch_e2e_chat_completions[None] SKIPPED [ 16%] tests/integration/batches/test_batches.py::TestBatchesIntegration::test_batch_e2e_completions[None] SKIPPED [ 20%] tests/integration/batches/test_batches_errors.py::TestBatchesErrorHandling::test_batch_invalid_endpoint[None] SKIPPED [ 25%] tests/integration/batches/test_batches_errors.py::TestBatchesErrorHandling::test_batch_cancel_completed[None] SKIPPED [ 29%] tests/integration/batches/test_batches_errors.py::TestBatchesErrorHandling::test_batch_missing_required_fields[None] SKIPPED [ 33%] tests/integration/batches/test_batches_errors.py::TestBatchesErrorHandling::test_batch_invalid_completion_window[None] SKIPPED [ 37%] tests/integration/batches/test_batches_errors.py::TestBatchesErrorHandling::test_batch_streaming_not_supported[None] SKIPPED [ 41%] tests/integration/batches/test_batches_errors.py::TestBatchesErrorHandling::test_batch_mixed_streaming_requests[None] SKIPPED [ 45%] tests/integration/batches/test_batches_errors.py::TestBatchesErrorHandling::test_batch_endpoint_mismatch[None] SKIPPED [ 50%] tests/integration/batches/test_batches_errors.py::TestBatchesErrorHandling::test_batch_missing_required_body_fields[None] SKIPPED [ 54%] tests/integration/batches/test_batches_errors.py::TestBatchesErrorHandling::test_batch_invalid_metadata_types[None] SKIPPED [ 58%] tests/integration/batches/test_batches.py::TestBatchesIntegration::test_batch_e2e_embeddings[None] SKIPPED [ 62%] tests/integration/batches/test_batches_errors.py::TestBatchesErrorHandling::test_batch_nonexistent_file_id PASSED [ 66%] tests/integration/batches/test_batches_errors.py::TestBatchesErrorHandling::test_batch_malformed_jsonl PASSED [ 70%] tests/integration/batches/test_batches_errors.py::TestBatchesErrorHandling::test_file_malformed_batch_file[empty] XFAIL [ 75%] tests/integration/batches/test_batches_errors.py::TestBatchesErrorHandling::test_file_malformed_batch_file[malformed] XFAIL [ 79%] tests/integration/batches/test_batches_errors.py::TestBatchesErrorHandling::test_batch_retrieve_nonexistent PASSED [ 83%] tests/integration/batches/test_batches_errors.py::TestBatchesErrorHandling::test_batch_cancel_nonexistent PASSED [ 87%] tests/integration/batches/test_batches_errors.py::TestBatchesErrorHandling::test_batch_error_handling_invalid_model PASSED [ 91%] tests/integration/batches/test_batches_idempotency.py::TestBatchesIdempotencyIntegration::test_idempotent_batch_creation_successful PASSED [ 95%] tests/integration/batches/test_batches_idempotency.py::TestBatchesIdempotencyIntegration::test_idempotency_conflict_with_different_params PASSED [100%] ================================================= slowest 10 durations ================================================== 1.01s call tests/integration/batches/test_batches_idempotency.py::TestBatchesIdempotencyIntegration::test_idempotent_batch_creation_successful 0.21s call tests/integration/batches/test_batches_errors.py::TestBatchesErrorHandling::test_batch_nonexistent_file_id 0.17s call tests/integration/batches/test_batches_errors.py::TestBatchesErrorHandling::test_batch_malformed_jsonl 0.12s call tests/integration/batches/test_batches_errors.py::TestBatchesErrorHandling::test_batch_error_handling_invalid_model 0.05s setup tests/integration/batches/test_batches.py::TestBatchesIntegration::test_batch_creation_and_retrieval[None] 0.02s call tests/integration/batches/test_batches_errors.py::TestBatchesErrorHandling::test_file_malformed_batch_file[empty] 0.01s call tests/integration/batches/test_batches_idempotency.py::TestBatchesIdempotencyIntegration::test_idempotency_conflict_with_different_params 0.01s call tests/integration/batches/test_batches_errors.py::TestBatchesErrorHandling::test_file_malformed_batch_file[malformed] 0.01s call tests/integration/batches/test_batches_errors.py::TestBatchesErrorHandling::test_batch_retrieve_nonexistent 0.00s call tests/integration/batches/test_batches_errors.py::TestBatchesErrorHandling::test_batch_cancel_nonexistent ======================================= 7 passed, 15 skipped, 2 xfailed in 1.78s ======================================== ``` --------- Signed-off-by: Sébastien Han <seb@redhat.com>
This commit is contained in:
parent
4237eb4aaa
commit
7f43051a63
22 changed files with 1095 additions and 248 deletions
82
docs/static/stainless-llama-stack-spec.yaml
vendored
82
docs/static/stainless-llama-stack-spec.yaml
vendored
|
|
@ -37,7 +37,7 @@ paths:
|
|||
description: Default Response
|
||||
tags:
|
||||
- Batches
|
||||
summary: List Batches
|
||||
summary: List all batches for the current user.
|
||||
description: List all batches for the current user.
|
||||
operationId: list_batches_v1_batches_get
|
||||
parameters:
|
||||
|
|
@ -48,14 +48,18 @@ paths:
|
|||
anyOf:
|
||||
- type: string
|
||||
- type: 'null'
|
||||
description: Optional cursor for pagination. Returns batches after this ID.
|
||||
title: After
|
||||
description: Optional cursor for pagination. Returns batches after this ID.
|
||||
- name: limit
|
||||
in: query
|
||||
required: false
|
||||
schema:
|
||||
type: integer
|
||||
description: Maximum number of batches to return. Defaults to 20.
|
||||
default: 20
|
||||
title: Limit
|
||||
description: Maximum number of batches to return. Defaults to 20.
|
||||
post:
|
||||
responses:
|
||||
'200':
|
||||
|
|
@ -76,9 +80,11 @@ paths:
|
|||
default:
|
||||
$ref: '#/components/responses/DefaultError'
|
||||
description: Default Response
|
||||
'409':
|
||||
description: 'Conflict: The idempotency key was previously used with different parameters.'
|
||||
tags:
|
||||
- Batches
|
||||
summary: Create Batch
|
||||
summary: Create a new batch for processing multiple API requests.
|
||||
description: Create a new batch for processing multiple API requests.
|
||||
operationId: create_batch_v1_batches_post
|
||||
requestBody:
|
||||
|
|
@ -97,20 +103,20 @@ paths:
|
|||
schema:
|
||||
$ref: '#/components/schemas/Batch'
|
||||
'400':
|
||||
description: Bad Request
|
||||
$ref: '#/components/responses/BadRequest400'
|
||||
description: Bad Request
|
||||
'429':
|
||||
description: Too Many Requests
|
||||
$ref: '#/components/responses/TooManyRequests429'
|
||||
description: Too Many Requests
|
||||
'500':
|
||||
description: Internal Server Error
|
||||
$ref: '#/components/responses/InternalServerError500'
|
||||
description: Internal Server Error
|
||||
default:
|
||||
description: Default Response
|
||||
$ref: '#/components/responses/DefaultError'
|
||||
description: Default Response
|
||||
tags:
|
||||
- Batches
|
||||
summary: Retrieve Batch
|
||||
summary: Retrieve information about a specific batch.
|
||||
description: Retrieve information about a specific batch.
|
||||
operationId: retrieve_batch_v1_batches__batch_id__get
|
||||
parameters:
|
||||
|
|
@ -119,7 +125,9 @@ paths:
|
|||
required: true
|
||||
schema:
|
||||
type: string
|
||||
description: 'Path parameter: batch_id'
|
||||
description: The ID of the batch to retrieve.
|
||||
title: Batch Id
|
||||
description: The ID of the batch to retrieve.
|
||||
/v1/batches/{batch_id}/cancel:
|
||||
post:
|
||||
responses:
|
||||
|
|
@ -130,20 +138,20 @@ paths:
|
|||
schema:
|
||||
$ref: '#/components/schemas/Batch'
|
||||
'400':
|
||||
description: Bad Request
|
||||
$ref: '#/components/responses/BadRequest400'
|
||||
description: Bad Request
|
||||
'429':
|
||||
description: Too Many Requests
|
||||
$ref: '#/components/responses/TooManyRequests429'
|
||||
description: Too Many Requests
|
||||
'500':
|
||||
description: Internal Server Error
|
||||
$ref: '#/components/responses/InternalServerError500'
|
||||
description: Internal Server Error
|
||||
default:
|
||||
description: Default Response
|
||||
$ref: '#/components/responses/DefaultError'
|
||||
description: Default Response
|
||||
tags:
|
||||
- Batches
|
||||
summary: Cancel Batch
|
||||
summary: Cancel a batch that is in progress.
|
||||
description: Cancel a batch that is in progress.
|
||||
operationId: cancel_batch_v1_batches__batch_id__cancel_post
|
||||
parameters:
|
||||
|
|
@ -152,7 +160,9 @@ paths:
|
|||
required: true
|
||||
schema:
|
||||
type: string
|
||||
description: 'Path parameter: batch_id'
|
||||
description: The ID of the batch to cancel.
|
||||
title: Batch Id
|
||||
description: The ID of the batch to cancel.
|
||||
/v1/chat/completions:
|
||||
get:
|
||||
responses:
|
||||
|
|
@ -3956,29 +3966,35 @@ components:
|
|||
input_file_id:
|
||||
type: string
|
||||
title: Input File Id
|
||||
description: The ID of an uploaded file containing requests for the batch.
|
||||
endpoint:
|
||||
type: string
|
||||
title: Endpoint
|
||||
description: The endpoint to be used for all requests in the batch.
|
||||
completion_window:
|
||||
type: string
|
||||
const: 24h
|
||||
title: Completion Window
|
||||
description: The time window within which the batch should be processed.
|
||||
metadata:
|
||||
anyOf:
|
||||
- additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
- type: 'null'
|
||||
description: Optional metadata for the batch.
|
||||
idempotency_key:
|
||||
anyOf:
|
||||
- type: string
|
||||
- type: 'null'
|
||||
description: Optional idempotency key. When provided, enables idempotent behavior.
|
||||
type: object
|
||||
required:
|
||||
- input_file_id
|
||||
- endpoint
|
||||
- completion_window
|
||||
title: CreateBatchRequest
|
||||
description: Request model for creating a batch.
|
||||
Batch:
|
||||
properties:
|
||||
id:
|
||||
|
|
@ -12563,6 +12579,44 @@ components:
|
|||
- query
|
||||
title: VectorStoreSearchRequest
|
||||
type: object
|
||||
ListBatchesRequest:
|
||||
description: Request model for listing batches.
|
||||
properties:
|
||||
after:
|
||||
anyOf:
|
||||
- type: string
|
||||
- type: 'null'
|
||||
description: Optional cursor for pagination. Returns batches after this ID.
|
||||
nullable: true
|
||||
limit:
|
||||
default: 20
|
||||
description: Maximum number of batches to return. Defaults to 20.
|
||||
title: Limit
|
||||
type: integer
|
||||
title: ListBatchesRequest
|
||||
type: object
|
||||
RetrieveBatchRequest:
|
||||
description: Request model for retrieving a batch.
|
||||
properties:
|
||||
batch_id:
|
||||
description: The ID of the batch to retrieve.
|
||||
title: Batch Id
|
||||
type: string
|
||||
required:
|
||||
- batch_id
|
||||
title: RetrieveBatchRequest
|
||||
type: object
|
||||
CancelBatchRequest:
|
||||
description: Request model for canceling a batch.
|
||||
properties:
|
||||
batch_id:
|
||||
description: The ID of the batch to cancel.
|
||||
title: Batch Id
|
||||
type: string
|
||||
required:
|
||||
- batch_id
|
||||
title: CancelBatchRequest
|
||||
type: object
|
||||
DialogType:
|
||||
description: Parameter type for dialog data with semantic output labels.
|
||||
properties:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue