Architecture
Kaveon is a pnpm monorepo with two applications — a Next.js frontend and a FastAPI backend — over a two-plane database (a small control + context store and a data warehouse) plus your registered data sources. The browser only ever talks to the frontend.
The two applications
| App | Path | Runtime |
|---|---|---|
kaveon-web | apps/kaveon-web | Next.js 15 · React 19 · TypeScript |
kaveon-api | apps/kaveon-api | FastAPI · Python 3.11 |
Shared TypeScript types live in packages/types.
Request flow
Browser
│ session cookie (same-origin)
▼
kaveon-web (Next.js — App Router, RSC by default)
│ /api/kaveon/[...path] — the only ingress to the API
│ stamps X-User-Email, X-User-Name, X-User-Role, X-Proxy-Secret
▼
kaveon-api (FastAPI)
├── kaveonmeta (Azure PG — control + context: datasets, charts, dashboards,
│ roles, history, and the DLM tables dlm_* / context_*)
├── kaveon (Azure PG — the data warehouse: the actual rows)
└── Data Sources (Fabric SQL · Azure SQL · PostgreSQL · MySQL · StarRocks)Frontend — kaveon-web
Next.js 15 App Router, React Server Components by default, with client components for the interactive builders.
app/
page.tsx — Homepage / NL→SQL chat
lab/ — SQL Lab (Monaco)
charts/ datasets/ — builders + lists
dashboards/ — react-grid-layout canvas
data-sources/ — connection management
docs/ — this documentation (public)
api/kaveon/[...]/ — API proxy (route.ts)
api/auth/ — NextAuth (Auth.js v5)| Key utility | Purpose |
|---|---|
utils/nlToSql.ts | Template-based NL→SQL engine — the fallback path (the DLM is primary) |
components/ContextBanner.tsx | Homepage banner: what the DLM can answer, with hover detail |
components/DatasetContextPanel.tsx | Dataset page: last-generated, duration, indexed dims/metrics, Regenerate |
utils/echartsTheme.ts | Dark/light theming for ECharts |
utils/querySemaphore.ts | Client-side query concurrency limit |
components/charts/ChartBuilderContext.tsx | Chart registry, SQL generation, builder state |
components/dashboards/DashboardCanvas.tsx | Grid-layout dashboard canvas |
Backend — kaveon-api
FastAPI with a routers/services/middleware split: one router per domain, business logic in services (query_generator, the dlm / context_* engine, auth/role resolution), and cross-cutting concerns in middleware. Identity is established by the frontend proxy, not the API: middleware reads the X-User-* headers and trusts them only when X-Proxy-Secret matchesKAVEON_PROXY_SECRET — there is no bearer token or JWT verification in the request path. A per-database connection pool is warmed at startup and kept alive with a 5-minute heartbeat; the smallkaveonmeta pool is sized independently from the warehouse pools.
Query paths — the DLM
The homepage’s primary NL→SQL engine is the DLM (Data Language Model) — a per-dataset compiled context artifact built with no hosted LLM. It resolves a question deterministically and, for the common cases, answers from precomputed context with no database scan at all. Only novel slices fall through to a single live query. Every answer is labelled honestly:
- ⚡ From context · no DB scan — a totals / by-dimension / single-dimension-filter answer served from an in-memory cache warmed from the precomputed
dlm_answersstore. - Live query · Xs — a year-slice or multi-filter combination assembled into one warehouse query, then cached.
Because the kaveonmeta control + context plane is physically separate from the kaveonwarehouse, context answers never contend with a multi-million-row scan. The in-browser template parser (utils/nlToSql.ts) remains as the fallback for shapes the DLM does not yet build (mainly time-series trends). See the NL→SQL page for details.
Theming
Every color is a CSS variable — no hardcoded values in components — so light/dark and per-user themes apply instantly. ECharts options are passed through applyChartTheme(option, isDark) so charts match.
var(--accent) /* #4A9EE8 — brand blue */
var(--bg-surface) /* cards / panels */
var(--text-primary) var(--text-muted) var(--border)