Skip to content

Add travel-concierge example (cloud catalog + live session)#347

Open
HarshaNalluru wants to merge 6 commits into
mainfrom
examples/add-travel-concierge
Open

Add travel-concierge example (cloud catalog + live session)#347
HarshaNalluru wants to merge 6 commits into
mainfrom
examples/add-travel-concierge

Conversation

@HarshaNalluru

Copy link
Copy Markdown
Contributor

What

A new voice-agent example under moss-live-labs/examples/travel-concierge that shows Moss answering from two indexes in a single call:

  • Pre-loaded catalog (cloud index) — long-term trip knowledge, shared across every call.
  • Live session — an in-memory session index that captures what the traveler says on this call.

Each turn the agent recalls the traveler's stated preferences from the session and recommends matching trips from the catalog. Both result sets are published on a moss.retrieval data channel, and the web UI renders them side by side, lighting up per turn with per-query latency.

Why it's interesting

  • Demonstrates long-term knowledge + short-term memory in the same call — one pre-loaded index and one live session, both queried in milliseconds.
  • Facts are distilled from each turn before they're stored, so the session holds clean preferences (e.g. "Budget is about $2,500 per person") rather than raw questions.

Contents

  • agent.py — LiveKit agent querying catalog + session each turn
  • seed_index.py — seeds the catalog cloud index
  • data/catalog.json — 14 destinations
  • web/ — Next.js UI showing both panels
  • README.md, DEMO_SCRIPT.md

Try it

uv sync && cp .env.example .env   # fill in Moss + provider keys
python seed_index.py
livekit-server --dev
python agent.py dev
cd web && npm install && cp .env.local.example .env.local && npm run dev

Docs: https://docs.moss.dev/docs/integrate/sessions

… session)

A voice travel concierge that answers from two Moss indexes at once:
- a pre-loaded catalog (long-term, shared across every call)
- a live session that captures what the traveler says on this call

Each turn it recalls stated preferences from the session and recommends
trips from the catalog. Facts are distilled from each turn before they
are stored, so the session holds clean preferences rather than raw
questions. The web UI shows both indexes side by side, lighting up per
turn with per-query latency.

Includes agent.py, seed_index.py, a Next.js web UI, README, and a demo
script.
@HarshaNalluru
HarshaNalluru requested a review from r4ghu as a code owner July 7, 2026 03:11
Copilot AI review requested due to automatic review settings July 7, 2026 03:11
@CLAassistant

CLAassistant commented Jul 7, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

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 experimental travel-concierge voice-agent example under moss-live-labs/ that demonstrates querying two Moss indexes in the same LiveKit call (pre-seeded cloud catalog + per-call live session memory), with a Next.js UI that visualizes both retrieval result sets side-by-side.

Changes:

  • Introduces a Python LiveKit agent that queries a pre-loaded catalog index and a per-call session index, publishing retrieval payloads over a LiveKit data channel.
  • Adds a Next.js web UI (LiveKit Components) to connect to the room, render transcript, and render dual retrieval panels.
  • Adds supporting assets/docs/config (catalog seed script, env examples, demo script, lockfiles, styling).

Reviewed changes

