Skip to content

Repository files navigation

Northwind Support Copilot

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.

Stack

  • 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, and generateObject
  • OpenAI client → OpenRouter for embeddings (a second, distinct LLM path)
  • SQLite + Drizzle ORM (better-sqlite3)
  • Sentry (@sentry/nextjs) — errors, tracing, and AI Agent monitoring

Getting started

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:3000

If 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.

Environment

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)

What the agent can do (tools)

Reads: lookupCustomer, getCustomerOrders, getOrder, searchProducts, checkInventory (flaky), searchHelpArticles (RAG). Writes: issueRefund, updateTicketStatus, postTicketReply, escalateTicket, createTicket.

Multi-agent auto-resolve

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.

Observability (Sentry)

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.

Surfaces

  • 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.

Things to try (each generates rich agent activity)

  1. Open a ticket → Summarize thread, then Draft reply (structured output).
  2. In the ticket copilot: "Look up this customer and list their recent orders" (multi-tool read loop).
  3. "Issue a refund for their most recent order" (a write tool that mutates the DB).
  4. "What's our return policy for opened items?" (embeddings RAG).
  5. Ask "Is SKU ELE-SPEAKER in stock?" a few times — the warehouse tool fails intermittently by design.
  6. 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).

Project layout

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

Scripts

pnpm dev · pnpm build · pnpm start · pnpm db:push · pnpm db:seed · pnpm db:embed

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages