Skip to content

fix(agent): map bare "custom" provider to named providers: key via base_url match#14718

Closed
cdanis wants to merge 1 commit into
NousResearch:mainfrom
cdanis:fix/auxiliary-named-provider-new
Closed

fix(agent): map bare "custom" provider to named providers: key via base_url match#14718
cdanis wants to merge 1 commit into
NousResearch:mainfrom
cdanis:fix/auxiliary-named-provider-new

Conversation

@cdanis

@cdanis cdanis commented Apr 23, 2026

Copy link
Copy Markdown
Contributor

What does this PR do?

When auxiliary models (vision, web_extract) use _read_main_provider(), they get the literal string "custom" for model.provider if the user configured a custom endpoint. But _get_named_custom_provider() and _resolve_vision_provider() expect a provider key name that exists in the providers: dict — they look up credentials, defaults, and client adapters by that key. This mismatch caused auxiliary tasks to fail with None provider resolution.

The fix enhances _read_main_provider() in agent/auxiliary_client.py: when the bare "custom" provider is detected, it searches the providers: dictionary for an entry whose api, url, or base_url matches the model base_url. If found, it returns the actual key name instead of "custom". This bridges the gap between config-time naming (what users put in providers: my-local) and runtime lookup (what auxiliary resolution code expects).

Related Issue

Fixes #14676

Type of Change

  • Bug fix (non-breaking change that fixes an issue)

Changes Made

  • agent/auxiliary_client.py: Added base_url matching logic in _read_main_provider() that iterates providers: entries and returns the first key whose api/url/base_url matches the model configured base_url (with trailing-slash normalization). Falls through to bare "custom" if no match.
  • tests/agent/test_auxiliary_named_custom_providers.py: Added 6 test cases covering: bare custom with matching providers entry, bare custom with no match, providers entries using "base_url" key, non-custom passthrough, absent providers dict fallback, and trailing slash normalization.

How to Test

  1. Run the new tests: pytest tests/agent/test_auxiliary_named_custom_providers.py -o addopts= -v
  2. Run the full auxiliary test suite: pytest tests/agent/test_auxiliary*.py -o addopts= -q (all 142 pass)
  3. For an integration test, configure a named custom provider in config.yaml and verify auxiliary tasks (vision model, web extract) resolve it correctly via the _read_main_provider() mapping.

Checklist

Code

  • Read Contributing Guide
  • Commit messages follow Conventional Commits (fix(scope):)
  • Searched for existing PRs — this is not a duplicate
  • PR contains only changes related to this fix (no unrelated commits)
  • All tests pass: pytest tests/ -q
  • Added tests for my changes
  • Tested on: Ubuntu 24.04

Documentation & Housekeeping

  • N/A — docstring updated in _read_main_provider()
  • N/A — no config key changes
  • N/A — no architecture or workflow changes
  • N/A — no cross-platform concerns
  • N/A — no tool schema changes

Screenshots / Logs

Test output:

tests/agent/test_auxiliary_named_custom_providers.py ... 25 passed in X.XXs
All 142 auxiliary tests pass.

…se_url match

When auxiliary models (vision, web_extract) use `_read_main_provider()`, they get
the literal string "custom" for `model.provider` if the user configured a custom
endpoint. But `_get_named_custom_provider()` and `_resolve_vision_provider()` expect
a provider key name that exists in the `providers:` dict — they look up credentials,
defaults, and client adapters by that key. This mismatch caused auxiliary tasks to
fail with `None` provider resolution.

The fix enhances `_read_main_provider()` in `agent/auxiliary_client.py`: when the
bare "custom" provider is detected, it searches the `providers:` dictionary for an
entry whose `api`, `url`, or `base_url` matches the model's `base_url`. If found, it
returns the actual key name instead of "custom". This bridges the gap between
_config-time naming_ (what users put in `providers: my-local`) and _runtime lookup_
(what auxiliary resolution code expects).

Changes:
- agent/auxiliary_client.py: Added base_url matching logic in `_read_main_provider()`
  that iterates `providers:` entries and returns the first key whose api/url/base_url
  matches the model's configured base_url (with trailing-slash normalization). Falls
  through to bare "custom" if no match.
- tests/agent/test_auxiliary_named_custom_providers.py: Added 6 test cases covering:
  * Bare custom with matching providers entry → returns key name
  * Bare custom with no match → falls through to "custom"
  * providers: entries using "base_url" key (not just "api")
  * Non-custom providers pass through unchanged
  * Absent providers dict uses bare "custom" as fallback
  * Trailing slash normalization on both sides of comparison

This is a minimal, surgical fix — only the single return point in `_read_main_provider()`
is modified. No refactoring of auxiliary resolution chain.

Refs: NousResearch#14676 (auxiliary named provider resolution)
@alt-glitch alt-glitch added type/bug Something isn't working P2 Medium — degraded but workaround exists comp/agent Core agent loop, run_agent.py, prompt builder area/config Config system, migrations, profiles labels Apr 23, 2026
@teknium1

Copy link
Copy Markdown
Contributor

Closing — the bare-custom runtime resolution fix landed via salvage #15103 (of parallel #14719), which fixes the root cause in runtime_provider.py for all callers (main runtime AND auxiliary client) rather than patching auxiliary_client.py independently.

Your base_url-matching approach in _read_main_provider() was clever, but the issue turned out to be upstream — _get_named_custom_provider() and the _resolve_openrouter_runtime fallback chain needed the fix, not the aux path. With #15103 landed, aux client now inherits the correct resolution automatically.

Thanks for the detailed 6-test coverage — that's the standard we want.

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

Labels

area/config Config system, migrations, profiles comp/agent Core agent loop, run_agent.py, prompt builder P2 Medium — degraded but workaround exists type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bug: bare 'custom' provider falls through to OpenRouter — no base_url resolution

3 participants