Skip to content

Decisions never queue behind other conversations; voice=True waits 1.5 s - #17

Merged
kbhatnagar1506 merged 2 commits into
mainfrom
decide-pool
Oct 9, 2026
Merged

kbhatnagar1506 merged 2 commits into
mainfrom
decide-pool

Conversation

@kbhatnagar1506

Copy link
Copy Markdown
Owner
  • fix: every sync decide call in a process ran on one shared pool of 8 threads, and each call's 5 s timer started when it was queued. With many conversations in one process, calls timed out waiting in the queue (some before they were sent) and failed open. The pool now grows with the calls in flight (up to 256), and a call's full timeout runs from when it is sent; waiting for a thread is also bounded by it. In a replay of a real prod run (1,348 server latencies, 32 conversations), fail-opens dropped from 13 to 4, and those 4 are exactly the requests the server itself took over 5 s on.
  • feat: wrap(..., voice=True) sets the default decision timeout to 1.5 s (chat stays 5 s); an explicit timeout still wins. In a 1,156-turn voice benchmark, decisions take 0.6 s at the median and 1.5 s for the slowest 10%.

Tests: tests/test_decide_pool.py (4 tests; 2 fail on the old code) and a parametrized voice-timeout test in tests/test_wrap.py. Ruff and mypy are clean. Locally, 3 async tests can't run without pytest-asyncio; CI installs the dev extras.

🤖 Generated with Claude Code

https://claude.ai/code/session_018ruNgP5DUwEkpSrXnv5R8T


Generated by Claude Code

claude added 2 commits October 9, 2026 05:52
…time runs from its send

Sync decision calls shared one process-wide pool of 8 threads, and each call's timeout ran
from when it was queued. An agent serving many conversations from one process (32 in our
benchmark harness) queued its decisions behind each other whenever the service slowed down:
the queued calls ran out of time before they were sent and failed open (30 of 1352 calls in
one run, most of them answered quickly once they reached the service).

The pool now grows with the calls in flight (up to MAX_DECIDE_THREADS = 256; a thread is
started only when none is idle), and a call has its full timeout from when it is sent, the
budget the deadline header tells the service. Waiting for a free thread, only past 256 at
once, is bounded by the timeout too.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018ruNgP5DUwEkpSrXnv5R8T
A voice caller hears every second of waiting. wrap(..., voice=True) sets the default
decision timeout to 1.5 s (chat stays 5 s); an explicit timeout still wins. Decisions take
0.6 s at the median and 1.5 s at p90 in a 1,156-turn voice benchmark.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018ruNgP5DUwEkpSrXnv5R8T
@kbhatnagar1506
kbhatnagar1506 merged commit dbaaf04 into main Oct 9, 2026
5 checks passed
@kbhatnagar1506
kbhatnagar1506 deleted the decide-pool branch October 9, 2026 16:31
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.

2 participants