fix(providers): aux pool fallback, tool-use 404 classification, poolside wire types, codex text recovery (4-PR salvage) - #58502
Merged
Conversation
teknium1
force-pushed
the
salvage/provider-error-resilience
branch
from
July 4, 2026 22:28
bbc4881 to
af903f9
Compare
Contributor
|
Thanks for salvaging this 🙌 — appreciate you keeping authorship on #58451. This salvage pack is cleaner than my standalone PR since it also fixes the Anthropic pool fallback, Poolside wire types, and Codex text recovery in one pass. LGTM — makes more sense to merge one consolidated PR than four individual ones for related provider-boundary issues. |
…o usable entry
_try_anthropic() hard-failed (return None, None) when the anthropic
credential pool was present but had no selectable entry — e.g. the pooled
OAuth token expired and its refresh_token had gone stale, so
_select_pool_entry("anthropic") returned (True, None). This wedged every
auxiliary task routed to Anthropic (goal judge surfaced "no auxiliary
client configured") even when a perfectly valid ANTHROPIC_TOKEN /
credentials-file token was available. The main session stayed healthy
because it resolves the env token directly.
The openrouter path (_try_openrouter) and codex path already fall through
to their standalone credential on (True, None); anthropic was the only
provider that hard-failed. Make _try_anthropic fall through to
resolve_anthropic_token() on that branch so the three paths are symmetric:
a temporarily dead pool entry must not block auxiliary tasks when a valid
standalone credential exists.
Adds a regression test covering: (1) pool present + no entry + valid env
token -> client built from the env token, (2) pool present + no entry + no
resolvable token -> clean (None, None), (3) base_url defaults correctly
when falling through with pool_present=True.
…llback When OpenRouter routes to an endpoint that does not support tool/function calling, it returns HTTP 404 with the message 'No endpoints found that support tool use. Try disabling "browser_back".' The raw error body does not contain 'model not found' or any other _MODEL_NOT_FOUND_PATTERNS entry, so it falls through to FailoverReason.unknown with retryable=True. The retry loop wastes 3-5 attempts on the same deterministic rejection, then surfaces a confusing generic error instead of automatically failing over to a fallback model or provider. Adding the OpenRouter phrase to _MODEL_NOT_FOUND_PATTERNS classifies it as model_not_found (retryable=False, should_fallback=True), which triggers the client-error fast-fallback path in conversation_loop.py: the agent switches to a configured fallback model/provider before the user sees the error. Existing buffered guidance in conversation_loop.py (the 'support tool use' hint at line ~2967) remains intact and surfaces only if every fallback exhausts.
- ChatCompletionsTransport.normalize_response: convert integer finish_reason (e.g. 24) to string for Poolside compatibility - Chat completion helpers: handle integer tool_call.id during streaming by converting to string - Add Poolside as first-class CANONICAL_PROVIDERS entry (visible in CLI/TUI/desktop provider pickers)
teknium1
force-pushed
the
salvage/provider-error-resilience
branch
from
July 4, 2026 22:40
af903f9 to
08f4f5b
Compare
teknium1
enabled auto-merge (rebase)
July 4, 2026 22:40
13 tasks
webdevtodayjason
added a commit
to webdevtodayjason/hermes-agent
that referenced
this pull request
Aug 13, 2026
…n error quirks
Adds a plugin seam at the top of agent/error_classifier.classify_api_error()
(step 0, before the built-in pipeline) so model-provider plugins can classify
their provider's error quirks without patching core:
- New "classify_api_error" entry in VALID_HOOKS. Callbacks receive the parsed
error context (provider, model, status_code, error_type, error_code,
error_message, error_body, error, approx_tokens, context_length,
num_messages), self-scope on `provider`, and return None to pass or a dict
{"reason": "<FailoverReason name>", ...optional recovery-hint overrides}.
- get_plugin_error_classification() helper mirrors
get_pre_tool_call_block_message(): first valid result wins, invalid dicts
and unknown reasons are skipped, callback exceptions are isolated — a
broken plugin can never break classification. Zero behavior change when no
plugin claims the error (all 179 existing classifier tests pass untouched).
- Bundled reference plugin `openrouter-tool-use-404` (opt-in, like all
bundled standalone plugins) re-implements PR NousResearch#58451: OpenRouter's
"No endpoints found that support tool use" 404 carries no
_MODEL_NOT_FOUND_PATTERNS signal, so it classifies as unknown/retryable
and the retry loop burns 3-5 attempts on a deterministic rejection.
The plugin classifies it as model_not_found (retryable=False,
should_fallback=True) so the fast-fallback path fires immediately —
demonstrating a waiting core PR converted to a publishable plugin.
Motivation: ~10 open PRs are single-provider error-classification patches
(NousResearch#58451, NousResearch#58355, NousResearch#58502, NousResearch#58474, NousResearch#58366, ...). This hook turns that whole
class of contribution into plugin territory.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FWMcB7RPSYUpsXDfBgwjzM
teknium1
pushed a commit
that referenced
this pull request
Aug 13, 2026
…n error quirks
Adds a plugin seam at the top of agent/error_classifier.classify_api_error()
(step 0, before the built-in pipeline) so model-provider plugins can classify
their provider's error quirks without patching core:
- New "classify_api_error" entry in VALID_HOOKS. Callbacks receive the parsed
error context (provider, model, status_code, error_type, error_code,
error_message, error_body, error, approx_tokens, context_length,
num_messages), self-scope on `provider`, and return None to pass or a dict
{"reason": "<FailoverReason name>", ...optional recovery-hint overrides}.
- get_plugin_error_classification() helper mirrors
get_pre_tool_call_block_message(): first valid result wins, invalid dicts
and unknown reasons are skipped, callback exceptions are isolated — a
broken plugin can never break classification. Zero behavior change when no
plugin claims the error (all 179 existing classifier tests pass untouched).
- Bundled reference plugin `openrouter-tool-use-404` (opt-in, like all
bundled standalone plugins) re-implements PR #58451: OpenRouter's
"No endpoints found that support tool use" 404 carries no
_MODEL_NOT_FOUND_PATTERNS signal, so it classifies as unknown/retryable
and the retry loop burns 3-5 attempts on a deterministic rejection.
The plugin classifies it as model_not_found (retryable=False,
should_fallback=True) so the fast-fallback path fires immediately —
demonstrating a waiting core PR converted to a publishable plugin.
Motivation: ~10 open PRs are single-provider error-classification patches
(#58451, #58355, #58502, #58474, #58366, ...). This hook turns that whole
class of contribution into plugin territory.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FWMcB7RPSYUpsXDfBgwjzM
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Four provider-boundary resilience fixes: anthropic auxiliary tasks no longer hard-fail when the credential pool has no usable entry, OpenRouter's tool-use 404 fast-fallbacks instead of burning retries, Poolside's integer wire types are coerced to strings, and the codex app-server no longer discards a completed answer when
turn/completednever arrives.Salvages #58474 (@Jigoooo), #58451 (@webtecnica), #58374 (@ostravajih), #58433 (@ooiuuii) onto current main, authorship preserved.
Changes
agent/auxiliary_client.py:_try_anthropicfalls through toresolve_anthropic_token()when_select_pool_entryreturns(True, None)— matching the fallback the openrouter and codex paths already haveagent/error_classifier.py: OpenRouter "No endpoints found that support tool use" 404 →model_not_found(retryable=False, should_fallback=True)agent/transports/chat_completions.py+agent/chat_completion_helpers.py: coerce integerfinish_reason/tool_call.idto strings (Poolside wire quirk)agent/transports/codex_app_server_session.py: accept recorded final_text as terminal at the deadline when not interrupted and no error, instead of retiring the session with "turn timed out"package-lock.jsonchurn (fix: classify OpenRouter 'no endpoints found that support tool use' 404 as model_not_found with fallback #58451) and the deadpoolsidepicker entry (fix(poolside): handle integer finish_reason and tool_call id #58374 — no ProviderConfig/setup flow exists for it)Validation
finish_reason: 24"24"turn/completedTargeted suites: auxiliary pool fallback regression, error_classifier, transports (incl. codex app-server), chat_completion_helpers — all pass.
Closes #58474. Closes #58451. Closes #58374. Closes #58433.
Infographic