3.8 KiB
3.8 KiB
Debug Session: mcp-audio-stutter
- Status: [OPEN]
- Issue: Piccole interruzioni audio quando una risposta MCP lunga viene eseguita o reiniettata nella conversazione live.
Reproduction Steps
- Avvia la UI realtime.
- Esegui una richiesta che attiva un tool MCP con output grande.
- Ascolta la risposta audio dell'assistente.
- Verifica se compaiono micro-interruzioni durante o subito dopo l'iniezione del risultato MCP.
Hypotheses & Verification
| ID | Hypothesis | Likelihood | Effort | Evidence |
|---|---|---|---|---|
| A | Inviare al modello il payload MCP quasi completo degrada la fluidità della risposta audio anche senza bloccare il main thread. | High | Low | Confirmed |
| B | Esiste una race: il fallback MCP decide il turno prima che response.created arrivi, quindi la prima response speculativa parte comunque e viene tagliata. |
High | Medium | Confirmed |
| C | La chat/UI fa render pesanti quando arriva il risultato MCP grande. | Medium | Medium | Rejected |
| D | I log/debug del payload MCP grande aggravano il problema sul main thread. | Medium | Low | Rejected |
| E | L'attacco audio di ogni frase soffre di jitter iniziale perché il playback parte senza prebuffer sul primo chunk disponibile. | High | Medium | Confirmed |
Log Evidence
execRemoteMcpToolriceve un result JSON di5738byte e produce un output testo di3262caratteri senza tempi anomali.built live prompt from MCP outputmostrapromptLength~3400ebuildMs: 0.rendered tool result rowerendered MCP tool result in chatmostranorenderMs: 0-1.sending conversation.item.create payloadmostrapayloadLength~3790estringifyMs: 0.- Quindi serializzazione, render UI e stringify non spiegano i micro-tagli.
- Nel run post-fix
sendUserTextmostraresponseActive: false,openResponses: 0,createInFlight: false: non è un classico barge-in su response già attiva. - Il sintomo residuo si manifesta solo sulla prima frase speculativa; questo è coerente con una race prima di
response.created. - Il browser può decidere il fallback MCP prima che il client abbia un
responseIdda silenziare: per questo ora viene marcata anche la next response come sopprimibile. - L'ultimo sintomo riferito dall'utente cambia ancora: le micro-interruzioni si sentono sulle prime parole di ogni frase, non solo su quella speculativa.
- Questo è coerente con il worklet
audio-playbackche parte subito sul primo chunk ricevuto, senza accumulare buffer iniziale.
Minimal Fix
- Accodata la
sendUserText()del payload MCP quando esiste una response attiva. - Flush del testo differito solo dopo
response.done, prima delresponse.creategià in coda. - Riassunto/troncamento del payload MCP prima di reiniettarlo nella conversazione live.
- Ridotto il delay del fallback MCP a
150msper match forti e senza argomenti obbligatori. - Soppressione della next speculative response se il fallback MCP vince il turno prima che
response.createdsia arrivato. - Introdotto un piccolo prebuffer di avvio (
90ms) nel workletaudio-playbackprima di iniziare ogni frase. - Obiettivo: evitare sia il barge-in quando presente, sia il carico eccessivo sul modello con JSON troppo grande.
- Obiettivo aggiuntivo: ridurre la finestra in cui il modello può iniziare una frase speculativa prima che parta il tool MCP.
- Obiettivo finale: evitare il clipping/jitter sulle prime parole di ogni frase.
Verification Conclusion
- Pre-fix: il browser non mostrava alcun collo di bottiglia locale; il prompt inviato al modello restava però molto grande.
- Post-fix: il testo MCP viene accodato se c'è una risposta attiva, il modello riceve solo un summary compatto, il fallback parte quasi subito, la next response speculativa viene silenziata se necessario e il playback aspetta un buffer iniziale prima di iniziare ogni frase.