A realistic, agentic support-operations console for a fictional e-commerce store ("Northwind Goods"). It is a support-ticket inbox plus a standalone conversational copilot, backed by a real SQLite database seeded with customers, orders, products, tickets, and help-center articles.
The app was built as a test subject for LLM/agent observability tooling: it deliberately exercises many distinct AI code paths — streaming chat, multi-step tool-calling loops, structured output, and embeddings-based RAG — plus real DB reads/writes and an intentionally flaky tool that produces errors.
It is instrumented with Sentry — error monitoring, tracing, and AI Agent monitoring (
gen_ai.*spans for agent runs, tool calls, token cost, and conversations). Sentry is optional: leave the DSN unset to run without it.
- Next.js 16 (App Router) + React 19 + TypeScript + Tailwind CSS v4
- Vercel AI SDK v7 (
ai,@ai-sdk/react) with the OpenRouter provider — chat, tools, andgenerateObject - OpenAI client → OpenRouter for embeddings (a second, distinct LLM path)
- SQLite + Drizzle ORM (
better-sqlite3) - Sentry (
@sentry/nextjs) — errors, tracing, and AI Agent monitoring
pnpm install
cp .env.example .env.local # then add your OpenRouter API key
pnpm db:push # create the SQLite schema
pnpm db:seed # seed data (+ embed help articles if key is set)
pnpm dev # http://localhost:3000If you seed before setting OPENROUTER_API_KEY, the relational data is still
created and the app runs; run pnpm db:embed afterward to build the help-center
embeddings that power semantic search.
| Variable | Default | Purpose |
|---|---|---|
OPENROUTER_API_KEY |
– | Required for all AI features |
OPENROUTER_MODEL |
openai/gpt-4o-mini |
Chat/agent model (needs tool-calling + JSON) |
OPENROUTER_EMBEDDING_MODEL |
openai/text-embedding-3-small |
RAG embeddings |
DATABASE_URL |
file:./northwind.db |
SQLite file |
CHAOS_FAILURE_RATE |
0.25 |
Probability the checkInventory tool fails |
SENTRY_DSN · NEXT_PUBLIC_SENTRY_DSN |
– | Enable Sentry (server · client); unset = no observability |
SENTRY_ORG · SENTRY_PROJECT |
– | Source-map upload on production builds |
SENTRY_AUTH_TOKEN |
– | Source-map upload token (build-time, secret) |
Reads: lookupCustomer, getCustomerOrders, getOrder, searchProducts,
checkInventory (flaky), searchHelpArticles (RAG).
Writes: issueRefund, updateTicketStatus, postTicketReply, escalateTicket,
createTicket.
The inbox Auto-resolve action runs a three-agent pipeline on a ticket, each on a different model (via OpenRouter), with explicit hand-offs:
Triage (gpt-4o-mini) → Specialist (claude-sonnet-4.5 — refunds/billing,
tech/product, or general) → QA (gpt-4o), which approves before any reply is
posted. Code in lib/ai/orchestrator.ts + lib/ai/models.ts; exposed at
POST /api/tickets/[id]/resolve. Per-agent models are overridable via
OPENROUTER_{TRIAGE,SPECIALIST,QA}_MODEL.
With a DSN set, the app reports errors and traces, and every AI call surfaces in
Sentry's AI Agents view as gen_ai.* spans — agent runs, tool calls, token
cost, hand-offs, and per-ticket conversation grouping. Config lives in
sentry.{server,edge}.config.ts, instrumentation-client.ts, and next.config.ts;
prompt/response capture is on via dataCollection: {}. Leave the DSN unset to disable.
- Inbox (
/inbox) — ticket list · thread · customer context, with a ticket-scoped copilot, two structured-output actions (Summarize thread, Draft reply), and Auto-resolve (the multi-agent pipeline below). - Copilot Chat (
/chat) — a standalone agent with all tools and persisted conversation history.
- Open a ticket → Summarize thread, then Draft reply (structured output).
- In the ticket copilot: "Look up this customer and list their recent orders" (multi-tool read loop).
- "Issue a refund for their most recent order" (a write tool that mutates the DB).
- "What's our return policy for opened items?" (embeddings RAG).
- Ask "Is SKU ELE-SPEAKER in stock?" a few times — the warehouse tool fails intermittently by design.
- In
/chat: "Find the customer for ticket #2, check if the item they mention is in stock, and draft an apology" (a longer tool chain).
app/ # routes: /inbox, /chat, and /api/*
api/chat/ # streaming agent endpoint (the main tool loop)
api/tickets/[id]/ # summarize, draft-reply, resolve (multi-agent) + reads
components/ # UI (InboxClient, ChatClient, CopilotPanel, ToolCallCard…)
lib/ai/ # provider, models, embeddings, rag, tools, agent, orchestrator
sentry.*.config.ts # Sentry init (server/edge); instrumentation(-client).ts at root
lib/queries.ts # DB read helpers
db/ # schema, client, seed + embed scripts
pnpm dev · pnpm build · pnpm start · pnpm db:push · pnpm db:seed ·
pnpm db:embed