Conversational BI internals¶
How the Conversational BI extension is built. Its source is in extensions/conversationalbi: its own Compose project (iceberg-conversationalbi) and Helm chart (deploy/helm/iceberg-conversationalbi, where a NetworkPolicy takes the place of the internal chat network), dependencies, images, version and releases. It uses the platform only through the Extension Bridge, Iceberg REST and S3.
| Chat | CopilotKit v2 generative UI, Chart.js, Nothing design |
| Agent | Pydantic AI, defined in YAML (analyst.yaml), served over AG-UI |
| API | OpenAPI contract |
| Engine | DuckDB 1.5.5 with iceberg_scan, in a disposable, locked-down process per query |
| Bridge | contract >=0.1,<0.2; shared models need capability shared-data |
Architecture¶
flowchart LR
subgraph Clients
Person["Person (chat)"]
Agent["AI agent (MCP)"]
end
subgraph Extension["Conversational BI (iceberg-conversationalbi)"]
GW["cbi-gateway :3007<br/>sign-in, REST, MCP, chat UI"]
AG["agent :3017 (internal)<br/>Pydantic AI · analyst.yaml"]
W["query worker<br/>DuckDB, one process per query"]
RT["cbi-runtime<br/>CopilotKit runtime<br/>internal network only"]
end
subgraph Core["Iceberg Data Platform"]
KC["Keycloak"]
BR["Extension Bridge /bridge/v1"]
PO["Polaris<br/>tables + semantic models"]
S3["RustFS"]
end
LLM["LLM provider<br/>(Gemini by default)"]
Person -- "session cookie" --> GW
Agent -- "MCP, Bearer" --> GW
GW -- "chat requests + run ticket" --> RT
RT -- "AG-UI + run ticket" --> AG
AG -- "tools" --> GW
AG -- "prompt, model names, ≤20 sample rows" --> LLM
GW -- "/me, principals, read tokens" --> BR
GW -- "models, schemas (read token)" --> PO
W -- "loadTable, vended credentials" --> PO
W -- "Parquet, table-scoped" --> S3
Person -. "PKCE sign-in" .-> KC
One question, step by step:
sequenceDiagram
autonumber
participant B as Browser
participant G as Gateway
participant R as CopilotKit runtime
participant A as Agent (analyst.yaml)
participant L as LLM
participant P as Bridge / Polaris
participant W as Query worker
B->>G: POST /api/copilotkit/agent/analyst/run (cookie)
G->>G: session, thread owner, mint run ticket
G->>R: forward with X-CBI-Run-Ticket
R->>A: AG-UI run (runtime key + ticket)
A->>P: GET /me (fresh): teams, own and shared databases
A->>L: question + selected model's context
L-->>A: call query(metrics, dimensions, filters)
A->>A: authorize, compile SQL from the Ossie model
A->>W: SQL + one read token (team principal)
W->>P: loadTable (vended credentials), iceberg_scan
W-->>A: rows → result res-…
A->>L: ≤20 sample rows, definitions, SQL
L-->>A: call show_chart(resultId, recommended arguments)
A-->>B: AG-UI events: query card, show_chart
B->>G: GET /api/v1/results/res-… (re-checks access)
B->>B: Chart.js draws the full result
Install from source¶
From the platform directory, register the extension. This creates its Keycloak clients and writes the handshake file:
uv run python -m scripts.setup --extension conversationalbi --origin http://localhost:3007 \
--handshake extensions/conversationalbi/.env.bridge
docker compose up -d --wait
docker compose -f compose.users.yaml up -d --wait users
Then in extensions/conversationalbi:
uv sync
uv run python -m scripts.setup # asks for the model and its key; stored in llm.env (0600)
docker compose build && docker compose up -d --wait
Unattended, pass them instead: LLM_MODEL=anthropic:claude-sonnet-5-5 ANTHROPIC_API_KEY=… uv run
python -m scripts.setup.
For a demo on a platform set up with scripts.setup --demo, uv run --all-groups python -m
scripts.flights_seed --own demo-demo (platform directory) publishes the flights product in a partner team, shares it with demo-team, and gives
demo-team its own copy too.
Configuration¶
scripts.setup writes llm.env (mode 0600), which only the gateway reads; the providers are listed under change the model. In a source checkout, restart the gateway with docker compose up -d --wait cbi-gateway. For the other options see .env.example. The agent is conversationalbi/agent/analyst.yaml, a Pydantic AI agent spec: change its instructions or model settings there. The SemanticLayer capability adds the data tools.
Development¶
uv run pytest && uv run ruff check . # gateway, agent, compiler, worker
(cd web && npm ci --ignore-scripts && npm test && npm run build)
uv run python -m tests.dev_server # real gateway + agent, platform faked
(cd runtime && npm ci --ignore-scripts && RUNTIME_KEY=kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk AGENT_URL=http://127.0.0.1:3017/ node server.mjs)
node scripts/cbi-browser.mjs # browser check against http://localhost:3007
uv run python -m scripts.api_contract # after API changes
LLM_MODEL=test:flights with BI_ALLOW_TEST_MODEL=1 replaces the LLM with a scripted agent for
tests and CI. scripts/e2e_smoke.py checks a live installation through MCP.