Backoffice API

kavalai.backoffice is the FastAPI service behind the management UI. It exposes agents, sessions, runs, metrics, model-call statistics and RAG indexes, and the project service that scopes them.

Copyright 2026 OÜ KAVAL AI (registry code 17393877)

Licensed under the Apache License, Version 2.0 (the “License”); you may not use this file except in compliance with the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

kavalai.backoffice.server.get_backoffice_session()[source]

Context manager to provide a session for the backoffice database. Handles connection errors gracefully.

kavalai.backoffice.server.get_project_session(project: Project)[source]

Context manager to provide a session for a specific project’s agent database. Handles connection errors gracefully.

kavalai.backoffice.server.required_setting(name: str) → str[source]

A setting the backoffice cannot run without.

There is no development fallback on purpose: a cookie signed with a well-known key, or an OAuth client with no id, is a backoffice that looks as if it works right up to the moment it is exposed.

async kavalai.backoffice.server.authenticate_and_sync_user(user_info: dict)[source]
kavalai.backoffice.server.is_logged_in(request: Request)[source]
kavalai.backoffice.server.assert_logged_in(request: Request)[source]
kavalai.backoffice.server.assert_is_admin(request: Request)[source]
async kavalai.backoffice.server.assert_is_owner(session: AsyncSession, request: Request, project_id: UUID)[source]
async kavalai.backoffice.server.assert_is_admin_or_owner(session: AsyncSession, request: Request, project_id: UUID)[source]
async kavalai.backoffice.server.assert_is_member(session: AsyncSession, request: Request, project_id: UUID)[source]
async kavalai.backoffice.server.get_project_and_assert_access(request: Request, project_id: UUID) → Project[source]

Fetch project and assert user is a member.

async kavalai.backoffice.server.login(request: Request)[source]
async kavalai.backoffice.server.logout(request: Request)[source]
async kavalai.backoffice.server.user_details(request: Request)[source]
async kavalai.backoffice.server.google_auth_callback(request: Request)[source]
async kavalai.backoffice.server.set_active_project(project_id: UUID, request: Request)[source]
async kavalai.backoffice.server.projects_create(request: Request, data: dict = Body(PydanticUndefined))[source]
async kavalai.backoffice.server.projects_get_by_id(project_id: UUID, request: Request)[source]
async kavalai.backoffice.server.projects_get_all(request: Request)[source]
async kavalai.backoffice.server.projects_update(project_id: UUID, request: Request, data: dict = Body(PydanticUndefined))[source]
async kavalai.backoffice.server.projects_delete(project_id: UUID, request: Request)[source]
async kavalai.backoffice.server.users_get_all(request: Request)[source]
async kavalai.backoffice.server.users_create(request: Request, data: dict = Body(PydanticUndefined))[source]
async kavalai.backoffice.server.users_update(user_id: UUID, request: Request, data: dict = Body(PydanticUndefined))[source]
async kavalai.backoffice.server.users_delete(user_id: UUID, request: Request)[source]
async kavalai.backoffice.server.agents_get_by_id(project_id: UUID, agent_id: UUID, request: Request)[source]
async kavalai.backoffice.server.workflows_render_svg(request: Request, data: dict = Body(PydanticUndefined))[source]

Render a workflow graph to an SVG diagram.

Accepts {"workflow": {...}} (or a bare workflow dict) and returns an image/svg+xml document. The agents page uses this to show an agent’s workflow as a backend-generated diagram (replacing the client-side build).

async kavalai.backoffice.server.agents_get_all(project_id: UUID, request: Request)[source]

Fetch all agents belonging to a specific project.

async kavalai.backoffice.server.agents_get_stats(project_id: UUID, request: Request, days: int = 7, agent_id: UUID | None = None)[source]

Fetch daily stats for agents in a specific project.

async kavalai.backoffice.server.agents_get_summary_stats(project_id: UUID, request: Request, agent_id: UUID | None = None)[source]

Fetch summary stats (last 30 days) for agents in a specific project.

async kavalai.backoffice.server.agents_get_sessions(project_id: UUID, request: Request, agent_id: UUID | None = None, search: str | None = None, external_id: str | None = None, start_date: datetime | None = None, end_date: datetime | None = None, limit: int = 50, offset: int = 0)[source]

Fetch session summaries for a specific project.

external_id matches the session’s caller-supplied key as a prefix. Evaluation runs record their sessions as eval:{tag}:{case}, so this is the click-through from a failing case to the conversation that produced it.

async kavalai.backoffice.server.agents_get_session_details(project_id: UUID, session_id: UUID, request: Request)[source]

Fetch all details (messages, runs, tasks) for a specific session.

async kavalai.backoffice.server.projects_get_llm_call_stats(project_id: UUID, request: Request, call_type: str | None = None, limit: int = 50, offset: int = 0, agent_id: UUID | None = None, session_id: UUID | None = None, run_id: UUID | None = None)[source]

Fetch paginated model call stats for a specific project.