Copilot reviewed 21 out of 24 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
moss-live-labs/examples/travel-concierge/web/tsconfig.json TypeScript config for the Next.js UI.
moss-live-labs/examples/travel-concierge/web/public/moss-wordmark.svg Adds branding asset used by the UI header.
moss-live-labs/examples/travel-concierge/web/package.json Defines Next.js + LiveKit UI dependencies and scripts.
moss-live-labs/examples/travel-concierge/web/package-lock.json Locks JS dependency versions for reproducible installs.
moss-live-labs/examples/travel-concierge/web/next.config.mjs Next.js configuration for monorepo tracing root (and should handle LiveKit server SDK externals).
moss-live-labs/examples/travel-concierge/web/lib/types.ts Shared types for retrieval payload and docs rendered in the UI.
moss-live-labs/examples/travel-concierge/web/components/Transcript.tsx Renders live STT/TTS transcript from LiveKit transcriptions.
moss-live-labs/examples/travel-concierge/web/components/DualPanel.tsx Subscribes to moss.retrieval data channel and renders catalog vs session hits.
moss-live-labs/examples/travel-concierge/web/components/AgentSide.tsx Agent “orb”, state label, transcript, and control bar UI.
moss-live-labs/examples/travel-concierge/web/app/page.tsx Main page: connect flow and LiveKitRoom layout wiring.
moss-live-labs/examples/travel-concierge/web/app/layout.tsx App metadata, fonts, LiveKit styles, global CSS inclusion.
moss-live-labs/examples/travel-concierge/web/app/globals.css Styling for split layout and retrieval panels.
moss-live-labs/examples/travel-concierge/web/app/api/token/route.ts API route that mints a LiveKit participant token for local dev.
moss-live-labs/examples/travel-concierge/web/.gitignore Ignores Next build output, node_modules, and env files.
moss-live-labs/examples/travel-concierge/web/.env.local.example Example LiveKit env vars for the UI token route.
moss-live-labs/examples/travel-concierge/seed_index.py Seeds the cloud catalog index from data/catalog.json.
moss-live-labs/examples/travel-concierge/README.md Setup and explanation for the demo (catalog + session).
moss-live-labs/examples/travel-concierge/pyproject.toml Python deps for the agent + seeding scripts.
moss-live-labs/examples/travel-concierge/DEMO_SCRIPT.md Suggested narration/script for recording a demo.
moss-live-labs/examples/travel-concierge/data/catalog.json Destination catalog content used to seed the cloud index.
moss-live-labs/examples/travel-concierge/agent.py LiveKit agent: queries session + catalog, publishes retrieval payloads, distills user facts into session memory.
moss-live-labs/examples/travel-concierge/.python-version Pins Python version for the example.
moss-live-labs/examples/travel-concierge/.env.example Example env vars for Moss + providers + LiveKit.
Files not reviewed (1)
  • moss-live-labs/examples/travel-concierge/web/package-lock.json: Generated file

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread moss-live-labs/examples/travel-concierge/web/next.config.mjs
Comment thread moss-live-labs/examples/travel-concierge/web/app/api/token/route.ts
- next.config: use serverExternalPackages for livekit-server-sdk instead
  of a non-standard outputFileTracingRoot, matching insurance-adjuster
- token route: require LIVEKIT_* env vars and return 500 on misconfig
  instead of silently falling back to well-known dev credentials
- DualPanel: reuse a single TextDecoder across data-channel messages
@github-actions

github-actions Bot commented Jul 7, 2026

Copy link
Copy Markdown

Codex review

The PR mostly adds a self-contained travel-concierge demo, but there are a few correctness and package metadata issues worth fixing. The most important risks are inconsistent dependency resolution and brittle handling of extracted session facts.

Findings not on changed lines:

  • packages/vitepress-plugin-moss/package.json:53 CONSIDER This manifest bump does not actually line up with the package's pnpm configuration:
"vite": "^8.0.6"

pnpm-lock.yaml and the package-level pnpm.overrides.vite still force Vite 6.4.x, so npm and pnpm installs can resolve different toolchains and the Vite 8 bump is effectively ignored under pnpm. Either update/remove the override and regenerate pnpm-lock.yaml, or keep this dev dependency on the same Vite 6 range.

  • sdks/javascript/bindings/package-lock.json:10 CONSIDER The lockfile now disagrees with the adjacent package manifest:
"license": "Proprietary"

sdks/javascript/bindings/package.json still declares BSD-2-Clause, so license/SBOM tooling that reads the lockfile will misclassify the public @moss-dev/moss-core package. Regenerate the lockfile from the manifest or restore BSD-2-Clause here.

- token route: use crypto.randomUUID() for room/identity (collision-resistant).
- agent.py: await the previous turn's fact-remember task before recalling, so an
  immediate follow-up question sees the just-stored facts (no fire-and-forget race).
Copilot AI review requested due to automatic review settings July 9, 2026 20:18
@HarshaNalluru

