Skip to content

Providers can't set a request timeout, so api_timeout_error retries almost never trigger #48

Description

@jessicafalcon

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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions