Synaplan
開源 AI 平台,供企業使用:自行託管的 AI 代理、符合 GDPR 的聊天機器人小工具、RAG 知識庫、WhatsApp 與 Outlook AI、MCP、多模型路由。Docker、Kubernetes 或託管於 Germ
AI 翻譯
尚未偵測到更新。GitHub 發布版本後會顯示在這裡。
個人 GitHub 帳號下公開儲存庫的擁有者可以登入並驗證所有權,自行管理介紹,無需管理員核准。
登入並產生驗證檔案,在 30 分鐘內將其提交至此儲存庫的預設分支。其他分支或分叉儲存庫中的檔案無效。然後在下方檢查檔案,即可無需管理員核准直接管理專案。請保留檔案以維持管理權限。
如需登入,將前往登入頁面。登入後請回到此處,再次點擊所選的驗證按鈕。
在專案資料夾中執行 /nomorevibe verify,即可驗證所有權並自行管理介紹。
一分鐘完成驗證製作者的介紹僅有上面的一句話。 更多詳情請閱讀儲存庫 README。 儲存庫 README ↗
正在載入完整 README。
檢視完整 README ↗Every phone enquiry goes to a voice agent (Vaani Labs) at any hour. The agent qualifies the caller against Nikhil's rules and books a consultation on the call (Cal.com). After the call, Gemini Flash classifies the transcript. Qualified leads become HubSpot deals, and the designer gets a brief by email (Resend) within two minutes. Nikhil follows it all on a mobile dashboard.
Next.js 16 (App Router, TypeScript) on Vercel · Neon Postgres · HubSpot free CRM · Resend · Cal.com · Gemini.
| Part | State |
|---|---|
| Database schema + seed | ✅ db/0001_init.sql, db/seed.sql (every app query checked against real Postgres) |
| Replay test | ✅ npm run replay -- --stub passes. Real-Gemini run needs the 12 transcripts in tests/fixtures/transcripts/ |
| Classification (Gemini) | ✅ written, not yet run against real transcripts |
| Routing + emails | ✅ written, not yet run against live Neon/Resend |
| Cal.com tools | ✅ live: Consultation event 7422764 (Mon–Sat 10:00–19:00 IST, 12 h notice, 30 min buffer), slots verified 10 Oct 2026 |
| Vaani adapter | ⏳ stub: src/lib/vaani/adapter.ts returns 501 until written against Vaani's API docs |
| HubSpot | ✅ written, not yet run live |
| Dashboard | ✅ |
| Crons | ✅ (see the cron note below) |
Caller ─▶ Vaani agent ──(mid-call)──▶ POST /api/tools/availability ─▶ Cal.com slots for the assigned designer
└─(mid-call)──▶ POST /api/tools/book ─▶ Cal.com booking (or "a designer will call within 2 hours")
call ends ──▶ POST /api/webhooks/vaani
1. verify signature 2. store raw payload 3. 200 OK
4. after(): Gemini classify → validate → confidence gate → route
qualified → designer email (+ack button) → HubSpot contact + deal
existing_client → [URGENT] email to Nikhil + escalation address
needs_review → email Nikhil (+ "pending review" brief to the designer if already booked)
declined/not_a_lead/dropped → log only (dropped with a number → call-back list)
agent said a price → compliance incident + email Nikhil
Crons: ack-check every 15 min (escalate after 2h, retry failed processing) · HubSpot sync hourly · digest 08:45 IST
Only src/lib/vaani/ knows Vaani's payload shapes. Everything else uses NormalizedInteraction / NormalizedToolCall (src/lib/types.ts). The calls table has a channel column, so WhatsApp or a web form can come in later as another adapter with no schema change.
db/0001_init.sql, then db/seed.sql. From a terminal, psql "$DATABASE_URL" -f db/0001_init.sql does the same.areas (Pune localities they prefer), and calcom_event_type_id (step 3).DATABASE_URL. The app talks to Neon over HTTPS with @neondatabase/serverless, which suits Vercel functions: there's no connection pool to manage.DATABASE_URL for you.Only the server holds DATABASE_URL. The browser never talks to the database.
Copy .env.example → .env.local for local dev, and set the same keys in Vercel. EMAIL_TEST_MODE defaults to true: every email goes to EMAIL_TEST_RECIPIENT with a [TEST → real recipient] prefix. Set it to false only when going live.
designers.calcom_event_type_id. Designers without an ID are never offered to callers during a call.CALCOM_API_KEY.CALCOM_FALLBACK_ATTENDEE_EMAIL.Qualified calls become a HubSpot contact plus a deal in the Consultation booked stage, and the dashboard's pipeline panel syncs hourly.
crm.objects.contacts.read/write, crm.objects.deals.read/write and crm.schemas.deals.read/write. Put the token in .env.local and Vercel as HUBSPOT_ACCESS_TOKEN.npm run hubspot:setup. It checks the token, creates a test pipeline "Aangan enquiries (test)" (or, on the free plan's single pipeline, adds a Consultation booked stage to it) and prints HUBSPOT_PIPELINE_ID and HUBSPOT_STAGE_CONSULTATION_BOOKED. Set both in .env.local and Vercel, then redeploy.Deals never get an amount from this system. The dashboard's "won value" only sums amounts a person typed into HubSpot on won deals.
Verify your sending domain, create an API key → RESEND_API_KEY, and set RESEND_FROM.
Pending API docs. Once the adapter is written:
agent/vaani-system-prompt.md into the agent. Apply the platform settings and run the red-team test script in agent/README.md before go-live and after every prompt change.check_availability → POST <APP_URL>/api/tools/availability. Args: location (string), consultation_mode (home | studio).book_consultation → POST <APP_URL>/api/tools/book. Args: slot (ISO start time returned by check_availability), caller_name, phone, location, consultation_mode.POST <APP_URL>/api/webhooks/vaani with VAANI_WEBHOOK_SECRET.APP_URL to the production URL.vercel.json schedules the daily digest at 15 3 * * * (03:15 UTC = 08:45 IST)..github/workflows/crons.yml). Add the repo secrets APP_URL and CRON_SECRET. GitHub can start scheduled runs a few minutes late, which is fine for these jobs. On Vercel Pro you can move both into vercel.json and delete the workflow.Authorization: Bearer $CRON_SECRET.npm run replay -- --stub # no network: gate, routing plan, email rendering, price guard
npm run replay # real Gemini over tests/fixtures/transcripts/T*.txt (needs GEMINI_API_KEY)
npm run replay -- T07 T10 # selected cases
Hard failures: outcome, decline_reason, pricing_requested, price_mentioned_by_agent (must be false), routing (T09 must never create a deal or handoff), and any price figure in a rendered email or deal. Flags are free text, so they are printed side by side but never fail the run.
src/lib/price-guard.ts). Caller-derived text is redacted before rendering. Every email and deal description is scanned again before sending, and a hit blocks the send and logs an incident. A volunteered budget shows as "Mentioned by caller (amount withheld)". The dashboard transcript view hides amounts too.incidents row + immediate email to Nikhil.src/lib/log.ts strips them from logs.(provider, external_id). Processing claims the row atomically, reuses an existing classification, and every side effect has its own dedupe record (handoffs, hubspot_sync, incidents, emails.dedupe_key).needs_review: the designer still gets the brief, marked "pending review", so the slot isn't missed. Nikhil gets the review email. No HubSpot deal is created until Nikhil decides.areas match the caller's location. The pointer moves only when a booking is made, or when a handoff is assigned post-call.