Source code for taimoe.platform.routes

"""Optional FastAPI routes for runtime discovery.

Exposes three endpoints a managed runtime advertises so the Taimoe Platform
can probe it:

- ``GET /.well-known/taimoe-runtime.json`` — RFC 8615 well-known descriptor.
  Carries the runtime manifest and optionally answers a challenge so the
  Platform can verify the runtime is running an actual Taimoe SDK.
- ``GET /agents`` — list of agents currently registered at this runtime.
- ``GET /health`` — runtime health status (uptime, version, etc).

Per RFC 8615 the well-known endpoint must live at the URL root. To make
that hard to get wrong we return two routers — one anchored at root, one
that can be mounted at any prefix — and the helper wires both into a
FastAPI app for you.
"""

from __future__ import annotations

import hashlib
from typing import TYPE_CHECKING, Any

from ._well_known import CHALLENGE_PROTOCOL_VERSION, WELL_KNOWN_PATH

if TYPE_CHECKING:
    from fastapi import APIRouter, FastAPI

    from .registry import TaimoeRegistry


[docs] def compute_challenge_response(challenge: str) -> str: """Hash ``challenge`` with the protocol version for the well-known reply. Anti-spoofing only — see ``_well_known.CHALLENGE_PROTOCOL_VERSION`` for why this isn't authentication and how to evolve it. """ return hashlib.sha256( f"{challenge}{CHALLENGE_PROTOCOL_VERSION}".encode() ).hexdigest()
[docs] def create_well_known_router(registry: "TaimoeRegistry") -> "APIRouter": """Router carrying only ``GET /.well-known/taimoe-runtime.json``. Must be mounted at the FastAPI app root with no prefix — RFC 8615 requires the well-known URI to be served from origin root. """ APIRouter, _ = _require_fastapi() router = APIRouter() @router.get(WELL_KNOWN_PATH, response_model=None) def well_known(challenge: str | None = None) -> dict[str, Any]: # `dict[str, Any]` instead of returning the Pydantic model directly # because we conditionally add `challenge_response`. The manifest # itself stays schema-checked via model_dump. data = registry.runtime_manifest().model_dump(mode="json") if challenge: data["challenge_response"] = compute_challenge_response(challenge) return data return router
[docs] def create_discovery_router(registry: "TaimoeRegistry") -> "APIRouter": """Router carrying ``/agents`` and ``/health``. Can be mounted at any prefix the host application prefers (e.g. ``prefix="/v1"``). """ APIRouter, _ = _require_fastapi() # Lazy import the response models so callers without ``fastapi`` can # still import this module (we already lazy-import APIRouter). from .types.manifest import AgentsManifest, HealthStatus router = APIRouter() @router.get("/agents", response_model=AgentsManifest) def agents() -> AgentsManifest: return registry.agents_manifest() @router.get("/health", response_model=HealthStatus) def health() -> HealthStatus: return registry.health() return router
[docs] def install_routes(registry: "TaimoeRegistry", app: "FastAPI") -> None: """Wire both routers into a FastAPI app with the correct mounts. The well-known router goes at the root; the discovery router is mounted bare (``/agents`` / ``/health``). Callers that need a different prefix for discovery can use :func:`create_discovery_router` directly. """ app.include_router(create_well_known_router(registry)) app.include_router(create_discovery_router(registry))
[docs] def create_fastapi_router(registry: "TaimoeRegistry") -> "APIRouter": """Backwards-compatible single-router entry point. Returns one combined router that includes both the well-known and discovery routes. Convenient for simple deployments, but means the caller must mount it at the app root or the well-known path will break RFC 8615. Prefer :func:`install_routes` for new code. """ APIRouter, _ = _require_fastapi() combined = APIRouter() combined.include_router(create_well_known_router(registry)) combined.include_router(create_discovery_router(registry)) return combined
def _require_fastapi() -> tuple[type, type]: """Import FastAPI lazily so non-proxy users don't pay the dep cost.""" try: from fastapi import APIRouter, FastAPI except ImportError as exc: # pragma: no cover - import guard raise RuntimeError( "Install taimoe-platform[proxy] to use FastAPI discovery routes" ) from exc return APIRouter, FastAPI