agent_id, session_id and run_id narrow the list to the calls attributed to that agent, conversation or run — the run’s own calls are what the task debugger shows beside its tasks.

kavalai.backoffice.server.rag_service_for_project(project: Project, model: str | None = None, normalizer=None, session_factory=None) → CollectionRagService[source]

The RAG service over the project’s agent database.

Both backends share one storage model, so the explorer endpoints only differ in how they reach the database: a SQLite project’s index lives in the same file as its agent tables, a PostgreSQL project’s in the project’s rag_schema (its agent schema unless one is set), reached through session_factory (the project’s sessionmaker unless one is given).

The service never provisions: the explorer only reads, and a registry or collection it would create is a write to a database that may be production. A read-only project’s SQLite file is opened with PRAGMA query_only besides.

async kavalai.backoffice.server.projects_rag_query(project_id: UUID, request: Request, query_data: dict = Body(PydanticUndefined))[source]

Execute a RAG query for a specific project.

model is optional: an existing collection is embedded with the model recorded for it, so a query needs only text.

async kavalai.backoffice.server.projects_rag_stats(project_id: UUID, request: Request)[source]

Fetch RAG statistics for a specific project.

async kavalai.backoffice.server.projects_rag_collections(project_id: UUID, request: Request)[source]

List a project’s RAG collections with their model, dimension and size.

The explorer takes the model a query is embedded with from here.

async kavalai.backoffice.server.projects_train_pca(project_id: UUID, collection_name: str, request: Request)[source]

Trigger PCA training for a specific project and collection.

async kavalai.backoffice.server.projects_test_connection(project_id: str, request: Request, data: dict = Body({}))[source]

Test connection to the project database.

async kavalai.backoffice.server.projects_get_members(project_id: UUID, request: Request)[source]
async kavalai.backoffice.server.projects_add_member(project_id: UUID, request: Request, data: dict = Body(PydanticUndefined))[source]
async kavalai.backoffice.server.projects_update_member(project_id: UUID, request: Request, data: dict = Body(PydanticUndefined))[source]
async kavalai.backoffice.server.projects_remove_member(project_id: UUID, user_id: UUID, request: Request)[source]

Copyright 2026 OÜ KAVAL AI (registry code 17393877)

Licensed under the Apache License, Version 2.0 (the “License”); you may not use this file except in compliance with the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

kavalai.backoffice.project_service.PROJECT_FIELDS = frozenset({'db_host', 'db_name', 'db_password', 'db_port', 'db_schema', 'db_type', 'db_user', 'description', 'name', 'rag_schema', 'read_only'})

its name, description and connection settings. Everything else a project row carries — id, the timestamps, the caller’s role the list endpoint adds — is the server’s.

Type:

The fields of a project a client may set

kavalai.backoffice.project_service.project_fields(data: dict[str, Any]) → dict[str, Any][source]

The part of a request body that may become project columns.

The projects page sends the whole project back as it listed it, so a body may carry created_at as an ISO string, which the database refuses for a timestamp column; keys outside PROJECT_FIELDS are dropped rather than written.

class kavalai.backoffice.project_service.ProjectService(session_maker: async_sessionmaker[AsyncSession])[source]

Bases: object

async get_user_projects(user_id: UUID) → list[dict[str, Any]][source]
async create_project(data: dict[str, Any], owner_id: UUID) → Project[source]
async update_project(project_id: UUID, data: dict[str, Any]) → Project | None[source]
async delete_project(project_id: UUID) → bool[source]
async get_members(project_id: UUID) → list[dict[str, Any]][source]
async add_member(project_id: UUID, user_id: UUID, role: ProjectRole) → None[source]
async update_member_role(project_id: UUID, user_id: UUID, new_role: ProjectRole) → None[source]
async remove_member(project_id: UUID, user_id: UUID) → None[source]
async test_connection(project: Project) → dict[str, str][source]
kavalai.backoffice.project_service.project_db_uri(project: Project) → str[source]

The URI of the agent database a project points at.

db_type decides how the connection fields are read: sqlite takes db_name as a file path, anything else builds a PostgreSQL URI from host, port, user, password and database.

kavalai.backoffice.project_service.project_schema(project: Project) → str | None[source]

The schema of the project’s agent database (None for SQLite).

kavalai.backoffice.project_service.project_rag_schema(project: Project) → str | None[source]

The schema of the project’s RAG collections: rag_schema when set, otherwise the agent database’s own schema (None for SQLite).

kavalai.backoffice.project_service.project_read_only(project: Project) → bool[source]

Whether the backoffice may only read the project’s agent database.

On unless the project says False: a project built from a form (Project(**data) on the connection test) carries no column default, and the safe reading of “unsaid” is read-only.

kavalai.backoffice.project_service.project_sessionmaker(project: Project) → async_sessionmaker[AsyncSession][source]

A sessionmaker for the agent database the project points at.

A read-only project gets connections the database itself holds to reads (get_sessionmaker()).

kavalai.backoffice.project_service.describe_project_database(project: Project) → str[source]

A log-safe description of the project’s database (no password).