Copy link
Copy Markdown
Contributor Author

Addressed the review: token route now uses crypto.randomUUID() for room/identity (collision-resistant), and the agent awaits the previous turn's fact-remember task before recalling so a follow-up question sees the just-stored facts.

The other three notes were already handled in the current code: the token route validates LIVEKIT_* and returns 500 (no hard-coded devkey/secret fallback), next.config already sets serverExternalPackages: ["livekit-server-sdk"] and doesn't use import.meta.dirname, and DualPanel reuses a module-level TextDecoder. Resolved those threads accordingly.

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

Copilot reviewed 21 out of 24 changed files in this pull request and generated 4 comments.

Files not reviewed (1)
  • moss-live-labs/examples/travel-concierge/web/package-lock.json: Generated file

Comment thread moss-live-labs/examples/travel-concierge/agent.py
Comment thread moss-live-labs/examples/travel-concierge/agent.py Outdated
Comment thread moss-live-labs/examples/travel-concierge/seed_index.py
Comment thread moss-live-labs/examples/travel-concierge/README.md Outdated
…seed_index), add uuid suffix to session name (no same-second collision), Node 18.18+ in README
Copilot AI review requested due to automatic review settings July 9, 2026 21:32

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

Copilot reviewed 21 out of 24 changed files in this pull request and generated 2 comments.

Files not reviewed (1)
  • moss-live-labs/examples/travel-concierge/web/package-lock.json: Generated file

Comment thread moss-live-labs/examples/travel-concierge/web/tsconfig.json
Comment thread moss-live-labs/examples/travel-concierge/DEMO_SCRIPT.md Outdated
…CRIPT quote block; fail fast if catalog index is missing
@HarshaNalluru

Copy link
Copy Markdown
Contributor Author

On injecting retrieved catalog rows + remembered facts into the model context: that's the intended RAG grounding for this demo (the agent is instructed to use it to help the traveler). Noted for anyone hardening it for production; resolving.

Copilot AI review requested due to automatic review settings July 9, 2026 21:42

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

Copilot reviewed 21 out of 24 changed files in this pull request and generated 2 comments.

Files not reviewed (1)
  • moss-live-labs/examples/travel-concierge/web/package-lock.json: Generated file

Comment thread moss-live-labs/examples/travel-concierge/web/app/page.tsx Outdated
Copilot AI review requested due to automatic review settings July 9, 2026 21:47

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

Copilot reviewed 21 out of 24 changed files in this pull request and generated 5 comments.

Files not reviewed (1)
  • moss-live-labs/examples/travel-concierge/web/package-lock.json: Generated file

Comment on lines +21 to +26
"devDependencies": {
"@types/node": "^22",
"@types/react": "^19",
"@types/react-dom": "^19",
"typescript": "^5"
}
@@ -0,0 +1,13 @@
export type Doc = {
id?: string;
Comment on lines +105 to +109
# 0. Make sure the previous turn's facts are stored before we recall,
# so an immediate follow-up question sees them.
if self._pending_remember is not None:
await self._pending_remember
self._pending_remember = None
Comment on lines +133 to +136
# 5. Distill this turn into facts and store only those in the live session, in the
# background so it never delays the reply. Awaited at the top of the next turn
# (step 0) so recall always sees it. Questions/recall add nothing.
self._pending_remember = asyncio.create_task(self._remember_facts(query))
Comment on lines +160 to +169
async def _remember_facts(self, text: str) -> None:
for fact in await self._extract_facts(text):
self.turn += 1
try:
await self.session_index.add_docs(
[DocumentInfo(id=f"fact-{self.turn}", text=fact, metadata={"role": "traveler"})]
)
logger.info(f"Remembered: {fact}")
except Exception as e:
logger.warning(f"Failed to store fact: {e}")
],
)
data = json.loads(resp.choices[0].message.content or "{}")
return [f.strip() for f in data.get("facts", []) if isinstance(f, str) and f.strip()]

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

CONSIDER This assumes the model always returns facts as a list:

return [f.strip() for f in data.get("facts", []) if isinstance(f, str) and f.strip()]

If the JSON is valid but shaped as {"facts": "Budget is $2,500"}, Python iterates the string and stores one document per character, polluting the session index. Validate the container first, for example facts = data.get("facts", []); if not isinstance(facts, list): return [], then filter string items.

# 5. Distill this turn into facts and store only those in the live session, in the
# background so it never delays the reply. Awaited at the top of the next turn
# (step 0) so recall always sees it. Questions/recall add nothing.
self._pending_remember = asyncio.create_task(self._remember_facts(query))

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

CONSIDER Remembering the user's facts is scheduled only after both Moss lookups succeed:

self._pending_remember = asyncio.create_task(self._remember_facts(query))

Because this sits inside the same try as session/catalog retrieval, any transient lookup error drops the current turn from short-term memory entirely. Start the remember task in a separate try/finally or before retrieval so catalog/search failures do not cause session-memory data loss.

@samanyugoyal2010

Copy link
Copy Markdown
Contributor

@cubic-dev-ai review this pull request

@cubic-dev-ai

cubic-dev-ai Bot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

@cubic-dev-ai review this pull request

@samanyugoyal2010 I have started the AI code review. It will take a few minutes to complete.

@cubic-dev-ai cubic-dev-ai Bot 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.

24 issues found across 24 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="moss-live-labs/examples/travel-concierge/web/components/DualPanel.tsx">

<violation number="1" location="moss-live-labs/examples/travel-concierge/web/components/DualPanel.tsx:41">
P2: A valid-JSON but malformed `moss.retrieval` packet can crash rendering because parsed data is force-cast to `RetrievalPayload` and later used with `toFixed()` without guards. Validating packet shape before `setData` would keep the panel resilient to unexpected data-channel messages.</violation>
</file>

<file name="moss-live-labs/examples/travel-concierge/README.md">

<violation number="1" location="moss-live-labs/examples/travel-concierge/README.md:21">
P2: A fresh setup installs dependencies into uv's `.venv`, but every subsequent `python ...` command uses the shell interpreter and can fail with missing imports. The examples should use `uv run python ...` throughout, or explicitly document virtual-environment activation after `uv sync`.</violation>
</file>

<file name="moss-live-labs/examples/travel-concierge/DEMO_SCRIPT.md">

<violation number="1" location="moss-live-labs/examples/travel-concierge/DEMO_SCRIPT.md:11">
P3: The opening overstates the session behavior: it does not remember everything said, only extracted durable trip preferences. Using the narrower wording would keep the demo consistent with the implementation and the later explanation.</violation>
</file>

<file name="moss-live-labs/examples/travel-concierge/web/package.json">

<violation number="1" location="moss-live-labs/examples/travel-concierge/web/package.json:9">
P2: `npm run lint` is not usable from a clean install because this package provides neither ESLint nor a configuration. Adding `eslint`/`eslint-config-next` plus an `eslint.config.mjs` and invoking `eslint .` would make the advertised check reproducible.</violation>

<violation number="2" location="moss-live-labs/examples/travel-concierge/web/package.json:22">
P2: The Node typings expose APIs newer than the documented Node 18.18 runtime, so type-checking can approve code that fails for supported users. Pinning this to the Node 18 type line keeps compile-time APIs aligned with the minimum runtime.</violation>
</file>

<file name="moss-live-labs/examples/travel-concierge/seed_index.py">

<violation number="1" location="moss-live-labs/examples/travel-concierge/seed_index.py:35">
P2: Catalog seeding can fail or corrupt destination text on systems whose locale encoding is not UTF-8. Specify `encoding="utf-8"` when reading this UTF-8 JSON file.</violation>
</file>

<file name="moss-live-labs/examples/travel-concierge/web/components/AgentSide.tsx">

<violation number="1" location="moss-live-labs/examples/travel-concierge/web/components/AgentSide.tsx:20">
P2: Agent failures are shown to the traveler as “IDLE”, hiding why the voice session is unusable. Handle LiveKit's `failed` state explicitly (and ideally `pre-connect-buffering`) instead of routing every unmapped state through the idle fallback.</violation>
</file>

