fix(runtime): honour providers-dict api_key/api_mode when resolving - #10910
fix(runtime): honour providers-dict api_key/api_mode when resolving#10910luigileap wants to merge 1 commit into
Conversation
When a user defines a custom provider via the v12 providers dict:
providers:
openai-direct-primary:
api: https://api.openai.com/v1
api_key: dir-key
default_model: gpt-5-mini
transport: codex_responses
_get_named_custom_provider() only looked at ``key_env`` for the API
key and ignored the literal ``api_key`` field, so the provider came
back with an empty api_key. _resolve_named_custom_runtime() then
fell through all remaining candidates and stamped the final result
with ``api_key: "no-key-required"`` — which the downstream client
treats as "no auth required" and fails on the first request.
api_mode/transport were similarly dropped: the entry field existed
but never made it into the returned dict, so the default
``chat_completions`` mode was used for providers that had explicitly
declared ``transport: codex_responses``.
Prefer the literal ``api_key`` on the providers entry and fall back
to the ``key_env`` lookup when absent; propagate ``api_mode`` /
``transport`` into the returned dict so named custom providers using
the v12 format behave like the legacy ``custom_providers`` list
format did.
Regression test: test_named_custom_provider_uses_providers_dict_when_list_missing
|
Thanks for the thorough write-up, @luigileap! Both fixes described here are already on
The target test ( Closing as implemented on main. |
Problem
The test sets up a v12
providersdict with an inline API key andexplicit transport:
and expects
resolve_runtime_provider(requested="openai-direct-primary")to come back with
api_key="dir-key"andapi_mode="codex_responses".Instead it gets
api_key="no-key-required"and the defaultchat_completionsmode.Root cause
_get_named_custom_provider(inhermes_cli/runtime_provider.py) onlyresolved the API key by reading the env var whose name is stored in the
providers entry's
key_envfield:It never looked at the literal
api_keyfield on the entry. Whenusers wrote
api_keydirectly (the natural thing given the fieldname), the returned dict had
api_key="". Downstream,_resolve_named_custom_runtimewalked its fallback chain, found nocandidate, and stamped the final result with
"no-key-required"—which the HTTP client treats as "skip auth" and the remote API then
rejects.
Similarly,
api_mode/transportwas read out of the entry but neverplaced in the returned dict, so downstream always fell back to
_detect_api_mode_for_url(base_url)/chat_completions, ignoringthe user's explicit choice.
Fix
In
_get_named_custom_provider:api_keyfield on the providers entry; fall backto the
key_envlookup whenapi_keyis missing.api_mode(or itstransportalias) and include it in thereturned dict when present.
Both
custom_providers:-list-style andproviders:-dict-style userendpoints now behave the same way with respect to API keys and
transport selection.
Verification
All 67 runtime-provider resolution tests pass, including the previously
failing
test_named_custom_provider_uses_providers_dict_when_list_missing.