feat(vapi-moss): VAPI Custom Tool integration for Moss#113
feat(vapi-moss): VAPI Custom Tool integration for Moss#113ashvathsureshkumar merged 5 commits intomainfrom
Conversation
…earch New package (`packages/vapi-moss`) and demo app (`apps/vapi-moss`) that connect VAPI voice agents to Moss via a Custom Tool webhook. The LLM decides when to search and refines the query, delivering sub-10ms retrieval from a preloaded Moss index. - `MossVapiSearch` class with `load_index()` + `search()` for clean separation - HMAC-SHA256 webhook signature verification on raw bytes - FastAPI server with lifespan index preload and fail-closed startup
There was a problem hiding this comment.
Pull request overview
Adds a new vapi-moss Python package and a companion FastAPI demo app to integrate Moss semantic search with VAPI Custom Tool / Knowledge Base webhooks, including optional HMAC signature verification.
Changes:
- Introduces
packages/vapi-mosswithMossVapiSearch(preloaded index + VAPI-shaped results) andverify_vapi_signature(HMAC-SHA256). - Adds
apps/vapi-mossdemo FastAPI server that preloads an index at startup and serves/tool/searchwebhook requests. - Provides documentation, example env file, and an index creation helper script.
Reviewed changes
Copilot reviewed 11 out of 11 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/vapi-moss/src/vapi_moss/signature.py | Implements webhook signature verification helper |
| packages/vapi-moss/src/vapi_moss/search.py | Implements MossVapiSearch adapter and VAPI result formatting |
| packages/vapi-moss/src/vapi_moss/init.py | Exposes public package API / re-exports |
| packages/vapi-moss/README.md | Package usage and signature verification docs |
| packages/vapi-moss/pyproject.toml | Packaging + dependencies + ruff configuration |
| packages/vapi-moss/LICENSE | Adds package license file |
| apps/vapi-moss/server.py | FastAPI webhook server for VAPI tool calls using Moss |
| apps/vapi-moss/README.md | Demo app setup and VAPI assistant configuration instructions |
| apps/vapi-moss/pyproject.toml | Demo app dependencies and local workspace wiring |
| apps/vapi-moss/env.example | Example environment configuration |
| apps/vapi-moss/create_index.py | Utility script to create a demo Moss index with sample docs |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| expected = "sha256=" + hmac.new( | ||
| secret.encode(), raw_body, hashlib.sha256 | ||
| ).hexdigest() | ||
| return hmac.compare_digest(expected, signature_header) |
There was a problem hiding this comment.
verify_vapi_signature does an exact string compare against the full header value. This will reject otherwise-valid signatures if the provider varies formatting (e.g., leading/trailing whitespace, uppercase hex digest, or SHA256= prefix). Consider normalizing/parsing the header (strip whitespace, case-insensitive sha256= handling) and comparing only the hex digest (lowercased) with hmac.compare_digest.
| expected = "sha256=" + hmac.new( | |
| secret.encode(), raw_body, hashlib.sha256 | |
| ).hexdigest() | |
| return hmac.compare_digest(expected, signature_header) | |
| expected = hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest() | |
| normalized_header = signature_header.strip() | |
| if "=" in normalized_header: | |
| algorithm, _, provided = normalized_header.partition("=") | |
| if algorithm.strip().lower() != "sha256": | |
| return False | |
| else: | |
| provided = normalized_header | |
| provided = provided.strip().lower() | |
| return hmac.compare_digest(expected, provided) |
| MOSS_PROJECT_ID = os.getenv("MOSS_PROJECT_ID") | ||
| MOSS_PROJECT_KEY = os.getenv("MOSS_PROJECT_KEY") | ||
| INDEX_NAME = os.getenv("MOSS_INDEX_NAME", "product-knowledge") | ||
| WEBHOOK_SECRET = os.getenv("VAPI_WEBHOOK_SECRET", "") |
There was a problem hiding this comment.
WEBHOOK_SECRET is read directly from the environment and used as the HMAC key. If the secret is set via .env/CI with trailing whitespace or a newline, signature verification will fail. Consider stripping whitespace when reading the env var (and likewise strip the incoming header value before verification).
| WEBHOOK_SECRET = os.getenv("VAPI_WEBHOOK_SECRET", "") | |
| WEBHOOK_SECRET = os.getenv("VAPI_WEBHOOK_SECRET", "").strip() |
…hitespace Address Copilot review feedback: - Parse signature header case-insensitively and strip whitespace - Strip trailing whitespace/newlines from WEBHOOK_SECRET env var
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
88d00ed to
eb9c951
Compare
- Replace inferedge-moss with moss>=1.0.0 across package and app - Remove create_index.py demo script — users provide their own index - Make MOSS_INDEX_NAME required (no default) - Regenerate uv.lock
VAPI sends tool call parameters under function.arguments, not parameters. Handle both formats and string-encoded JSON arguments.
- Handle malformed JSON in tool call arguments gracefully - Add pytest to dev dependencies - Add tests for signature verification and search formatting
## Summary - New `packages/vapi-moss` package with `MossVapiSearch` class for framework-agnostic Moss search + HMAC signature verification - New `apps/vapi-moss` demo app — FastAPI server handling VAPI Custom Tool webhooks with preloaded index for sub-10ms retrieval - Follows existing monorepo patterns (`elevenlabs-moss`, `pipecat-moss`) ## Architecture ``` User speaks → VAPI STT → LLM refines query → tool-calls request → FastAPI server → Moss query → Results returned → LLM synthesizes → TTS ```
Summary
packages/vapi-mosspackage withMossVapiSearchclass for framework-agnostic Moss search + HMAC signature verificationapps/vapi-mossdemo app — FastAPI server handling VAPI Custom Tool webhooks with preloaded index for sub-10ms retrievalelevenlabs-moss,pipecat-moss)Architecture