feat: migrate Inspect API to FastAPI router (#4403)

# What does this PR do?

Migrate the Inspect API to the FastAPI router pattern.

Changes:
- Add inspect API to FastAPI router registry
- Add PUBLIC_ROUTE_KEY support for routes that don't require auth
- Update WebMethod creation to respect route's openapi_extra for
authentication requirements

Fixes: https://github.com/llamastack/llama-stack/issues/4346

<!-- Provide a short summary of what this PR does and why. Link to
relevant issues if applicable. -->

<!-- If resolving an issue, uncomment and update the line below -->
<!-- Closes #[issue-number] -->

## Test Plan

CI and various curls on /v1/inspect/routes, /v1/health, /v1/version

Signed-off-by: Sébastien Han <seb@redhat.com>
This commit is contained in:
Sébastien Han 2025-12-17 17:33:42 +01:00 committed by GitHub
parent cd5095a247
commit a7d509aaf9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 281 additions and 182 deletions

View file

@ -1,103 +0,0 @@
# Copyright (c) Meta Platforms, Inc. and affiliates.
# All rights reserved.
#
# This source code is licensed under the terms described in the LICENSE file in
# the root directory of this source tree.
from typing import Literal, Protocol, runtime_checkable
from pydantic import BaseModel
from llama_stack_api.datatypes import HealthStatus
from llama_stack_api.schema_utils import json_schema_type, webmethod
from llama_stack_api.version import (
LLAMA_STACK_API_V1,
)
# Valid values for the route filter parameter.
# Actual API levels: v1, v1alpha, v1beta (filters by level, excludes deprecated)
# Special filter value: "deprecated" (shows deprecated routes regardless of level)
ApiFilter = Literal["v1", "v1alpha", "v1beta", "deprecated"]
@json_schema_type
class RouteInfo(BaseModel):
"""Information about an API route including its path, method, and implementing providers.
:param route: The API endpoint path
:param method: HTTP method for the route
:param provider_types: List of provider types that implement this route
"""
route: str
method: str
provider_types: list[str]
@json_schema_type
class HealthInfo(BaseModel):
"""Health status information for the service.
:param status: Current health status of the service
"""
status: HealthStatus
@json_schema_type
class VersionInfo(BaseModel):
"""Version information for the service.
:param version: Version number of the service
"""
version: str
@json_schema_type
class ListRoutesResponse(BaseModel):
"""Response containing a list of all available API routes.
:param data: List of available route information objects
"""
data: list[RouteInfo]
@runtime_checkable
class Inspect(Protocol):
"""Inspect
APIs for inspecting the Llama Stack service, including health status, available API routes with methods and implementing providers.
"""
@webmethod(route="/inspect/routes", method="GET", level=LLAMA_STACK_API_V1)
async def list_routes(self, api_filter: ApiFilter | None = None) -> ListRoutesResponse:
"""List routes.
List all available API routes with their methods and implementing providers.
:param api_filter: Optional filter to control which routes are returned. Can be an API level ('v1', 'v1alpha', 'v1beta') to show non-deprecated routes at that level, or 'deprecated' to show deprecated routes across all levels. If not specified, returns all non-deprecated routes.
:returns: Response containing information about all available routes.
"""
...
@webmethod(route="/health", method="GET", level=LLAMA_STACK_API_V1, require_authentication=False)
async def health(self) -> HealthInfo:
"""Get health status.
Get the current health status of the service.
:returns: Health information indicating if the service is operational.
"""
...
@webmethod(route="/version", method="GET", level=LLAMA_STACK_API_V1, require_authentication=False)
async def version(self) -> VersionInfo:
"""Get version.
Get the version of the service.
:returns: Version information containing the service version number.
"""
...

View file

@ -0,0 +1,37 @@
# Copyright (c) Meta Platforms, Inc. and affiliates.
# All rights reserved.
#
# This source code is licensed under the terms described in the LICENSE file in
# the root directory of this source tree.
"""Inspect API protocol and models.
This module contains the Inspect protocol definition.
Pydantic models are defined in llama_stack_api.inspect.models.
The FastAPI router is defined in llama_stack_api.inspect.fastapi_routes.
"""
# Import fastapi_routes for router factory access
from . import fastapi_routes
# Import protocol for re-export
from .api import Inspect
# Import models for re-export
from .models import (
ApiFilter,
HealthInfo,
ListRoutesResponse,
RouteInfo,
VersionInfo,
)
__all__ = [
"Inspect",
"ApiFilter",
"HealthInfo",
"ListRoutesResponse",
"RouteInfo",
"VersionInfo",
"fastapi_routes",
]

View file

@ -0,0 +1,25 @@
# Copyright (c) Meta Platforms, Inc. and affiliates.
# All rights reserved.
#
# This source code is licensed under the terms described in the LICENSE file in
# the root directory of this source tree.
from typing import Protocol, runtime_checkable
from .models import (
ApiFilter,
HealthInfo,
ListRoutesResponse,
VersionInfo,
)
@runtime_checkable
class Inspect(Protocol):
"""APIs for inspecting the Llama Stack service, including health status, available API routes with methods and implementing providers."""
async def list_routes(self, api_filter: ApiFilter | None = None) -> ListRoutesResponse: ...
async def health(self) -> HealthInfo: ...
async def version(self) -> VersionInfo: ...

View file

@ -0,0 +1,76 @@
# Copyright (c) Meta Platforms, Inc. and affiliates.
# All rights reserved.
#
# This source code is licensed under the terms described in the LICENSE file in
# the root directory of this source tree.
"""FastAPI router for the Inspect API.
This module defines the FastAPI router for the Inspect API using standard
FastAPI route decorators.
"""
from typing import Annotated
from fastapi import APIRouter, Query
from llama_stack_api.router_utils import PUBLIC_ROUTE_KEY, standard_responses
from llama_stack_api.version import LLAMA_STACK_API_V1
from .api import Inspect
from .models import (
ApiFilter,
HealthInfo,
ListRoutesResponse,
VersionInfo,
)
def create_router(impl: Inspect) -> APIRouter:
"""Create a FastAPI router for the Inspect API."""
router = APIRouter(
prefix=f"/{LLAMA_STACK_API_V1}",
tags=["Inspect"],
responses=standard_responses,
)
@router.get(
"/inspect/routes",
response_model=ListRoutesResponse,
summary="List routes.",
description="List all available API routes with their methods and implementing providers.",
responses={200: {"description": "Response containing information about all available routes."}},
)
async def list_routes(
api_filter: Annotated[
ApiFilter | None,
Query(
description="Optional filter to control which routes are returned. Can be an API level ('v1', 'v1alpha', 'v1beta') to show non-deprecated routes at that level, or 'deprecated' to show deprecated routes across all levels. If not specified, returns all non-deprecated routes."
),
] = None,
) -> ListRoutesResponse:
return await impl.list_routes(api_filter)
@router.get(
"/health",
response_model=HealthInfo,
summary="Get health status.",
description="Get the current health status of the service.",
responses={200: {"description": "Health information indicating if the service is operational."}},
openapi_extra={PUBLIC_ROUTE_KEY: True},
)
async def health() -> HealthInfo:
return await impl.health()
@router.get(
"/version",
response_model=VersionInfo,
summary="Get version.",
description="Get the version of the service.",
responses={200: {"description": "Version information containing the service version number."}},
openapi_extra={PUBLIC_ROUTE_KEY: True},
)
async def version() -> VersionInfo:
return await impl.version()
return router

View file

@ -0,0 +1,53 @@
# Copyright (c) Meta Platforms, Inc. and affiliates.
# All rights reserved.
#
# This source code is licensed under the terms described in the LICENSE file in
# the root directory of this source tree.
"""Pydantic models for Inspect API requests and responses.
This module defines the request and response models for the Inspect API
using Pydantic with Field descriptions for OpenAPI schema generation.
"""
from typing import Literal
from pydantic import BaseModel, Field
from llama_stack_api.datatypes import HealthStatus
from llama_stack_api.schema_utils import json_schema_type
# Valid values for the route filter parameter.
# Actual API levels: v1, v1alpha, v1beta (filters by level, excludes deprecated)
# Special filter value: "deprecated" (shows deprecated routes regardless of level)
ApiFilter = Literal["v1", "v1alpha", "v1beta", "deprecated"]
@json_schema_type
class RouteInfo(BaseModel):
"""Information about an API route including its path, method, and implementing providers."""
route: str = Field(description="The API route path")
method: str = Field(description="The HTTP method for the route")
provider_types: list[str] = Field(description="List of provider types implementing this route")
@json_schema_type
class HealthInfo(BaseModel):
"""Health status information for the service."""
status: HealthStatus = Field(description="The health status of the service")
@json_schema_type
class VersionInfo(BaseModel):
"""Version information for the service."""
version: str = Field(description="The version string of the service")
@json_schema_type
class ListRoutesResponse(BaseModel):
"""Response containing a list of all available API routes."""
data: list[RouteInfo] = Field(description="List of available API routes")

View file

@ -18,6 +18,11 @@ from typing import Annotated, Any, TypeVar
from fastapi import Path, Query
from pydantic import BaseModel
# OpenAPI extension key to mark routes that don't require authentication.
# Use this in FastAPI route decorators: @router.get("/health", openapi_extra={PUBLIC_ROUTE_KEY: True})
PUBLIC_ROUTE_KEY = "x-public"
standard_responses: dict[int | str, dict[str, Any]] = {
400: {"$ref": "#/components/responses/BadRequest400"},
429: {"$ref": "#/components/responses/TooManyRequests429"},