<file name="moss-live-labs/examples/travel-concierge/web/app/page.tsx">

<violation number="1" location="moss-live-labs/examples/travel-concierge/web/app/page.tsx:37">
P2: The header reports “live” before LiveKit has connected, and connection failures are not surfaced through a retry state because `conn` represents token acquisition rather than room connectivity. Track `onConnected`/`onError` separately and only mark the room live after `onConnected`, clearing back to the start screen on error.</violation>
</file>

<file name="moss-live-labs/examples/travel-concierge/web/app/globals.css">

<violation number="1" location="moss-live-labs/examples/travel-concierge/web/app/globals.css:7">
P2: Placeholder, speaker, and footer text rendered with `--faint` has insufficient contrast on both card and page backgrounds. A darker neutral such as `#706e67` preserves the palette while bringing these small labels above 4.5:1.</violation>

<violation number="2" location="moss-live-labs/examples/travel-concierge/web/app/globals.css:11">
P2: User/session labels and retrieval scores rendered with `--live` are difficult to read at their 11–12px sizes because the color misses normal-text contrast on both white and tinted cards. Darkening this token avoids changing every consumer individually.</violation>
</file>

<file name="moss-live-labs/examples/travel-concierge/web/app/api/token/route.ts">

<violation number="1" location="moss-live-labs/examples/travel-concierge/web/app/api/token/route.ts:11">
P2: Any caller can hit `/api/token` and receive a fully privileged room token, so a public deployment can be abused for unauthorized room usage and resource consumption. Consider adding an app-auth or server-side shared-secret check before minting tokens.</violation>

<violation number="2" location="moss-live-labs/examples/travel-concierge/web/app/api/token/route.ts:36">
P3: The 500 response currently returns raw exception text to the browser, which can expose internal configuration/runtime details. A generic client error body with server-side logging avoids leaking internals while preserving debuggability.</violation>
</file>

<file name="moss-live-labs/examples/travel-concierge/web/components/Transcript.tsx">

<violation number="1" location="moss-live-labs/examples/travel-concierge/web/components/Transcript.tsx:38">
P3: New and updated voice turns are only exposed visually, so screen-reader users are not notified as the conversation changes. Keep a mounted transcript container across the empty/populated states and expose it as a polite `role="log"` live region.</violation>
</file>

<file name="moss-live-labs/examples/travel-concierge/agent.py">

<violation number="1" location="moss-live-labs/examples/travel-concierge/agent.py:10">
P3: The direct `AsyncOpenAI` usage is not represented in this example’s declared dependencies and currently works only through the LiveKit plugin extra. Declare `openai` explicitly so dependency updates cannot silently remove an API this module imports.</violation>

<violation number="2" location="moss-live-labs/examples/travel-concierge/agent.py:102">
P2: Non-text or empty transcription turns pass `None`/blank input into Moss and can fail the whole retrieval path. Normalize and skip blank text before querying or scheduling extraction.</violation>

<violation number="3" location="moss-live-labs/examples/travel-concierge/agent.py:103">
P2: Traveler speech and distilled preferences are persisted in normal INFO logs, exposing potentially sensitive budgets, dates, names, or destinations outside the in-memory session. Log turn metadata only, or gate/redact content behind an explicit debug option.</violation>

<violation number="4" location="moss-live-labs/examples/travel-concierge/agent.py:108">
P1: A slow or stalled fact-extraction request blocks the traveler’s next response indefinitely at this await. Bound the wait and skip/cancel memory extraction on timeout so the voice loop remains responsive.</violation>

<violation number="5" location="moss-live-labs/examples/travel-concierge/agent.py:113">
P2: Travelers with more than three stored facts lose constraints from every recall, including the four-fact utterance in the demo script. Size `top_k` to the number of stored facts, or maintain a consolidated preference document.</violation>

