Files
vocero-client/docs/client/troubleshooting.md
T
alvaro 4b0108ecf0 feat(vocero): add linked content and TALKFIRST greetings (#2)
Opening a report or podcast link now establishes an authenticated starting reference for the conversation. Users can select other accessible content or compare reports while retaining the original link, and TALKFIRST greetings can name the verified report or podcast.

Release notes:

- Added authenticated report and episode link resolution with bounded lookups and unavailable-content handling
- Added explicit conversation selection without restricting searches or changing the reference during comparisons
- Added contextual TALKFIRST greetings through both voice-proxy and HTTP reasoning paths
- Preserved backend permission checks, conversation isolation, cancellation, and the existing speech protocol

Reviewed-on: AquaPortal/s2s#2
2026-10-08 09:34:16 -04:00

7.2 KiB

Troubleshooting

Diagnose configuration, protocol events, execution and playback separately. Use architecture, tool calling and context injection to identify which path is active.

Check configuration and connection first

Inspect the server configuration without printing secret environment files:

curl -fsS http://localhost:8420/api/config | jq

Confirm s2sUrl, lb, reasoningType, injectProfile, apipilot, theme and startSessionMode. A direct WebSocket URL bypasses the local relay's server-side context injection and marker execution. For a local proxy, confirm the server can resolve and reach S2S_PROXY_TARGET.

In browser DevTools, inspect the WebSocket handshake and session.created, session.update and error events. Check that instructions and native tools are inside session. A cached browser module can disagree with current server code; inspect served asset versions and reload with cache disabled when needed.

Launcher and hot reload

start_ui.sh serves Vite on 8420 and FastAPI on loopback port 8421 by default. Check both processes in the launcher/PM2 logs. If either exits, the launcher stops the other and returns a failure. Port conflicts are errors; use distinct UI_PORT and S2S_BACKEND_PORT values for additional worktrees.

A Vite "Blocked request" response means the public hostname is missing from S2S_DEV_ALLOWED_HOSTS. Configure it without a scheme or port and restart the launcher. If the page loads but edits do not appear, check the browser's /__vite_hmr WebSocket and the reverse proxy's upgrade handling. Speech uses the separate /v1/realtime WebSocket on the same public origin.

For a startup dependency error, check uv 0.12.5 and Node/npm on the process manager's PATH, plus setsid/curl. uv manages Python 3.12 in vocero-api/.venv and checks the committed lock on every start. Frontend dependencies are reused until their manifests change. Changes to root .env or launcher settings require a restart.

The assistant promises a tool but nothing happens

Follow the actual execution mechanism:

  1. For a native tool, confirm it appears in the outbound session.tools array.
  2. Find response.function_call_arguments.done with a tool name, call_id, and JSON-string arguments.
  3. Confirm the browser dispatched toolcall and the executor sent its request.
  4. Confirm function_call_output and any required response.create were sent.
  5. For ApiPilot, inspect discovery, marker detection, authorization/schema validation, execution, and second-pass result injection instead.

A spoken sentence such as “Voy a buscar el reporte” does not satisfy step 2. If the function-call event is absent, inspect model/backend tool generation before debugging a browser executor that has not been invoked.

Earlier local debug notes recorded a reproduction where tools and required tool policy were sent but the model produced only spoken text. Those notes refer to the retired MCP ranking path; they do not establish a current ApiPilot failure. Both local investigation notes remain marked OPEN. Their useful lesson is to collect evidence at each boundary and reproduce against current code before claiming a root cause is resolved.

The assistant misses caller or report context

With MCP enabled, check that /api/conversations receives report_id or episode_id from the page and completes before voice startup. Report IDs retain their full prefix: report-example selects podcast report:report-example. Link resolution uses the same user's backend session and has a ten-second budget. An inaccessible or unavailable link produces a generic conversation, not a fallback to the latest report. Login or catalog failures still block startup. A report may exist without a podcast. To change the conversational subject, the agent uses select_content; search and comparison calls do not change it. MCP ignores the legacy injection flags below.

  • Verify the page has an encoded phoneNumber or the intended episode_id.
  • Inspect /api/user/phone and context-fetch logs for HTTP/JSON failures.
  • Confirm profile injection is enabled; a visible profile alone does not prove realtime instructions contain it.
  • Reports need S2S_INJECT_REPORTS=true; minimal sends only labels and dates. A historical script will not be in the prompt in that mode.
  • Static summary text overrides the HTTP summary endpoint.
  • Inspect the final session.update rather than only its browser base prompt. The proxy can append and deduplicate context after the browser sends it.

S2S_DEBUG_PROMPT=true enables complete prompt logs, including caller information and conversation text. Use summaries when full payloads are unnecessary. The browser's application functions are module-scoped; do not assume names such as buildInstructions() are available directly on window in DevTools.

Short gaps or clipped words around tool results

The retained audio investigation found very low local serialization/render times for one large tool result, while still observing audio interruptions. That evidence ruled out a UI-render bottleneck for that reproduction; it does not rule out rendering costs for all future payloads.

Useful checks are:

  • Measure tool-result size, prompt length, serialization time and UI render time.
  • Inspect whether result injection overlaps an active or pending response.
  • Check whether a speculative response starts before its response.created reaches the browser. Cancellation based only on an existing response ID can miss that window.
  • Inspect deferred text/response queues and whether they flush after response.done rather than interrupting the active turn.
  • Inspect playback prebuffering and queue underflow, especially when the first words of every sentence are affected.
  • Confirm oversized tool results are summarized or capped on the relevant path rather than assuming every tool payload gets the same treatment.

The historical investigation introduced a 90ms playback prebuffer and reduced MCP prompt reinjection. The old 150ms MCP fallback timer is historical, not a current ApiPilot configuration knob. Verify the live worklet and current relay before changing audio timing based on those observations.

External reasoning and startup

/api/reasoning returning 409 is expected in PROXY mode. In OPENAI, check the configured endpoint/model/key and provider logs; HTTP reasoning does not use the native session's tool array.

TALK/TALKFIRST attempt startup as soon as configuration and caller context are ready. Browser microphone/audio policies can still require a gesture. If audio is blocked, tap the main microphone button when the inline hint appears. If microphone access fails, allow it in the browser's site permissions and use the same button to retry. No startup dialog is shown.

Local validation

From the repository root, run the frontend checks and local backend fixtures:

npm --prefix vocero-frontend run check
npm --prefix vocero-frontend run build
make -C vocero-api check

These checks cover frontend contracts and local session/queue/WebSocket fixtures. They do not validate deployed services, real microphone hardware, model output, or audio quality. Use a local browser/backend reproduction for those paths before deployment.