Skip to content

Add PartsLine: voice agent for auto parts counters (LiveKit + Moss) - #350

Draft
dongm58 wants to merge 1 commit into
usemoss:mainfrom
dongm58:add-partsline-example
Draft

Add PartsLine: voice agent for auto parts counters (LiveKit + Moss)#350
dongm58 wants to merge 1 commit into
usemoss:mainfrom
dongm58:add-partsline-example

Conversation

@dongm58

@dongm58 dongm58 commented Jul 7, 2026

Copy link
Copy Markdown

Pull Request Checklist

Please ensure that your PR meets the following requirements:

  • I have read the CONTRIBUTING guide.
  • I have updated the documentation (if applicable).
  • My code follows the style guidelines of this project.
  • I have performed a self-review of my own code.
  • [ X] I have added tests that prove my fix is effective or that my feature works.
  • New and existing unit tests pass locally with my changes.

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.

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • This change requires a documentation update

@CLAassistant

Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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_part tool plus set_aside / transfer_to_human flows, 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.

Comment on lines +71 to +78
result = subprocess.run(
["node", str(script_path)],
cwd=ROOT,
env={**(env or {})},
text=True,
capture_output=True,
check=True,
)
Comment on lines +188 to +199
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)
Comment on lines +219 to +241


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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants