This is a small Vite + React example for the simple ask-message flow in KalamDB.
What it does:
- the browser writes a user message into the
chat_demo.messagesUSER table with the ORM chat_demo.ai_inboxreceives those inserts as a topicsrc/agent.tsrunsrunAgent()from@kalamdb/consumer- the agent writes progress rows into
chat_demo.agent_events - the agent commits one assistant reply back into
chat_demo.messages - every open browser tab sees the rows through live subscriptions
There is no Gemini call in this example. The agent returns a deterministic demo reply so the example is easy to run locally.
This README uses the ask path: send one message and get one response. An agent-message workflow with human approval would add a pending approvals table and require a user to approve before the final assistant message is committed.
From this folder:
npm installThe npm scripts build the local KalamDB TypeScript SDK packages if their generated dist output is missing.
Start a KalamDB server. The fastest Docker option is:
docker run -d --name kalamdb -p 8080:8080 -e KALAMDB_SERVER_HOST=0.0.0.0 -e KALAMDB_ROOT_PASSWORD=kalamdb123 -e KALAMDB_JWT_SECRET=local-dev-secret-please-change-32chars -v kalamdb_data:/data jamals86/kalamdb:latestThen prepare the demo schema and environment file:
npm run setupsetup also runs npm run generate:schema, which writes src/schema.generated.ts from the live chat_demo namespace with kTable.user(...), kTable.stream(...), table config exports, inferred row types, and typed KalamDB system columns such as _seq.
Run the agent worker in one terminal:
npm run agentRun the browser app in another terminal:
npm run devOpen the Vite URL printed by npm run dev, usually http://127.0.0.1:5173.
The browser write is intentionally small. The table comes directly from src/schema.generated.ts, then the form submit writes a user row through the ORM:
await db.insert(chatMessages).values({
room: ROOM,
role: 'user',
author: CHAT_USERNAME,
sender_username: CHAT_USERNAME,
content,
});The generated table metadata is also available in code:
chatMessagesConfig.tableType; // "user"
chatMessagesConfig.systemColumns; // ["_seq", "_deleted"]The worker uses the generated row type directly. runAgent() decodes KalamDB topic payloads and unwraps { row: ... } envelopes automatically, so there is no custom parser in the demo agent:
await runAgent<ChatDemoMessages>({
client,
name: 'chat-ai-agent',
topic: 'chat_demo.ai_inbox',
groupId: 'chat-ai-agent',
onRow: async (_ctx, row) => {
if (row.role !== 'user') return;
// Build and persist the assistant reply here.
},
});That insert is enough to wake the agent because setup creates this route:
CREATE TOPIC chat_demo.ai_inbox;
ALTER TOPIC chat_demo.ai_inbox ADD SOURCE chat_demo.messages ON INSERT;Type a short message such as:
latency spike after deploy
You should see your user row, live agent progress, and then a final AI reply: row in the same chat thread. Open a second browser tab to see the live updates arrive there too.
- src/App.tsx: browser client, ORM insert, and live subscriptions
- src/schema.generated.ts: generated
kTable()definitions with_seq, table-kind metadata, config exports, and inferred row types - src/agent.ts: topic consumer and deterministic reply worker
- chat-app.sql: USER table, STREAM table, and namespace setup
- setup.sh: idempotent local bootstrap script
- tests/chat.spec.mjs: browser end-to-end test
npm run setup creates or reuses this local account and writes .env.local:
- user:
admin - password:
kalamdb123
The browser and the worker both use that account for the local demo. The worker uses EXECUTE AS USER when it writes agent events and assistant replies for the message sender. In production, a service account can do the same when its role is authorized for that target user.
With KalamDB running and after npm run setup:
npm testThe test starts the agent, opens browser tabs, sends an ask message, and verifies that both tabs receive the user message and the assistant reply.
If setup cannot reach KalamDB, check the server health endpoint:
curl http://127.0.0.1:8080/healthIf the agent is running but no reply appears, rerun setup so the topic route exists:
npm run setupIf port 5173 is busy, Vite prints the next available local URL. Use the URL from the terminal output.
Part of the KalamDB project. See the repository root for license details.