<violation number="6" location="moss-live-labs/examples/travel-concierge/agent.py:118">
P1: Generic follow-ups such as “So where should we go?” retrieve catalog hits without any remembered preferences, so the top three trips need not fit the traveler. Include recalled session facts when constructing the catalog query.</violation>

<violation number="7" location="moss-live-labs/examples/travel-concierge/agent.py:122">
P2: The live-session panel does not show facts from the current utterance; they appear only after a later turn triggers another query. Publish an updated session payload when `_remember_facts` finishes, or include the extracted facts in a follow-up data-channel event.</violation>

<violation number="8" location="moss-live-labs/examples/travel-concierge/agent.py:136">
P2: A lookup failure on one turn also drops that turn’s preferences from future memory because remembering is scheduled only after every retrieval step succeeds. Start `_remember_facts` independently of retrieval success, such as in a `finally` path.</violation>

<violation number="9" location="moss-live-labs/examples/travel-concierge/agent.py:155">
P2: A schema-validity deviation can turn one string-valued `facts` field into dozens of one-character session documents. Validate that `facts` is a list before iterating it, or use Structured Outputs with an array schema.</violation>

<violation number="10" location="moss-live-labs/examples/travel-concierge/agent.py:165">
P2: Corrected preferences remain alongside stale values, so later recall can return contradictory budgets, dates, or party sizes. Use stable category IDs/upserts or delete the superseded fact when a correction is extracted.</violation>
</file>

<file name="moss-live-labs/examples/travel-concierge/web/.gitignore">

<violation number="1" location="moss-live-labs/examples/travel-concierge/web/.gitignore:3">
P2: Credentials placed in other Next.js-supported environment files can still be committed because only `.env.local` is ignored. Cover all `.env*` files while explicitly retaining the example template.</violation>
</file>

Tip: instead of fixing issues one by one fix them all with cubic
Tip: cubic can generate docs of your entire codebase and keep them up to date. Try it here.

Re-trigger cubic

# 0. Make sure the previous turn's facts are stored before we recall,
# so an immediate follow-up question sees them.
if self._pending_remember is not None:
await self._pending_remember

@cubic-dev-ai cubic-dev-ai Bot Jul 17, 2026

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.

P1: A slow or stalled fact-extraction request blocks the traveler’s next response indefinitely at this await. Bound the wait and skip/cancel memory extraction on timeout so the voice loop remains responsive.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At moss-live-labs/examples/travel-concierge/agent.py, line 108:

<comment>A slow or stalled fact-extraction request blocks the traveler’s next response indefinitely at this await. Bound the wait and skip/cancel memory extraction on timeout so the voice loop remains responsive.</comment>

<file context>
@@ -0,0 +1,216 @@
+            # 0. Make sure the previous turn's facts are stored before we recall,
+            #    so an immediate follow-up question sees them.
+            if self._pending_remember is not None:
+                await self._pending_remember
+                self._pending_remember = None
+
</file context>
Fix with cubic


# 2. Look up matching trips in the pre-loaded catalog (long-term knowledge).
t = time.perf_counter()
catalog_results = await self.moss.query(CATALOG_INDEX, query, QueryOptions(top_k=3))

@cubic-dev-ai cubic-dev-ai Bot Jul 17, 2026

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.

P1: Generic follow-ups such as “So where should we go?” retrieve catalog hits without any remembered preferences, so the top three trips need not fit the traveler. Include recalled session facts when constructing the catalog query.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At moss-live-labs/examples/travel-concierge/agent.py, line 118:

<comment>Generic follow-ups such as “So where should we go?” retrieve catalog hits without any remembered preferences, so the top three trips need not fit the traveler. Include recalled session facts when constructing the catalog query.</comment>

<file context>
@@ -0,0 +1,216 @@
+
+            # 2. Look up matching trips in the pre-loaded catalog (long-term knowledge).
+            t = time.perf_counter()
+            catalog_results = await self.moss.query(CATALOG_INDEX, query, QueryOptions(top_k=3))
+            catalog_ms = (time.perf_counter() - t) * 1000.0
+
</file context>
Fix with cubic

