API¶
Core app & server¶
Shared FastAPI app factory for Klea packages.
File: klea_utils/api/app.py
Copyright 2026 Ankur Sinha Author: Ankur Sinha <sanjay DOT ankur AT gmail DOT com>
- klea_utils.api.app.make_app(graph_factory: Callable[[], BaseLangGraph], title: str = 'Klea API', version: str = '0.1.0', routers: list[APIRouter] | None = None) FastAPI[source]¶
Create a FastAPI instance with a standard lifespan.
The lifespan:
Instantiates and sets up the graph via graph_factory
Opens a persistent
SessionStoreat{graph.paths.user_data_dir}/sessions.dbalongside the graph’s checkpoints.Stores the graph and session store on
app.state
- Parameters:
graph_factory – Callable that returns a configured
BaseLangGraphinstancetitle – API title (appears in OpenAPI docs)
version – API version (appears in OpenAPI docs)
routers – List of APIRouters to include on the app
- Returns:
Configured FastAPI app
Shared server launcher factory for Klea packages.
File: klea_utils/api/server.py
Copyright 2026 Ankur Sinha Author: Ankur Sinha <sanjay DOT ankur AT gmail DOT com>
- klea_utils.api.server.is_loopback_host(host: str) bool[source]¶
Return
Truewhen host refers to the local machine.- Parameters:
host – Hostname from a server URL (e.g.
"127.0.0.1")- Returns:
Truefor loopback addresses,Falseotherwise
- klea_utils.api.server.make_serve_app(app_module: str, default_port: int = 8005) Typer[source]¶
Create a Typer app that runs uvicorn on the given app_module.
The module string should be the importable path to a FastAPI
appinstance, e.g."klea_rag.api.main:app".- Parameters:
app_module – Uvicorn module string
default_port – Default port number
- Returns:
A
typer.Typerapp for use as a CLI entry point
- klea_utils.api.server.spawn_server(app_module: str, host: str = '127.0.0.1', port: int = 8005, timeout: float = 180.0) Iterator[Popen | None][source]¶
Context manager that runs an API server in a subprocess.
If a healthy server is already listening at
host:port(probed via/health/ready), nothing is spawned andNoneis yielded, so the caller does not own the server’s lifecycle. Otherwise a uvicorn subprocess is spawned (stdout and stderr inherited so startup errors and app output are visible; access logs are disabled to keep the shared terminal clean, with full logging preserved in the server’s rotating log file), readiness is waited on, and the subprocess is terminated when thewithblock exits.- Parameters:
app_module – Uvicorn module string (e.g.
"klea_rag.api.main:app")host – Host to bind
port – Port to bind
timeout – Total seconds to wait for readiness after spawning
- Returns:
The spawned
subprocess.Popen(orNoneif an existing server was reused)
- klea_utils.api.server.split_server_url(url: str, default_port: int = 8005) tuple[str, int][source]¶
Return
(host, port)parsed from url.Falls back to
127.0.0.1and default_port when the URL does not carry a hostname or port.- Parameters:
url – Server URL (e.g.
http://127.0.0.1:8005)default_port – Port to use when the URL omits one
- Returns:
(host, port)suitable for binding a local server
Chat endpoints¶
Shared chat endpoint factory for Klea packages.
File: klea_utils/api/chat.py
Copyright 2026 Ankur Sinha Author: Ankur Sinha <sanjay DOT ankur AT gmail DOT com>
- class klea_utils.api.chat.ChatPayload(*, query: str, chat_id: str, user_id: str = '')[source]¶
Bases:
BaseModel- model_config = {}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- klea_utils.api.chat.create_chat_router() APIRouter[source]¶
Create an APIRouter with
/queryand/query/streamendpoints.The router reads the graph instance from
request.app.state.graph(set byklea_utils.api.app.make_app()). Chat-session data is stored via theSessionStoreonapp.state.chat_sessions.
Shared health check endpoint factory for Klea packages.
File: klea_utils/api/health.py
Copyright 2026 Ankur Sinha Author: Ankur Sinha <sanjay DOT ankur AT gmail DOT com>
- klea_utils.api.health.create_health_router() APIRouter[source]¶
Create an APIRouter with
/health/liveand/health/readyendpoints.
Message history endpoints for chat sessions.
File: klea_utils/api/messages.py
Copyright 2026 Ankur Sinha Author: Ankur Sinha <sanjay DOT ankur AT gmail DOT com>
Session management¶
Chat session CRUD endpoints.
NOTE: user_id is currently a browser-generated UUID taken from
the URL path. For multi-user deployments this must be replaced with
an authenticated identity (JWT / OAuth) extracted from the request
context — otherwise any user can delete or rename another user’s chats
by modifying the user_id in the URL.
File: klea_utils/api/sessions.py
Copyright 2026 Ankur Sinha Author: Ankur Sinha <sanjay DOT ankur AT gmail DOT com>
- class klea_utils.api.sessions.CreateChatPayload(*, chat_id: str, title: str = '')[source]¶
Bases:
BaseModel- model_config = {}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class klea_utils.api.sessions.UpdateChatPayload(*, title: str)[source]¶
Bases:
BaseModel- model_config = {}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- klea_utils.api.sessions.create_sessions_router() APIRouter[source]¶
Create an APIRouter for chat session CRUD.
Endpoints:
GET /chat/{user_id}List all chats for the user.
POST /chat/{user_id}Create a new chat. Title is auto-generated from coolname if not provided.
PATCH /chat/{user_id}/{chat_id}Update a chat’s metadata (title).
DELETE /chat/{user_id}/{chat_id}Remove a chat and all associated data.
DELETE /chat/{user_id}Remove all chats, messages, and checkpoints for the user.
Persistent SQLite-backed store for chat session data.
- Manages two tables alongside the LangGraph checkpoint DB:
chat_sessions (chat metadata, listing, and model overrides)
messages (curated Q&A history for chat display)
There is no separate state table. Graph state (plan, goal,
tool_status, …) is read directly from the latest LangGraph checkpoint
via graph.aget_state(thread_id) – the checkpoint DB is the
canonical source and already stores the full deserialised state with no
serialization round-trip.
File: klea_utils/api/sessions_db.py
Copyright 2026 Ankur Sinha Author: Ankur Sinha <sanjay DOT ankur AT gmail DOT com>
- class klea_utils.api.sessions_db.SessionStore(db_path: str | Path)[source]¶
Bases:
objectSQLite-backed persistent store for chat session data.
All public methods are thread-safe. The store auto-creates its schema on first connection.
- Parameters:
db_path – Filesystem path to the SQLite database file.
- add_message(user_id: str, chat_id: str, role: str, content: str, metadata: dict[str, Any] | None = None) None[source]¶
Append a single message to a chat’s history.
- add_messages(user_id: str, chat_id: str, messages: Sequence[dict[str, Any]]) None[source]¶
Append multiple messages atomically.
Each dict must have
roleandcontentkeys, and may have an optionalmetadatakey.
- clear_override(user_id: str, chat_id: str, role: str) None[source]¶
Remove the model override for a single role in a chat.
- create_chat(user_id: str, chat_id: str, title: str = '') None[source]¶
Insert a chat row if it does not already exist.
- get_messages(user_id: str, chat_id: str) list[dict[str, Any]][source]¶
Return all messages for a chat, oldest first.
- get_overrides(user_id: str, chat_id: str) dict[str, dict[str, Any]][source]¶
Return per-role model overrides keyed by role.
Returns
{"rag": {"model": "...", "provider": "..."}, ...}
- rename_chat(user_id: str, chat_id: str, title: str) None[source]¶
Update the display title of a chat.
Model configuration¶
Per-session model configuration endpoints for runtime model switching.
File: klea_utils/api/models.py
Copyright 2026 Ankur Sinha Author: Ankur Sinha <sanjay DOT ankur AT gmail DOT com>
- class klea_utils.api.models.ChatModelConfigPayload(*, model: str, api_key: str | None = None, base_url: str | None = None, provider: str | None = None, user_id: str = '')[source]¶
Bases:
BaseModel- model_config = {}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- klea_utils.api.models.create_models_router() APIRouter[source]¶
Create an APIRouter for per-chat model configuration.
GET /chat/{user_id}/{chat_id}/models/overridesReturns stored model overrides for a chat.
GET /chat/{user_id}/{chat_id}/models/activeReturns resolved model config (defaults merged with overrides).
POST /chat/{user_id}/{chat_id}/models/overrides/{role}Stores per-chat model overrides.
SSE streaming¶
Shared SSE streaming client for Klea frontends.
Provides both an async generator (for NiceGUI and TUI) and a synchronous
generator (for Streamlit) that consume the /query/stream SSE endpoint
and yield parsed event dicts.
File: klea_utils/api/sse.py
Copyright 2026 Ankur Sinha Author: Ankur Sinha <sanjay DOT ankur AT gmail DOT com>
- async klea_utils.api.sse.fetch_active_models(server_url: str, user_id: str, chat_id: str) dict[str, dict[str, str]][source]¶
Fetch the resolved model config per role for a chat.
Calls
GET /chat/{user_id}/{chat_id}/models/activeand returns the merged default + override config dict.- Parameters:
server_url – Base URL of the backend API server.
user_id – Opaque persistent user identifier.
chat_id – Chat conversation identifier.
- Returns:
{"chat": {"model": "...", "provider": "..."}, "guard": ..., "embedding": ...}
- klea_utils.api.sse.fetch_active_models_sync(server_url: str, user_id: str, chat_id: str) dict[str, dict[str, str]][source]¶
Synchronous counterpart of
fetch_active_models().Intended for frontends that cannot use asyncio.
- Parameters:
server_url – Base URL of the backend API server.
user_id – Opaque persistent user identifier.
chat_id – Chat conversation identifier.
- klea_utils.api.sse.format_model_info(info: dict[str, dict[str, str]]) str[source]¶
Build a compact one-line model summary from active models config.
Strips provider prefixes and joins roles, e.g.:
Chat:deepseek-v4-flash | Guard:llama-guard3 | Embedding:bge-m3
- Parameters:
info – The dict returned by
fetch_active_models/fetch_active_models_sync.- Returns:
Empty string if no models are configured.
- async klea_utils.api.sse.stream_events(query: str, chat_id: str, server_url: str, user_id: str = '') AsyncGenerator[dict, None][source]¶
POST to
/query/streamand yield parsed SSE event dicts.Each yielded dict has at least a
"type"key. Known types:progress {"type": "progress", "node": "<label>"} info {"type": "info", "node": "<label>", "data": {...}} debug {"type": "debug", "node": "<label>", "data": {...}} token {"type": "token", "content": "<chunk>", "node": "<label>"} usage {"type": "usage", "node": "<label>", "data": {...}} complete {"type": "complete", "message_for_user": "<text>"} error {"type": "error", "message": "<text>", "error_type": "<class>", "node": "<label>"}
This async generator is intended for NiceGUI and TUI frontends.
- Parameters:
query – User’s query string.
chat_id – Chat conversation identifier.
server_url – Base URL of the backend API server.
user_id – Opaque persistent user identifier.
- klea_utils.api.sse.stream_events_sync(query: str, chat_id: str, server_url: str, user_id: str = '') Generator[dict, None, None][source]¶
Synchronous counterpart of
stream_events().Intended for frontends that cannot use asyncio. Async frontends (NiceGUI, TUI) should use
stream_events()instead.- Parameters:
query – User’s query string.
chat_id – Chat conversation identifier.
server_url – Base URL of the backend API server.
user_id – Opaque persistent user identifier.
Utilities¶
Utility functions for the Klea API layer.
File: klea_utils/api/utils.py
Copyright 2026 Ankur Sinha Author: Ankur Sinha <sanjay DOT ankur AT gmail DOT com>
- async klea_utils.api.utils.check_api_is_ready(url: str, attempts: int | None = None, timeout: float = 180.0)[source]¶
Exponentially back off checking that the API is ready.
- Parameters:
url – Health check endpoint URL
attempts – If set, maximum number of probe attempts (overrides timeout)
timeout – Total wall-clock seconds to keep probing when attempts is unset