fix(run_agent): skip OpenAI client rebuild for anthropic_messages api_mode - #36719
fix(run_agent): skip OpenAI client rebuild for anthropic_messages api_mode#36719zhiyanliu wants to merge 1 commit into
Conversation
…_mode
When provider=bedrock + a Claude model (api_mode=anthropic_messages),
the stale-stream watchdog and other rebuild triggers
(*_credential_refresh, credential_rotation, dead_connection_cleanup,
fallback_timeout_apply, recreate_closed:*) call
_replace_primary_openai_client(), which falls through to OpenAI(**{})
and emits a misleading "OPENAI_API_KEY missing" warning.
On this api_mode there is no shared OpenAI client to rebuild —
agent.client is intentionally None and real requests flow through
agent._anthropic_client, which rotates through its own path. Short-
circuit at the rebuild entrypoint with an info-level log so the no-op
is observable, leaving the lower-level create_openai_client factory
contract ("return a non-None client") intact.
Fixes NousResearch#36693
|
For reviewer context — there is now a parallel PR #36714 that fixes the same issue at a single call-site (the stale-stream watchdog in This PR's no-op short-circuit covers all 8 rebuild call-sites in one place but does not rebuild the Anthropic client on connection-pool-cleanup reasons. For Bedrock + Claude that's probably fine in practice (the next request via If you'd prefer a strict superset of both directions, I'm happy to update this PR to:
Just let me know which shape you'd like and I'll push the update. Either way, happy to close this in favor of #36714 if you'd rather keep the per-call-site pattern. |
mxnstrexgl
left a comment
There was a problem hiding this comment.
🤖 Automated PR Review
Security Scan
- ✓ No hardcoded secrets, injection sinks, unsafe deserialization, or dependency red flags found by this automated scan.
Code Quality
- ✓ No blocking code-quality issues found by this automated scan.
Summary
Status: APPROVE — security findings: 0, quality suggestions: 0.
Automated review; raw diff content intentionally omitted.
|
Closed in favor of #53926, now merged to main (commit a0b9663). This was a popular bug — 12 independent PRs fixed the same issue (#28161): the three stream-cleanup paths in the streaming code rebuilt the OpenAI primary client unconditionally, which on Anthropic-native sessions both failed (no OPENAI_API_KEY) and left the wedged stream open, causing the ~15-minute hang. We salvaged @EloquentBrush0x's #28240 (the earliest dedicated fix for this issue) onto current main, resolved the conflict against the newer request-client cleanup helper, repointed the bug-encoding regression test, and added coverage for the two reachable Anthropic cleanup sites. Verified live: stale stream torn down in <1s instead of ~900s, Anthropic client closed+rebuilt, OpenAI rebuild never called on the Anthropic path. Thank you for the fix — closing as a duplicate of the merged work. Credit to everyone who reported and fixed this. |
Fixes #36693.
What
Short-circuits
_replace_primary_openai_clientto a no-op success(returns
True, logs atinfo) whenagent.api_mode == "anthropic_messages". Covers all rebuild call-sites in one place(
stale_stream_pool_cleanup,<provider>_credential_refresh,nous_credential_refresh,copilot_credential_refresh,credential_rotation,dead_connection_cleanup,fallback_timeout_apply,recreate_closed:*).Why
On
provider=bedrock+ Claude,agent.clientis intentionallyNone— real requests flow through
agent._anthropic_client. The rebuildattempt is dead-code on this path: it constructs
OpenAI(**{}), whichfails with the misleading
The conversation isn't actually broken (the next API request succeeds),
but the warning is confusing for a Bedrock setup. Issue #36693 has the
full trace.
Design choices
_replace_primary_openai_client, notcreate_openai_client. The factory's existing per-provider earlybranches (
copilot-acp,google-gemini-cli,gemini) all returnadapter clients;
anthropic_messageshas no client to return, so thesemantically clean place to short-circuit is one layer up where the
caller decides whether a rebuild is needed at all. This also leaves
the factory's contract ("return a non-None client") intact.
info, not silent."Rebuild skipped for anthropic_messages api_mode (%s) %s"keeps the no-op observable inagent.log without raising the warning bar.
Tests
tests/run_agent/test_replace_primary_openai_client_anthropic_messages.py(3 tests, all passing locally):
test_replace_primary_openai_client_short_circuits_for_anthropic_messages— verifies
stale_stream_pool_cleanupreturnsTruewithout invokingthe factory and
agent.clientstaysNone.test_replace_primary_openai_client_short_circuits_for_all_rebuild_reasons— loops over all 8 rebuild reasons in the codebase and asserts each
short-circuits.
test_replace_primary_openai_client_still_rebuilds_for_openai_api_mode— sanity check that
chat_completionsapi_mode still goes throughthe factory.
Happy to revisit the design choices in review.