Provider resolution and fallback

Corresponding source

SubjectSource
Configuration models and presetsnanobot/config/schema.py::ProviderConfig, ModelPresetConfig
Provider registrynanobot/providers/registry.py::ProviderSpec, find_by_name
Provider construction and snapshotsnanobot/providers/factory.py::make_provider, load_provider_snapshot
Common protocol and retrynanobot/providers/base.py::LLMProvider, LLMResponse
Fallback behaviornanobot/providers/fallback_provider.py::FallbackProvider
WebUI settings synchronizationnanobot/webui/settings_api.py

Learning objectives

After this lesson, you should be able to:

  • distinguish Provider configuration, ProviderSpec, Provider instances, and model presets;
  • explain explicit Provider selection and automatic detection;
  • describe how a fallback chain is constructed;
  • trace DeepSeek into the OpenAI-compatible adapter.

Prerequisites

  • Complete the first DeepSeek request.
  • Understand LLMProvider and LLMResponse.
  • Read a Pydantic schema.

1. Four conceptual layers

LayerExampleLocation
Configuration valuesAPI key, apiBaseConfig.providers
Model selectionprovider + model + generation settingsModelPresetConfig
Provider metadatabackend, default base, keywords, environment keyProviderSpec
Runtime instanceOpenAICompatProvider and othersproviders/factory.py

Do not interpret "provider": "deepseek" as a Python class imported directly. Factory first looks up ProviderSpec, then selects a backend.

2. Named model preset

A preset contains:

  • label;
  • model;
  • provider;
  • maximum tokens;
  • context window;
  • temperature;
  • reasoning effort.

agents.defaults.modelPreset selects the startup preset. Legacy direct fields under agents.defaults.provider/model still form an implicit default preset.

A named preset is preferred because it also supports:

  • /model switching;
  • WebUI model selection;
  • fallback presets;
  • consistent generation settings.

3. ProviderSpec

Location:

  • nanobot/providers/registry.py::ProviderSpec

Important fields:

  • name
  • keywords
  • env_key
  • display_name
  • backend
  • default_api_base
  • is_gateway
  • is_oauth
  • is_local
  • strip_model_prefix
  • model_overrides
  • thinking/reasoning adaptation fields
  • capability flags such as transcription-only

Registry ordering can affect automatic matching and is therefore part of the implementation contract.

4. Provider resolution

Simplified path:

flowchart TD
    CFG["Config"] --> PRE["resolve_preset"]
    PRE --> EX{"Provider explicit and not auto?"}
    EX -->|Yes| SPEC["Find ProviderSpec by name"]
    EX -->|No| MATCH["Match by model / key / apiBase / gateway / local"]
    MATCH --> SPEC
    SPEC --> BACK["Create concrete Provider from backend"]
    BACK --> P["LLMProvider instance"]

For a first configuration, specify the Provider explicitly so you do not debug all of these at once:

  • model keywords;
  • multiple configured keys;
  • Gateway;
  • local endpoint;
  • custom Provider.

5. Current DeepSeek path

ProviderSpec:

name = deepseek
keywords = ("deepseek",)
env_key = DEEPSEEK_API_KEY
backend = openai_compat
default_api_base = https://api.deepseek.com
thinking_style = thinking_type

Runtime path:

ModelPreset(provider=deepseek)
  → Config/Factory
  → ProviderSpec(deepseek)
  → OpenAICompatProvider
  → DeepSeek OpenAI-compatible Chat Completions

When reasoningEffort is explicit, the adapter uses ProviderSpec to map it to DeepSeek’s wire field thinking.type. If omitted, Provider defaults remain in effect.

6. Custom Provider

ProvidersConfig accepts extra Provider configuration. A custom name with apiBase can use the generic OpenAI-compatible path.

However:

  • only an OpenAI-compatible proxy fits the generic custom path;
  • an Anthropic-compatible proxy should use its dedicated configuration;
  • an apiBase alone does not prove complete protocol compatibility;
  • tool calls, reasoning, usage, and streaming still require validation.

7. Fallback

agents.defaults.fallbackModels can reference:

  • a named preset;
  • inline model/Provider/settings.

Factory:

  1. creates the primary;
  2. resolves each fallback preset;
  3. creates a Provider for each fallback;
  4. wraps them in a fallback chain;
  5. retains each candidate’s generation and context parameters.

Fallback is not:

  • broadcasting every request to every model;
  • switching models for each ordinary Tool error;
  • creating a new Session;
  • a guarantee that all candidates have identical protocol semantics.

8. Runtime model refresh

AgentLoop retains a Provider snapshot loader and signature and can refresh after configuration or runtime-model changes:

  • Provider;
  • model;
  • context window;
  • preset;
  • Subagent Provider.

A Turn in progress keeps the model snapshot from its start. Later Turns use the switched model.

9. Debugging path

SymptomInspect
Provider name unrecognizedProvidersConfig, Provider registry
automatic selection chooses wrong Providerexplicit preset, Config._match_provider
incorrect apiBaseProvider configuration, ProviderSpec default
reasoning argument returns 400ProviderSpec thinking style, adapter keyword arguments
fallback does not occurfallback configuration, whether the error is retryable/fallback-eligible
UI does not change after /modelruntime-model event, WebUI coordinator
parent Agent changes but Subagent does notSubagentManager.set_provider call chain

Source and tests

  • nanobot/config/schema.py
  • nanobot/providers/registry.py
  • nanobot/providers/factory.py
  • nanobot/providers/base.py
  • nanobot/providers/fallback_provider.py
  • nanobot/providers/openai_compat_provider.py
  • tests/providers/
  • tests/agent/test_runtime_refresh.py
  • tests/agent/test_runner_fallback.py
  • tests/command/test_model_command.py

Self-check

  1. How do ProviderSpec and ProviderConfig differ?
  2. Why should a first configuration select the Provider explicitly?
  3. Why does DeepSeek not need a dedicated deepseek_provider.py?
  4. Why must fallback retain each candidate model’s own context parameters?
  5. Why must a runtime model switch not alter an already-started Turn?

Next step

Continue to Engineering and Security for concurrency, reliability, testing, and permission boundaries.