Package Layout
The kavai package
ai/pyproject.toml is both the PEP 621 project (kavion-ai-server) and the pixi workspace. Source lives under ai/src/kavai/ (package-dir = {"" = "src"}), installed editable via pixi/uv. A single console script is exposed:
[project.scripts]
kavai = "kavai.main:main"Two sibling packages in ai/packages/ are pulled in as editable dependencies of the workspace — see DataADK packages.
Subpackages
Path (src/kavai/) |
Purpose |
|---|---|
app.py |
The ADK Runtime FastAPI app (kavai.app:app), hosting the systems on :50052. |
main.py |
CLI / single-process entry point (kavai.main:main) — serve, chat, auth, api, ifc, dexpi, gcloud. |
gateway/ |
The front-door FastAPI Gateway (kavai.gateway.app:app, :8080) that routes to the runtime and Kawa adapter. |
systems/ |
The AI-engine implementations — argus, orion, kawa, dataadk_package — plus base.py and the SystemRegistry. |
router.py |
SystemType enum + SystemRouter.resolve_system (routes and falls back retired system names). |
integrity/ |
The API 571/584 integrity pipeline — stages, schemas, and reference data. |
api/ |
The HTTP API layer: v1/ routers (equipment, health, integrity, iow) and the integrity contract endpoints. |
core/ |
Cross-cutting concerns: auth.py, config.py, errors.py, metrics.py, feature_flags.py, response_pipeline.py, tool_execution_hooks.py. |
models/ |
AG-UI event/handler models, request/response models, parser, enricher, response builder. |
server/ |
AG-UI server plumbing: app_factory.py, jwt_utils.py, streaming/, telemetry/, api/. |
cli/ |
The Typer/Click CLI command modules (auth, chat, azure, gcloud, ifc, dexpi, findings, server, …) plus api/, which renders the surface below as commands. |
web_api/ |
The web backend’s operations as data, read from swagger.yaml and indexed by operationId. Consumed by cli/api/ today and by engine tool generation next; not to be confused with api/ above, which is this package’s own HTTP layer. |
tools/, skills/, datasources/, ingestion/, processing/, routing/, foundation_services/, microvm/, azure/, config/, data/ |
Supporting subsystems (Supabase tools, skills, ingestion/processing, the GKE MicroVM sandbox, Azure integration, config, and reference data). |
The servers/kawa/ Kawa adapter is a sibling to src/kavai/ (not under it), because it runs in its own isolated pixi environment — see Runtime & systems.
kavai api — the web backend from the terminal
src/kavai/cli/api/ turns every operation in web/public/api-docs/swagger.yaml into a command. Nothing in it is hand-maintained per route: kavai.web_api reads the spec and indexes it by operationId, client.py sends the request, and __init__.py builds one Click command per operation, grouped by the operation’s first tag and resolved lazily so kavai --help does not parse the YAML.
The reading of the spec sits in kavai/web_api/ rather than inside the CLI because the CLI is not its only consumer. Engine tool generation needs the same index — the same operationId, the same typed parameters, the same x-agent-tool.safe flag and the same answer to whether a bearer can reach the operation. Keeping one reader means a generated tool and a generated command cannot disagree about what an operation takes.
kavai auth login # writes JWT_TOKEN to ai/.env
kavai api list --tag gas-readings # what exists
kavai api show listDatasetGasReadings # parameters, auth, safety
kavai api gas-readings list-gas-readings --limit 500 # typed form
kavai api call listGasReadings limit=500 | jq # generic form, for scriptsThe response body goes to stdout and everything else to stderr, so piping to jq works. Non-GET operations prompt before they run unless given --yes. --base-url (or KAVAI_WEB_URL) points the CLI at dev or alpha instead of NEXT_PUBLIC_APP_URL.
Authentication is the bearer JWT that kavai auth login writes, which web/lib/api-auth.ts accepts alongside the browser’s session cookie. Not every route does: the spec marks some operations CookieAuth only, and those warn before running rather than returning a plausible empty result. kavai api coverage reports the split.
That split is a claim, though, not a measurement. security is hand-maintained beside the handlers, so it can name a scheme the handler never reads — in both directions. --probe calls the reads and reports where the spec and the backend disagree:
kavai api coverage --probe --param slug=my-campaign --param id=<image-uuid>Each result is one of agrees, spec-overstates (declares SupabaseAuth, answers 401), spec-understates (declared CookieAuth-only, answers a bearer), or inconclusive (a 400/404/500 says nothing about authorization). The probe calls reads only, skips anything whose path or required query parameters --param did not supply — a 404 on an invented slug is not evidence — and never calls the three GETs that create an organization as a side effect of being read (ensureUserOrganization, debugFixOrganization, debugCreateTestOrganization). Those are excluded by operationId in MUTATING_READS, and a test asserts the names still resolve, since a rename would silently re-arm them.
Because the commands are generated, the CLI cannot cover fewer routes than the spec documents; what it can lose is an operation whose spec entry breaks generation — a missing or duplicated operationId, an undeclared path parameter, a tag that collides with a meta command, a query parameter that shadows a common option. ai/tests/cli/test_api_swagger_parity.py fails on each of those, and is the reason no per-route registry is kept here.
kavai findings — the worklist the API has no route for
src/kavai/cli/findings.py is the one hand-written group that overlaps the generated surface, and the overlap is deliberate. kavai api can already call each route behind a damage-mechanism finding; what it cannot do is the three things that make those routes usable from a terminal.
It resolves names. Every command takes an asset by tag — AB106 as readily as T100-AB106 — and a grounding by name, then looks the uuids up in the register and the two catalogs before it writes. The tag ranking is the same one web/lib/mcp/manifest.ts uses (tagMatchScore), so a shorthand means the same asset to the CLI and to the MCP tool surface. A name matching more than one row aborts with its candidates rather than taking the first: a finding filed against the wrong vessel is an organisation-visible claim that nothing downstream shows as misplaced.
It lists findings across assets. No backend route does. GET /equipment-damage-mechanisms answers for one photo and GET /equipment/{id} for one asset, which is the gap docs/proposals/20260821_findings_parity_with_anomalies.md records as P3. So findings list reads the register once, reads only the assets whose findings_count is non-zero, and fans out over those concurrently. The per-asset read is expensive (datasheet, nozzles, confirmed and nearby image sets), so the fan-out is capped at --max-assets (default 25, 0 lifts it) and names the assets it did not reach. A worklist that truncates in silence reads as a clean plant.
It reads a finding back whole. There is no GET by finding id at all, so findings show locates one through the asset that holds it — accepting the uuid or an unambiguous prefix of at least 8 characters, and --asset to skip the search.
kavai findings assets # register, findings-first
kavai findings list --undecided # the worklist
kavai findings show <finding-id> # grounding, evidence, decisions
kavai findings mechanisms --grep insulation # API-571 catalog
kavai findings raise AB106 -m "Corrosion Under Insulation" -n "Stained lagging"
kavai findings decide <finding-id> confirm -r "Wall loss measured"
kavai findings amend <finding-id> -c "Coating Loss" # re-ground, logged
kavai findings evidence <finding-id> --add <image-id>Nothing here re-implements a route: each call names an operationId and lets kavai.web_api supply the method, path and auth, so a path that moves in swagger.yaml moves here with it, and a test asserts every id the module names still resolves. Writes confirm before they run unless given --yes, matching kavai api. decide offers confirm and dismiss only: reclassify survives in the decision log’s enum but was retired as a verb by D2 of the 20260821 proposal, which made re-grounding an ordinary amendment.
The group is a workflow layer, not a second copy of the surface. If swagger.yaml ever grows a Findings tag, kavai api findings and kavai findings would be two different things wearing one word; a test in ai/tests/cli/test_findings.py fails if that tag appears, so the collision is resolved deliberately rather than discovered by a user.
Packaged data
Non-code assets shipped with the package:
kavai.integrity.data/*.yaml— API 571/584 reference data (damage mechanisms, process-unit mappings, IOW parameters).kavai.data.api571/*.json— API 571 catalogue.kavai.cli/scripts/*.sh— shell helpers invoked by the CLI.