Skip to content

Execute control-flow tool actions, and take the cold start off the first turn - #10

Merged
My-CMDhub merged 2 commits into
bugsmash/3-turn-loopfrom
bugsmash/4-control-flow-coldstart
Oct 7, 2026
Merged

My-CMDhub merged 2 commits into
bugsmash/3-turn-loopfrom
bugsmash/4-control-flow-coldstart

Conversation

@My-CMDhub

Copy link
Copy Markdown
Owner

The dispatcher answers hang_up_call and transfer_to_staff with an
action field, but the cascaded tool loop only serialized the result
back to the LLM and never read it. The model therefore narrated both
actions while nothing performed them: it said goodbye and left the line
open, and promised a transfer that never happened.

Verified live — hang_up_call fired 3 times on one call (11:13:00,
11:15:33, 11:15:41 AEST) and the leg only ended when the caller hung up
at 566s.

  • read action after dispatcher.execute
  • terminate via Twilio REST Status=completed, idempotent (the model
    retries a tool that produces no observable effect)
  • transfer via a TwiML update with a redirect back to the AI on
    no-answer
  • both fire after the turn's audio has drained, so the farewell and the
    handoff line are heard first; a barge-in during the farewell aborts
    the hangup

Second commit — perf(voice): remove first-call cold start from the voice hot path:

First call after a dyno boot reached "Call context ready" 4,316ms after the
Twilio stream started; the second call on the same dyno took 2ms. Whoever
calls first pays the difference, and voice_settings (voice, TTS model, Flux
turn thresholds) are not applied until it clears, so the opening turn also
ran on Deepgram's defaults rather than the tenant's configuration.

Two causes, both removed:

  • Tenant rows are keyed by an Appwrite document id, not by their slug, so
    the primary GET .../documents/coalcreek always 404'd before the slug
    query found the row — ~920ms of pure waste per cache miss, logged as a
    success. Slug query is now primary; document-id lookup stays as fallback.
  • openai and the dispatcher chain were imported inside
    _build_call_context(), so the first call paid a blocking cold import on
    the event loop. Startup now warms those imports and pre-fetches the tenant
    config into its 5-minute cache.

Warm-up failures are logged and swallowed — a cold first call is slow, not
broken.

The dispatcher answers hang_up_call and transfer_to_staff with an
`action` field, but the cascaded tool loop only serialized the result
back to the LLM and never read it. The model therefore *narrated* both
actions while nothing performed them: it said goodbye and left the line
open, and promised a transfer that never happened.

Verified live — hang_up_call fired 3 times on one call (11:13:00,
11:15:33, 11:15:41 AEST) and the leg only ended when the caller hung up
at 566s.

- read `action` after dispatcher.execute
- terminate via Twilio REST Status=completed, idempotent (the model
  retries a tool that produces no observable effect)
- transfer via a TwiML <Dial> update with a redirect back to the AI on
  no-answer
- both fire after the turn's audio has drained, so the farewell and the
  handoff line are heard first; a barge-in during the farewell aborts
  the hangup
First call after a dyno boot reached "Call context ready" 4,316ms after the
Twilio stream started; the second call on the same dyno took 2ms. Whoever
calls first pays the difference, and voice_settings (voice, TTS model, Flux
turn thresholds) are not applied until it clears, so the opening turn also
ran on Deepgram's defaults rather than the tenant's configuration.

Two causes, both removed:

- Tenant rows are keyed by an Appwrite document id, not by their slug, so
  the primary `GET .../documents/coalcreek` always 404'd before the slug
  query found the row — ~920ms of pure waste per cache miss, logged as a
  success. Slug query is now primary; document-id lookup stays as fallback.
- openai and the dispatcher chain were imported inside
  `_build_call_context()`, so the first call paid a blocking cold import on
  the event loop. Startup now warms those imports and pre-fetches the tenant
  config into its 5-minute cache.

Warm-up failures are logged and swallowed — a cold first call is slow, not
broken.
@vercel

vercel Bot commented Aug 19, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
ovela-ai Ready Ready Preview Aug 19, 2026 10:50am

@My-CMDhub My-CMDhub closed this Sep 18, 2026
Repository owner locked and limited conversation to collaborators Sep 18, 2026
@My-CMDhub My-CMDhub reopened this Sep 18, 2026
@My-CMDhub
My-CMDhub merged commit 1068c8a into bugsmash/3-turn-loop Oct 7, 2026
2 checks passed

This branch was successfully deployed

1 active deployment
Preview — 64290bb3 Deployed Aug 19, 2026 by vercel[bot]
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant