|
| 1 | +"""FastAPI application for the extractable KnowCode agent gateway.""" |
| 2 | + |
| 3 | +from __future__ import annotations |
| 4 | + |
| 5 | +from typing import Any, Dict, List, Optional |
| 6 | + |
| 7 | +from fastapi import FastAPI, HTTPException, Query |
| 8 | + |
| 9 | +from agent_gateway.litellm_client import LiteLLMError |
| 10 | +from agent_gateway.models import ChatRequest, ChatResponse, ErrorResponse |
| 11 | +from agent_gateway.openapi_tools import OpenAPIFetchError, OpenAPITranslationError |
| 12 | +from agent_gateway.orchestrator import AgentOrchestrator |
| 13 | +from agent_gateway.settings import GatewaySettings |
| 14 | + |
| 15 | + |
| 16 | + |
| 17 | +def create_app() -> FastAPI: |
| 18 | + """Create and wire the gateway API application.""" |
| 19 | + |
| 20 | + settings = GatewaySettings.from_env() |
| 21 | + orchestrator = AgentOrchestrator(settings=settings) |
| 22 | + |
| 23 | + app = FastAPI( |
| 24 | + title="KnowCode Agent Gateway", |
| 25 | + description="LiteLLM-backed tool execution gateway for KnowCode", |
| 26 | + version="0.1.0", |
| 27 | + ) |
| 28 | + |
| 29 | + @app.get("/health") |
| 30 | + def health() -> Dict[str, str]: |
| 31 | + return {"status": "ok"} |
| 32 | + |
| 33 | + @app.get("/api/v1/config") |
| 34 | + def config_snapshot() -> Dict[str, Any]: |
| 35 | + return { |
| 36 | + "knowcode_api_base_url": settings.knowcode_api_base_url, |
| 37 | + "litellm_base_url": settings.litellm_base_url, |
| 38 | + "default_model": settings.default_model, |
| 39 | + "max_tool_rounds": settings.max_tool_rounds, |
| 40 | + "allowed_tool_names": list(settings.allowed_tool_names), |
| 41 | + "default_tags": list(settings.default_tags), |
| 42 | + } |
| 43 | + |
| 44 | + @app.get("/api/v1/tools") |
| 45 | + def list_tools( |
| 46 | + names: Optional[List[str]] = Query( |
| 47 | + default=None, |
| 48 | + description="Optional repeated query arg to filter tool names", |
| 49 | + ) |
| 50 | + ) -> Dict[str, Any]: |
| 51 | + try: |
| 52 | + tools = orchestrator.list_tools(requested_tool_names=names) |
| 53 | + except (OpenAPIFetchError, OpenAPITranslationError, ValueError) as exc: |
| 54 | + raise HTTPException(status_code=502, detail=str(exc)) from exc |
| 55 | + return {"tools": tools, "count": len(tools)} |
| 56 | + |
| 57 | + @app.post( |
| 58 | + "/api/v1/chat", |
| 59 | + response_model=ChatResponse, |
| 60 | + responses={ |
| 61 | + 400: {"model": ErrorResponse}, |
| 62 | + 502: {"model": ErrorResponse}, |
| 63 | + 500: {"model": ErrorResponse}, |
| 64 | + }, |
| 65 | + ) |
| 66 | + def chat(request: ChatRequest) -> ChatResponse: |
| 67 | + try: |
| 68 | + return orchestrator.run(request) |
| 69 | + except ValueError as exc: |
| 70 | + raise HTTPException(status_code=400, detail=str(exc)) from exc |
| 71 | + except (OpenAPIFetchError, OpenAPITranslationError, LiteLLMError, RuntimeError) as exc: |
| 72 | + raise HTTPException(status_code=502, detail=str(exc)) from exc |
| 73 | + except Exception as exc: # pragma: no cover |
| 74 | + raise HTTPException(status_code=500, detail=str(exc)) from exc |
| 75 | + |
| 76 | + return app |
0 commit comments