Skip to content

About

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

InsightPilot

Live demo: https://insightpilot-jade.vercel.app

Ask a question in plain English. A planner agent turns it into a structured, schema-validated query plan; a critic agent reviews that plan and can send it back for revision; a deterministic executor runs it as a safe, parameterized query — the LLM never touches SQL directly; a narrator agent summarizes the result. The resulting widget is pinned to a live dashboard that updates in real time as new data arrives.

It's a multi-tenant analytics copilot for a fictional SaaS support/billing platform, modeled on the pattern of a dashboard-widget system built at a real production job — reports built by end users from natural language, not fixed to a handful of pre-built charts.

Try it

Sign in at /login with any of the seeded demo accounts (password demo1234 for all):

Role Email Scope
Admin admin@insightpilot.dev All organizations, via an org switcher; user management
Manager manager@insightpilot.dev Own organization (Northwind Ops); can create/pin widgets
Viewer viewer@insightpilot.dev Own organization (Northwind Ops); read-only

Ask something like:

  • "How many tickets are currently open?"
  • "Weekly ticket volume as a line chart"
  • "Ratio of resolved to total tickets"
  • "Tickets by category as a pie chart"
  • "Support workload as a pivot of priority vs status"

As a manager/admin, hit Simulate activity on the dashboard to insert fresh rows and watch pinned widgets update live via Supabase Realtime — no refresh needed.

Architecture

Stack: Next.js 16 (App Router) · TypeScript · Postgres (Supabase) via Drizzle ORM · Supabase Realtime · NextAuth (Credentials/JWT) · Groq (multi-agent pipeline) · Tailwind CSS · Recharts · react-grid-layout

Data model: a seeded multi-tenant dataset — organizations, users (role-scoped: admin/manager/viewer), tickets, subscriptions, events, and the widgets a user pins to their dashboard (src/lib/db/schema.ts).

The agent pipeline (src/lib/agents/) is the core of the project:

  1. Planner (planner.ts) — given the question and a fixed description of the available data (SCHEMA_DESCRIPTION in types.ts), asks Groq for a structured JSON plan: source table, metric, chart type, filters, grouping. The plan is validated against a Zod schema (WidgetPlanSchema) before anything downstream trusts it — the model can only choose from a whitelist of known fields and operators, it never writes SQL.
  2. Critic (critic.ts) — reviews the plan against the original question (right chart type for the intent? sane filters?) and can request one revision from the planner with specific feedback.
  3. Executor (src/lib/query/executor.ts) — deterministic, no LLM involved. Turns the validated plan into a parameterized Drizzle query by mapping whitelisted field names to real columns; every query is scoped to the caller's organization.
  4. Narrator (narrator.ts) — writes a one- or two-sentence plain-English summary of the result.

All four stages stream live to the UI over Server-Sent Events (/api/agent/plan), so the "thinking" is visible, not just the final answer.

Multi-tenancy & RBAC: every query is scoped server-side to an organization. Managers/viewers are hard-locked to their own org; admins pick an org via a cookie-backed switcher (src/lib/rbac.ts) — client-supplied org overrides are only ever honored for the admin role.

Accessibility: streaming agent output lands in an ARIA live region; every chart (ChartRenderer.tsx) renders alongside an accessible data table, not just an SVG; the widget grid is draggable/resizable by mouse but every resize also has keyboard-operable buttons, since drag-and-drop alone excludes keyboard users.

Local setup

npm install
cp .env.local.example .env.local   # fill in the values below
npm run db:push                    # create tables in your Postgres/Supabase project
npm run db:seed                    # seed demo orgs, users, and ~90 days of data
npm run dev

Environment variables (.env.local)

Variable Where to get it
DATABASE_URL Supabase project → Connect → Transaction pooler connection string (port 6543 — the app is configured for pooler mode, not a direct connection)
NEXT_PUBLIC_SUPABASE_URL Supabase project → Settings → Data API → Project URL
NEXT_PUBLIC_SUPABASE_ANON_KEY Supabase project → Settings → Data API → anon public key (never the service_role/secret key — this one ships to the browser)
GROQ_API_KEY console.groq.com/keys (free tier)
NEXTAUTH_SECRET Any random string, e.g. openssl rand -base64 32
NEXTAUTH_URL http://localhost:3000 locally; your deployed URL in production

For live widget updates to work, enable Realtime replication on the tickets, subscriptions, and events tables (Supabase dashboard → Database → Replication).

Other scripts

npm run lint        # eslint .
npm run build       # production build
npm run db:studio   # Drizzle Studio, to browse the seeded data

Deployment

Deployed on Vercel with auto-deploy on push to main. The same environment variables above are set as Production env vars in the Vercel project; NEXTAUTH_URL is set to the deployed domain.

Design notes / known simplifications

  • Realtime scope: the Supabase Realtime subscription currently broadcasts row changes across all organizations, not filtered per-tenant at the database level — fine for this seeded demo, but a real multi-tenant product would add Row Level Security policies scoping Realtime broadcasts per org too.
  • Narrator text is a snapshot: a pinned widget's chart value refreshes live, but its plain-English sentence is written once, when the widget is created, and stored as-is — regenerating it on every view would mean an extra LLM call on every dashboard load.
  • Single free-tier Postgres connection pool: using Supabase's transaction pooler (not a direct connection) since serverless functions open/close connections far more often than a long-lived server would.

About

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages