Summary
At e1d4cc9 (v0.2.1), none of the providers let the caller set a request timeout, so every request uses the SDK default: up to 10 minutes for OpenAI and Anthropic, and no timeout at all for Gemini (its client defaults to httpx.Timeout(None)).
This means RetryPolicy(api_timeout_error=True), which the adapter accepts as a drop-in for TypeSafeClient, can't realistically trigger, and a stalled request hangs the caller instead of failing fast so it can fall back.
Reproduction
A local server that accepts connections but never replies, with RetryPolicy(max_retries=1):
import socket, threading, time
from typesafe_sdk import RetryPolicy
from system_one_adapter import Noul, SystemOneAdapterClient
from system_one_adapter.providers.openai import OpenAIProvider
server = socket.socket()
server.bind(("127.0.0.1", 0))
server.listen()
held = []
threading.Thread(target=lambda: [held.append(server.accept()) for _ in iter(int, 1)], daemon=True).start()
provider = OpenAIProvider("gpt-4o-mini", base_url=f"http://127.0.0.1:{server.getsockname()[1]}/v1", api_key="test")
client = SystemOneAdapterClient(structured_outputs=True, llm_answer_mode="discrete", retry=RetryPolicy(max_retries=1))
start = time.monotonic()
try:
client.system_one(state="hi", questions={"q": Noul(instructions="Is it a greeting?")}, model=provider)
except Exception as error:
print(f"{type(error).__name__} after {time.monotonic() - start:.2f}s")
- Expected: a way to bound each request, failing with
TypeSafeAPITimeoutError.
- Actual on
main: no output; I killed it after 30 s. Each attempt waits up to 10 minutes.
- With the proposed fix (
OpenAIProvider(..., timeout=0.5)): TypeSafeAPITimeoutError after 1.5s (two attempts plus backoff).
Proposed fix
An optional timeout= (seconds) on all six providers, following the existing max_tokens= option on AnthropicProvider:
client.system_one(state, questions, model=OpenAIProvider("gpt-4o-mini", timeout=2.0))
- Omitted, it keeps today's behavior.
None is never passed through, since the SDKs read it as "never time out".
- Zero or negative values raise
ValueError, like max_tokens.
- Gemini receives it as
sdk_configuration.timeout_ms.
- README section (including that the timeout is per request, not per
system_one call) and an Unreleased changelog entry.
The fix is one commit on my fork:
Happy to adjust the API (for example, also accepting an httpx.Timeout) or for a maintainer to take it from there.
Test plan
uv run pytest tests/test_provider_timeouts.py: new tests cover all six providers. The ones checking that the timeout reaches the SDK fail on main and pass with the fix.
uv run pytest: 445 passed.
uv run pyrefly check: 0 errors.
- The reproduction above, run on
main and on the branch.
Summary
At
e1d4cc9(v0.2.1), none of the providers let the caller set a request timeout, so every request uses the SDK default: up to 10 minutes for OpenAI and Anthropic, and no timeout at all for Gemini (its client defaults tohttpx.Timeout(None)).This means
RetryPolicy(api_timeout_error=True), which the adapter accepts as a drop-in forTypeSafeClient, can't realistically trigger, and a stalled request hangs the caller instead of failing fast so it can fall back.Reproduction
A local server that accepts connections but never replies, with
RetryPolicy(max_retries=1):TypeSafeAPITimeoutError.main: no output; I killed it after 30 s. Each attempt waits up to 10 minutes.OpenAIProvider(..., timeout=0.5)):TypeSafeAPITimeoutError after 1.5s(two attempts plus backoff).Proposed fix
An optional
timeout=(seconds) on all six providers, following the existingmax_tokens=option onAnthropicProvider:Noneis never passed through, since the SDKs read it as "never time out".ValueError, likemax_tokens.sdk_configuration.timeout_ms.system_onecall) and anUnreleasedchangelog entry.The fix is one commit on my fork:
curl -L https://github.com/jessicafalcon/system-one-adapter-python/commit/5dbe2075b54a24d3405df2d435b9ad9e9032b58b.patch | git amHappy to adjust the API (for example, also accepting an
httpx.Timeout) or for a maintainer to take it from there.Test plan
uv run pytest tests/test_provider_timeouts.py: new tests cover all six providers. The ones checking that the timeout reaches the SDK fail onmainand pass with the fix.uv run pytest: 445 passed.uv run pyrefly check: 0 errors.mainand on the branch.