Skip to content

Commit 78886cc

Browse files
committed
examples(voice-agent): address review
- README: list Cartesia as TTS (OpenAI=LLM, Deepgram=STT), add CARTESIA_API_KEY, fix DEMO_SCRIPT link to DEMO_SCRIPT_METADATA.md. - next.config: derive dir from import.meta.url (Node 18 compatible). - agent.py: validate region from data channel against an allow-list; fix stale 'upload.py' hint to 'seed_index.py'. - seed_index.py: fail fast if Moss credentials are missing. - RetrievalPanel: add type=button + aria-pressed on the region toggle.
1 parent 93fa217 commit 78886cc

5 files changed

Lines changed: 25 additions & 7 deletions

File tree

moss-live-labs/examples/voice-agent/README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ Browser (web/) ⟷ LiveKit room ⟷ agent.py (STT → LLM → TTS) ⟷ Mos
1313

1414
- A [Moss](https://moss.dev) account (project ID + key)
1515
- [LiveKit](https://livekit.io) running locally (`livekit-server --dev`)
16-
- [OpenAI](https://platform.openai.com) (LLM + text-to-speech) and
17-
[Deepgram](https://deepgram.com) (speech-to-text) API keys
16+
- [OpenAI](https://platform.openai.com) (LLM), [Deepgram](https://deepgram.com)
17+
(speech-to-text), and [Cartesia](https://cartesia.ai) (text-to-speech) API keys
1818
- Python 3.14+ (`uv`) and Node 18+ (`npm`) for the web UI
1919

2020
## Setup
@@ -25,7 +25,7 @@ cp .env.example .env # fill in your Moss + provider keys
2525
python agent.py download-files
2626
```
2727

28-
`.env` keys: `MOSS_PROJECT_ID`, `MOSS_PROJECT_KEY`, `OPENAI_API_KEY`, `DEEPGRAM_API_KEY`.
28+
`.env` keys: `MOSS_PROJECT_ID`, `MOSS_PROJECT_KEY`, `OPENAI_API_KEY`, `DEEPGRAM_API_KEY`, `CARTESIA_API_KEY`.
2929
For local LiveKit, leave the `LIVEKIT_*` values as-is; for LiveKit Cloud, set them to your
3030
project's URL/key/secret and copy the same three into `web/.env.local`. The index name
3131
defaults to `demo-customer_faqs` (override with `MOSS_INDEX_NAME`).
@@ -67,7 +67,7 @@ On each user turn, `agent.py` queries Moss and publishes the results to the Live
6767
on the `moss.retrieval` data channel (`{query, docs:[{text, score}], took_ms}`). The web
6868
UI listens on that channel and renders them. The voice pipeline is otherwise untouched.
6969

70-
See [`DEMO_SCRIPT.md`](./DEMO_SCRIPT.md) for a ready-to-record walkthrough.
70+
See [`DEMO_SCRIPT_METADATA.md`](./DEMO_SCRIPT_METADATA.md) for a ready-to-record walkthrough.
7171

7272
## Resources
7373

moss-live-labs/examples/voice-agent/agent.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,10 @@
2828
INDEX_NAME = os.getenv("MOSS_INDEX_NAME", "demo-customer_faqs")
2929
# This support line serves one region. Metadata filtering scopes retrieval to
3030
# region-specific policies + global ("all") docs. Set MOSS_REGION=EU to compare.
31+
ALLOWED_REGIONS = {"US", "EU"}
3132
REGION = os.getenv("MOSS_REGION", "US")
33+
if REGION not in ALLOWED_REGIONS:
34+
REGION = "US"
3235

3336
logging.basicConfig(level=logging.INFO)
3437
logger = logging.getLogger("moss-agent")
@@ -135,7 +138,7 @@ async def entrypoint(ctx: JobContext):
135138
logger.info(f"Successfully loaded index: {INDEX_NAME}")
136139
except Exception as e:
137140
logger.warning(f"Index not found or failed to load: {e}")
138-
logger.warning("Moss queries will fail until the index is created. Run upload.py first.")
141+
logger.warning("Moss queries will fail until the index is created. Run seed_index.py first.")
139142

140143
# Create Session
141144
session = AgentSession(
@@ -163,9 +166,11 @@ def _on_data(pkt: rtc.DataPacket):
163166
if pkt.topic == "moss.region":
164167
try:
165168
r = json.loads(bytes(pkt.data).decode("utf-8")).get("region")
166-
if r:
169+
if r in ALLOWED_REGIONS:
167170
agent.region = r
168171
logger.info(f"Region filter set to {r}")
172+
else:
173+
logger.warning(f"Ignoring unknown region {r!r} (allowed: {sorted(ALLOWED_REGIONS)})")
169174
except Exception as e:
170175
logger.warning(f"Bad region packet: {e}")
171176

moss-live-labs/examples/voice-agent/seed_index.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,11 @@
2828

2929

3030
async def main() -> None:
31+
if not MOSS_PROJECT_ID or not MOSS_PROJECT_KEY:
32+
raise SystemExit(
33+
"Missing MOSS_PROJECT_ID / MOSS_PROJECT_KEY. Copy .env.example to .env and fill them in."
34+
)
35+
3136
faqs = json.loads(FAQS_PATH.read_text())
3237
docs = [
3338
DocumentInfo(

moss-live-labs/examples/voice-agent/web/components/RetrievalPanel.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,8 @@ export function RetrievalPanel() {
6161
{REGIONS.map((r) => (
6262
<button
6363
key={r}
64+
type="button"
65+
aria-pressed={region === r}
6466
onClick={() => setRegion(r)}
6567
style={{
6668
padding: "6px 18px",
Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,14 @@
1+
import { dirname } from "node:path";
2+
import { fileURLToPath } from "node:url";
3+
4+
// Node 18 doesn't provide import.meta.dirname; derive it from import.meta.url.
5+
const rootDir = dirname(fileURLToPath(import.meta.url));
6+
17
/** @type {import('next').NextConfig} */
28
const nextConfig = {
39
reactStrictMode: true,
410
// This app lives in a monorepo with other lockfiles; pin the tracing root here.
5-
outputFileTracingRoot: import.meta.dirname,
11+
outputFileTracingRoot: rootDir,
612
};
713

814
export default nextConfig;

0 commit comments

Comments
 (0)