Add PartsLine: voice agent for auto parts counters (LiveKit + Moss) - #350
Add PartsLine: voice agent for auto parts counters (LiveKit + Moss)#350dongm58 wants to merge 1 commit into
Conversation
|
|
There was a problem hiding this comment.
Pull request overview
Adds a new example app under apps/partsline demonstrating a browser-based LiveKit voice agent backed by Moss retrieval, including a Next.js UI with a “proof layer” (lookup chips), a simple call log, and a Python LiveKit agent worker with strict grounded-lookup guardrails.
Changes:
- Introduces a Next.js demo UI (voice test + demo transcript + call log) and API routes for minting LiveKit join tokens and reading call log data.
- Adds a Python LiveKit agent with Moss-backed
lookup_parttool plusset_aside/transfer_to_humanflows, session limits, and SQLite persistence. - Adds extensive Python tests that pin the static wiring/contract for prompts, tool behavior, call-log schema, and observability logging.
Reviewed changes
Copilot reviewed 65 out of 65 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| apps/partsline/tsconfig.json | TypeScript config for the PartsLine Next.js app. |
| apps/partsline/package.json | Declares Next.js/React/LiveKit dependencies and scripts. |
| apps/partsline/eslint.config.mjs | ESLint config using Next core-web-vitals preset and build ignores. |
| apps/partsline/.gitignore | Ignores env files, caches, build artifacts, logs, and lockfile. |
| apps/partsline/.env.example | Documents required env vars for LiveKit, STT/TTS/LLM, Moss, DB path, and session limits. |
| apps/partsline/README.md | End-user quickstart + explanation of behavior and architecture. |
| apps/partsline/PROJECT.md | Deeper design/guardrails notes and evaluation rationale for the demo. |
| apps/partsline/LICENSE | MIT license for the PartsLine app. |
| apps/partsline/catalog/demo_catalog.json | Demo catalog data seeded into Moss with vehicle metadata + traps (supersession, ambiguity, missing vehicle). |
| apps/partsline/seed.py | Seeds the demo catalog into a Moss index. |
| apps/partsline/query.py | CLI tool to query the Moss index interactively with optional metadata filters. |
| apps/partsline/app/layout.tsx | Next.js root layout and metadata. |
| apps/partsline/app/page.tsx | App home page rendering the demo client. |
| apps/partsline/app/PartsLineDemoClient.tsx | Main demo client: LiveKit connect flow + transcript + lookup-chip + call-ended proof layer rendering. |
| apps/partsline/app/LookupChip.tsx | UI component for rendering structured lookup results. |
| apps/partsline/app/voice-test/page.tsx | Voice-test route page. |
| apps/partsline/app/voice-test/VoiceTestClient.tsx | Minimal LiveKit connect + transcript rendering harness. |
| apps/partsline/app/calls/page.tsx | /calls page wiring (client component mount). |
| apps/partsline/app/calls/CallsListClient.tsx | Client-side call log fetch + rendering. |
| apps/partsline/app/calls/callLog.ts | Server-side SQLite reader (read-only) for the call log. |
| apps/partsline/app/api/token/route.ts | API route that mints a short-lived LiveKit participant token configured to dispatch the agent. |
| apps/partsline/app/api/calls/route.ts | API route that returns call log rows via the reader. |
| apps/partsline/agent/init.py | Declares the Python agent package. |
| apps/partsline/agent/main.py | LiveKit AgentServer wiring, tool wrappers, proof-layer emits, observability logging, and session orchestration. |
| apps/partsline/agent/prompts.py | System prompt encoding grounded retrieval and escalation guardrails. |
| apps/partsline/agent/requirements.txt | Python runtime deps for the agent worker. |
| apps/partsline/agent/tools/init.py | Declares the tools package. |
| apps/partsline/agent/tools/lookup_part.py | Moss-backed lookup tool enforcing vehicle-filtered retrieval + disambiguation/supersession handling. |
| apps/partsline/agent/tools/set_aside.py | Set-aside tool for quoted in-stock parts. |
| apps/partsline/agent/tools/transfer.py | Transfer tool that records context and returns a handoff payload. |
| apps/partsline/agent/outcome.py | Call outcome model + JSON contract for persistence and UI consumption. |
| apps/partsline/agent/db.py | SQLite persistence for append-only CALL_LOG. |
| apps/partsline/agent/process_lock.py | Lockfile-based single-process guard for the agent worker. |
| apps/partsline/agent/session_limits.py | Idle + max-duration watchdogs and shutdown helpers. |
| apps/partsline/agent/check_moss.py | Connectivity check for Moss credentials/index loading. |
| apps/partsline/requirements-dev.txt | Dev tooling pins for pytest/ruff/mypy in this app directory. |
| apps/partsline/tests/init.py | Marks the tests package. |
| apps/partsline/tests/test_t1_env_contract.py | Verifies .env.example keys and .gitignore behavior. |
| apps/partsline/tests/test_t1_retrieval_catalog.py | Validates demo catalog traps + seed script behavior. |
| apps/partsline/tests/test_t1_call_outcome.py | Validates call outcome record schema and documentation. |
| apps/partsline/tests/test_t1_call_log_db.py | Validates SQLite save/list ordering and immutability of persisted records. |
| apps/partsline/tests/test_t2_agent_worker.py | Verifies agent worker wiring and component choices. |
| apps/partsline/tests/test_t2_call_end_persistence.py | Pins call persistence then call-ended emission order. |
| apps/partsline/tests/test_t2_lookup_part_tool.py | Tests lookup tool behavior with fake Moss client and catalog fixtures. |
| apps/partsline/tests/test_t2_set_aside_tool.py | Tests set-aside tool acceptance/rejection behavior. |
| apps/partsline/tests/test_t3_retrieval_agent.py | Verifies LLM/provider wiring and prompt/tool constraints. |
| apps/partsline/tests/test_t3_lookup_chip_events.py | Tests lookup-chip proof-layer publishing behavior. |
| apps/partsline/tests/test_t3_token_endpoint.py | Tests token endpoint JWT shape/signing and error behavior. |
| apps/partsline/tests/test_t3_transfer_tool.py | Tests transfer tool normalization/context payloads. |
| apps/partsline/tests/test_t4_disambiguation_loop.py | Pins prompt instructions for the ambiguity recall loop. |
| apps/partsline/tests/test_t4_real_demo_page.py | Verifies main demo page/client wiring. |
| apps/partsline/tests/test_t4_transfer_setaside_wiring.py | Tests tool registration and wrapper updates to session call outcome. |
| apps/partsline/tests/test_t4_voice_test_page.py | Verifies voice test page/client wiring and accessibility hooks. |
| apps/partsline/tests/test_t5_e2e_wiring.py | Static wiring checks for browser token fetch and agent dispatch claims. |
| apps/partsline/tests/test_t5_lookup_chips_page.py | Verifies lookup-chip UI integration and “no extra fetch” behavior. |
| apps/partsline/tests/test_t5_trigger_guard_verification.py | Tests prompt guardrails and tool-based transfer/set-aside gating. |
| apps/partsline/tests/test_t6_calls_page.py | Verifies calls page/API/reader contract and read-only DB usage. |
| apps/partsline/tests/test_t6_endpointing_numbers.py | Pins endpointing config intended to help spoken part numbers. |
| apps/partsline/tests/test_t7_proof_layer_path.py | Pins proof-layer contract across agent + demo + calls page. |
| apps/partsline/tests/test_livekit_observability_wiring.py | Tests structured logging for LiveKit turn metrics. |
| apps/partsline/tests/test_tool_timing_instrumentation.py | Tests structured tool timing instrumentation log payloads. |
| apps/partsline/tests/test_check_moss.py | Tests Moss connectivity check utility output/exit codes. |
| apps/partsline/tests/test_agent_process_lock.py | Tests lockfile behavior and error messaging. |
| apps/partsline/tests/test_session_limits.py | Tests watchdog correctness and env override handling. |
| apps/partsline/tests/test_session_limits_wiring.py | Tests agent wiring to session limits and shutdown sequence. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| result = subprocess.run( | ||
| ["node", str(script_path)], | ||
| cwd=ROOT, | ||
| env={**(env or {})}, | ||
| text=True, | ||
| capture_output=True, | ||
| check=True, | ||
| ) |
| stage_start = perf_counter() | ||
| outcome = ctx.userdata.call_outcome | ||
| quoted = next( | ||
| ( | ||
| p | ||
| for p in outcome.parts | ||
| if p["part_number"].upper() == part_number.strip().upper() | ||
| ), | ||
| None, | ||
| ) | ||
| available = quoted["stock"] if quoted else 0 | ||
| stages["quote_lookup_seconds"] = _elapsed_since(stage_start) |
|
|
||
|
|
||
| def part_index() -> dict[str, PartRecord]: | ||
| records: dict[str, PartRecord] = {} | ||
| for entry in load_catalog_entries(): | ||
| metadata = entry["metadata"] | ||
| part_number = metadata.get("part_number") | ||
| if not part_number: | ||
| continue | ||
|
|
||
| stock = int(metadata.get("stock", "0")) | ||
| record: PartRecord = { | ||
| "part_number": part_number, | ||
| "price": metadata["price"], | ||
| "stock": stock, | ||
| "metadata": metadata, | ||
| } | ||
| superseded_by = metadata.get("superseded_by") | ||
| if superseded_by: | ||
| record["superseded_by"] = superseded_by | ||
| records[part_number] = record | ||
|
|
||
| return records |
Pull Request Checklist
Please ensure that your PR meets the following requirements:
Description
Adds PartsLine under apps/partsline - a browser-based voice agent for
auto parts counters, built on Moss for retrieval and LiveKit for voice.
Demonstrates grounded, vehicle-filtered retrieval: the agent asks when
a lookup is ambiguous, tells the caller when a part's been replaced
rather than quoting a discontinued one, and refuses to guess when a
vehicle isn't in the catalog instead of returning a confident wrong
answer.
Not fixing an issue - this is a new example contribution.