"moss.retrieval",
useCallback((msg: { payload: Uint8Array }) => {
try {
setData(JSON.parse(decoder.decode(msg.payload)) as RetrievalPayload);

@cubic-dev-ai cubic-dev-ai Bot Jul 17, 2026

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.

P2: A valid-JSON but malformed moss.retrieval packet can crash rendering because parsed data is force-cast to RetrievalPayload and later used with toFixed() without guards. Validating packet shape before setData would keep the panel resilient to unexpected data-channel messages.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At moss-live-labs/examples/travel-concierge/web/components/DualPanel.tsx, line 41:

<comment>A valid-JSON but malformed `moss.retrieval` packet can crash rendering because parsed data is force-cast to `RetrievalPayload` and later used with `toFixed()` without guards. Validating packet shape before `setData` would keep the panel resilient to unexpected data-channel messages.</comment>

<file context>
@@ -0,0 +1,83 @@
+    "moss.retrieval",
+    useCallback((msg: { payload: Uint8Array }) => {
+      try {
+        setData(JSON.parse(decoder.decode(msg.payload)) as RetrievalPayload);
+      } catch (err) {
+        console.error("failed to parse moss.retrieval payload", err);
</file context>
Fix with cubic

@@ -0,0 +1,55 @@
# Travel Concierge — pre-loaded catalog + live session

@cubic-dev-ai cubic-dev-ai Bot Jul 17, 2026

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.

P2: A fresh setup installs dependencies into uv's .venv, but every subsequent python ... command uses the shell interpreter and can fail with missing imports. The examples should use uv run python ... throughout, or explicitly document virtual-environment activation after uv sync.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At moss-live-labs/examples/travel-concierge/README.md, line 21:

<comment>A fresh setup installs dependencies into uv's `.venv`, but every subsequent `python ...` command uses the shell interpreter and can fail with missing imports. The examples should use `uv run python ...` throughout, or explicitly document virtual-environment activation after `uv sync`.</comment>

<file context>
@@ -0,0 +1,55 @@
+
+## Setup
+```bash
+uv sync
+cp .env.example .env          # fill in Moss + provider keys
+python agent.py download-files
</file context>
Fix with cubic

"react-dom": "^19.0.0"
},
"devDependencies": {
"@types/node": "^22",

@cubic-dev-ai cubic-dev-ai Bot Jul 17, 2026

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.

P2: The Node typings expose APIs newer than the documented Node 18.18 runtime, so type-checking can approve code that fails for supported users. Pinning this to the Node 18 type line keeps compile-time APIs aligned with the minimum runtime.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At moss-live-labs/examples/travel-concierge/web/package.json, line 22:

<comment>The Node typings expose APIs newer than the documented Node 18.18 runtime, so type-checking can approve code that fails for supported users. Pinning this to the Node 18 type line keeps compile-time APIs aligned with the minimum runtime.</comment>

<file context>
@@ -0,0 +1,27 @@
+    "react-dom": "^19.0.0"
+  },
+  "devDependencies": {
+    "@types/node": "^22",
+    "@types/react": "^19",
+    "@types/react-dom": "^19",
</file context>
Fix with cubic

@@ -0,0 +1,4 @@
node_modules
.next
.env.local

@cubic-dev-ai cubic-dev-ai Bot Jul 17, 2026

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.

P2: Credentials placed in other Next.js-supported environment files can still be committed because only .env.local is ignored. Cover all .env* files while explicitly retaining the example template.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At moss-live-labs/examples/travel-concierge/web/.gitignore, line 3:

<comment>Credentials placed in other Next.js-supported environment files can still be committed because only `.env.local` is ignored. Cover all `.env*` files while explicitly retaining the example template.</comment>

<file context>
@@ -0,0 +1,4 @@
+node_modules
+.next
+.env.local
+next-env.d.ts
</file context>
Suggested change
.env.local
.env*
!.env.local.example
Fix with cubic


## 1 · Frame it (0:00–0:12)
> "This concierge knows a catalog of trips — that's loaded ahead of time. But it also
> remembers everything I say on the call. Two Moss indexes, live, side by side. Watch."

@cubic-dev-ai cubic-dev-ai Bot Jul 17, 2026

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.

P3: The opening overstates the session behavior: it does not remember everything said, only extracted durable trip preferences. Using the narrower wording would keep the demo consistent with the implementation and the later explanation.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At moss-live-labs/examples/travel-concierge/DEMO_SCRIPT.md, line 11:

<comment>The opening overstates the session behavior: it does not remember everything said, only extracted durable trip preferences. Using the narrower wording would keep the demo consistent with the implementation and the later explanation.</comment>

<file context>
@@ -0,0 +1,55 @@
+
+## 1 · Frame it (0:00–0:12)
+> "This concierge knows a catalog of trips — that's loaded ahead of time. But it also
+> remembers everything I say on the call. Two Moss indexes, live, side by side. Watch."
+
+**[Click Start planning. The agent greets you.]**
</file context>
Fix with cubic

{ headers: { "Cache-Control": "no-store" } },
);
} catch (error) {
const msg = error instanceof Error ? error.message : "Unknown error";

@cubic-dev-ai cubic-dev-ai Bot Jul 17, 2026

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.

P3: The 500 response currently returns raw exception text to the browser, which can expose internal configuration/runtime details. A generic client error body with server-side logging avoids leaking internals while preserving debuggability.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At moss-live-labs/examples/travel-concierge/web/app/api/token/route.ts, line 36:

<comment>The 500 response currently returns raw exception text to the browser, which can expose internal configuration/runtime details. A generic client error body with server-side logging avoids leaking internals while preserving debuggability.</comment>

<file context>
@@ -0,0 +1,39 @@
+      { headers: { "Cache-Control": "no-store" } },
+    );
+  } catch (error) {
+    const msg = error instanceof Error ? error.message : "Unknown error";
+    return new NextResponse(msg, { status: 500 });
+  }
</file context>
Fix with cubic

}

return (
<div className="transcript">

@cubic-dev-ai cubic-dev-ai Bot Jul 17, 2026

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.

P3: New and updated voice turns are only exposed visually, so screen-reader users are not notified as the conversation changes. Keep a mounted transcript container across the empty/populated states and expose it as a polite role="log" live region.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At moss-live-labs/examples/travel-concierge/web/components/Transcript.tsx, line 38:

<comment>New and updated voice turns are only exposed visually, so screen-reader users are not notified as the conversation changes. Keep a mounted transcript container across the empty/populated states and expose it as a polite `role="log"` live region.</comment>

<file context>
@@ -0,0 +1,47 @@
+  }
+
+  return (
+    <div className="transcript">
+      {ordered.map((turn) => (
+        <div className={`turn ${turn.isUser ? "user" : "agent"}`} key={turn.id}>
</file context>
Fix with cubic

from datetime import datetime

from dotenv import load_dotenv
from openai import AsyncOpenAI

@cubic-dev-ai cubic-dev-ai Bot Jul 17, 2026

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.

P3: The direct AsyncOpenAI usage is not represented in this example’s declared dependencies and currently works only through the LiveKit plugin extra. Declare openai explicitly so dependency updates cannot silently remove an API this module imports.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At moss-live-labs/examples/travel-concierge/agent.py, line 10:

<comment>The direct `AsyncOpenAI` usage is not represented in this example’s declared dependencies and currently works only through the LiveKit plugin extra. Declare `openai` explicitly so dependency updates cannot silently remove an API this module imports.</comment>

<file context>
@@ -0,0 +1,216 @@
+from datetime import datetime
+
+from dotenv import load_dotenv
+from openai import AsyncOpenAI
+from livekit import rtc
+from livekit.plugins import openai, deepgram, silero, cartesia
</file context>
Fix with cubic

@samanyugoyal2010

Copy link
Copy Markdown
Contributor

Superseded by #408 — same examples/add-travel-concierge history plus review hardening (agent memory path, catalog query with recalled prefs, web resilience, docs/uv run).

Please close this PR in favor of #408.

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.

4 participants