How to Extend Nanobot Providers: A Complete Guide to Adding Custom LLM Backends
To extend Nanobot providers, subclass the LLMProvider base class in nanobot/providers/base.py, register a ProviderSpec entry in nanobot/providers/registry.py, and wire the backend into nanobot/providers/factory.py.
Extending Nanobot providers allows you to integrate custom LLM services into the HKUDS/nanobot framework. Whether you're connecting to a private API or a new cloud provider, the modular architecture makes it straightforward to add support without modifying core logic. This guide walks through the exact file locations and code patterns required to extend Nanobot providers correctly.
Understanding the Nanobot Provider Architecture
Before you extend Nanobot providers, understand the three core components that handle LLM interactions. The LLMProvider abstract base class in nanobot/providers/base.py defines the interface that every provider must implement, including the chat and chat_stream methods. The ProviderSpec registry in nanobot/providers/registry.py serves as the single source of truth for provider metadata, keywords, and environment variable mappings. Finally, the factory in nanobot/providers/factory.py instantiates concrete provider classes based on configuration, handling API key injection and base URL resolution.
Step-by-Step Guide to Extending Nanobot Providers
Step 1 - Implement the Concrete Provider Class
Create a new file in nanobot/providers/ that subclasses LLMProvider from nanobot/providers/base.py. You must implement the abstract chat method, and optionally chat_stream if your service supports streaming. If your API is OpenAI-compatible, consider subclassing OpenAICompatProvider instead to reuse existing logic.
Key implementation details:
- Set
supports_progress_deltas = Trueif your API streams partial responses. - Use
_sanitize_empty_content()to clean message payloads. - Return an
LLMResponseinstance containing content and optional tool calls.
import httpx
from nanobot.providers.base import LLMProvider, LLMResponse, ToolCallRequest
from typing import Any, List
class MyProvider(LLMProvider):
"""Example provider that talks to a hypothetical OpenAI‑compatible endpoint."""
supports_progress_deltas = True
async def chat(
self,
messages: List[dict[str, Any]],
tools: List[dict[str, Any]] | None = None,
model: str | None = None,
max_tokens: int = 4096,
temperature: float = 0.7,
reasoning_effort: str | None = None,
tool_choice: str | dict[str, Any] | None = None,
) -> LLMResponse:
payload = {
"model": model or self.default_model,
"messages": self._sanitize_empty_content(messages),
"max_tokens": max_tokens,
"temperature": temperature,
}
if tools:
payload["tools"] = tools
if tool_choice:
payload["tool_choice"] = tool_choice
async with httpx.AsyncClient() as client:
resp = await client.post(
f"{self.api_base}/chat/completions",
headers={"Authorization": f"Bearer {self.api_key}"},
json=payload,
timeout=60,
)
data = resp.json()
content = data.get("choices", [{}])[0].get("message", {}).get("content")
tool_calls = []
for tc in data.get("choices", [{}])[0].get("message", {}).get("tool_calls", []):
tool_calls.append(
ToolCallRequest(
id=tc["id"],
name=tc["function"]["name"],
arguments=tc["function"]["arguments"],
)
)
return LLMResponse(content=content, tool_calls=tool_calls)
Step 2 - Register the Provider in the Registry
Add a ProviderSpec entry to the PROVIDERS list in nanobot/providers/registry.py. This specification tells Nanobot how to identify and configure your provider.
Critical fields:
name: Unique identifier (e.g.,"myprovider").keywords: Tuple of strings for model-name matching (e.g.,("myprovider", "my-model")).backend: Short identifier used by the factory (e.g.,"myprovider").env_key: Environment variable name for the API key.default_api_base: URL prefix for API calls.
from nanobot.providers.registry import ProviderSpec
ProviderSpec(
name="myprovider",
keywords=("myprovider", "my-model"),
env_key="MYPROVIDER_API_KEY",
display_name="MyProvider",
backend="myprovider",
default_api_base="https://api.myprovider.com/v1",
env_extras=(("MY_SPECIAL_HEADER", "{api_key}"),),
),
Place your entry in the list where you want it to rank for automatic matching; the registry order determines priority when multiple providers match a model prefix.
Step 3 - Wire the Backend into the Factory
Extend the make_provider function in nanobot/providers/factory.py to recognize your new backend value. Insert a branch that imports and instantiates your provider class, passing the API key, base URL, and any extra configuration.
elif backend == "myprovider":
from nanobot.providers.myprovider import MyProvider
provider = MyProvider(
api_key=p.api_key if p else None,
api_base=config.get_api_base(model, preset=resolved),
default_model=model,
extra_body=p.extra_body if p else None,
)
The factory already handles validation of API keys, base URLs, and OAuth/local flags, so you only need to pass the relevant configuration pieces to your constructor.
Step 4 - Add Provider-Specific Configuration (Optional)
If your service requires custom headers, query parameters, or authentication methods, expose them via ProviderConfig in nanobot/config/schema.py. The registry can reference these through env_extras or default_extra_headers.
# In nanobot/config/schema.py
class ProvidersConfig(BaseModel):
# ... existing providers ...
myprovider: ProviderConfig = Field(default_factory=ProviderConfig)
Configuring and Using Your Extended Provider
Once implemented, configure your extended provider in ~/.nanobot/config.json. Use the model prefix that matches your registered keywords.
{
"providers": {
"myprovider": {
"api_key": "sk-xxxxxxxxxxxx",
"api_base": "https://api.myprovider.com/v1"
}
},
"agents": {
"defaults": {
"model": "myprovider/gpt-4",
"provider": "auto"
}
}
}
When you launch Nanobot with "model": "myprovider/gpt-4", the system matches the myprovider/ prefix to your ProviderSpec, constructs MyProvider via the factory, and routes all chat calls through your implementation.
Summary
- Subclass
LLMProviderinnanobot/providers/base.pyto implement thechatmethod and optionalchat_stream. - Register a
ProviderSpecinnanobot/providers/registry.pywith unique keywords and backend identifier. - Wire the factory in
nanobot/providers/factory.pyby adding a branch tomake_providerthat instantiates your class. - Configure via JSON using the model prefix matching your registered keywords to activate the provider.
- Reuse
OpenAICompatProviderif your API follows OpenAI conventions to minimize boilerplate.
Frequently Asked Questions
Do I need to implement streaming support to extend Nanobot providers?
No, streaming is optional. Implement the chat_stream method only if your LLM service supports server-sent events or progressive deltas. For non-streaming providers, Nanobot will handle the response using your synchronous chat implementation. Set supports_progress_deltas = False (or omit it) to indicate that your provider returns complete responses only.
Can I extend Nanobot providers for non-OpenAI-compatible APIs?
Yes, you can extend Nanobot providers for any HTTP-based LLM service. While subclassing OpenAICompatProvider simplifies integration for OpenAI-compatible endpoints, the base LLMProvider class is agnostic to the underlying protocol. Implement the chat method to handle your specific authentication headers, request payload structure, and response parsing.
How does the provider registry prioritize matching when multiple keywords overlap?
The registry evaluates PROVIDERS in nanobot/providers/registry.py sequentially, and the first match wins. Place your ProviderSpec higher in the list if you want it to take precedence over existing providers with similar keywords. The keywords tuple supports multiple aliases, allowing model names like myprovider/gpt-4 or my-model/llama to resolve to the same backend.
What environment variables are required when extending Nanobot providers?
At minimum, define an env_key in your ProviderSpec (e.g., "MYPROVIDER_API_KEY"). The factory automatically checks for this variable if no API key is provided in the config file. You can also specify env_extras for additional headers or configuration tokens that your provider requires beyond the standard API key.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →