Skip to content
Router One

Connect smolagents to Router One through OpenAIModel and one api_base

smolagents is Hugging Face's Python library for agents that write their actions as Python code (CodeAgent) or as JSON tool calls (ToolCallingAgent). Its OpenAIModel class connects to an OpenAI-compatible API server, so pointing it at Router One reaches every chat model in the catalog served on /v1/chat/completions, with a cost and latency trace per request, while the agent loop, the Python executor, and the tools keep running in your process or in a sandbox you configure. This guide configures one OpenAIModel with api_base, a key from an environment variable, and a model ID from another, runs a CodeAgent without tools and then with the built-in WebSearchTool, and explains which requests a run sends, what max_steps bounds, and how to reconcile the library's token counts with Dashboard → Logs.

Install the openai extra and set your credentials

Use Python 3.10 or newer. Install smolagents with the openai extra: OpenAIModel calls the OpenAI Python package and raises ModuleNotFoundError with the message Please install 'openai' extra to use OpenAIModel when it is missing. The toolkit extra adds the default toolbox (ddgs for DuckDuckGoSearchTool, markdownify for VisitWebpageTool) so that add_base_tools=True works later; the WebSearchTool used below needs only the requests package that the core install already carries. Listing two extras in one bracket is the syntax the installation page documents. The shell example below is for macOS/Linux. Replace both placeholders before running it, using a Router One key and the exact ID of a current chat model. The ROUTER_ONE_* names belong to this example, which reads them explicitly; OPENAI_API_KEY is not needed, because the key is passed to the model class as api_key. Keep the same environment active when running the Python file.

terminal
python -m pip install "smolagents[openai,toolkit]"
export ROUTER_ONE_API_KEY="sk-your-router-one-key"
export ROUTER_ONE_MODEL_ID="<exact-model-id-from-/models>"

Configure smolagents to use the Router One base URL

Save this as agent.py and run python agent.py. OpenAIModel takes the catalog ID as model_id, unchanged, including any provider prefix that is part of the ID; api_base is the /v1 base URL and is handed to the OpenAI client as base_url, which appends /chat/completions, so every model call in a run is one POST /v1/chat/completions; api_key is read from ROUTER_ONE_API_KEY. The first CodeAgent gets tools=[] and max_steps=4: the model writes Python, smolagents parses the code block and executes it in the local Python executor, and the run ends when that code calls final_answer or the fourth step is used up. return_full_result=True makes run() return a RunResult instead of the bare answer: output is the final answer, state is success or max_steps_error, and token_usage sums the input and output tokens the API reported for every action and planning step. The second agent adds WebSearchTool(), whose default engine is DuckDuckGo and needs no key: the search is an HTTP request from your process to lite.duckduckgo.com, so it appears nowhere in Logs, while the model calls of that run reach the gateway exactly as before. Nothing else differs between the two agents, so a run that fails only with the tool isolates the tool's own network access.

agent.py
import os

from smolagents import CodeAgent, OpenAIModel, WebSearchTool

model = OpenAIModel(
    model_id=os.environ["ROUTER_ONE_MODEL_ID"],
    api_base="https://api.router.one/v1",
    api_key=os.environ["ROUTER_ONE_API_KEY"],
)

# 1. No tools: every model call in this run is one POST /v1/chat/completions
agent = CodeAgent(tools=[], model=model, max_steps=4)
result = agent.run(
    "Compute the 30th Fibonacci number in Python and return it with final_answer.",
    return_full_result=True,
)
print(result.output, result.state, result.token_usage)

# 2. Same model, one built-in tool: the search leaves your process; the model calls still reach the gateway
agent = CodeAgent(tools=[WebSearchTool()], model=model, max_steps=4)
print(agent.run("Find the URL of the smolagents documentation and return it with final_answer."))

Which smolagents setting sends which request

OpenAIModel carries the connection; the agent class decides what each step asks the model for. The rows below give the value for each setting in this guide, the request it produces, and what to confirm before relying on it.

smolagents fieldValueWhat to verify
OpenAIModel → api_basehttps://api.router.one/v1Passed to the OpenAI client as base_url, which appends /chat/completions; a 404 not_found on step 1 means /v1 is missing
OpenAIModel → api_keyA Router One key created for this agent, with maxSpendThe step-1 request appears in Dashboard → Logs under that key
OpenAIModel → model_idThe exact catalog IDThe model in each Logs trace matches /models character for character; with CodeAgent any current chat model works
CodeAgent(tools=..., model=...)Chat Completions with stop sequences and no tools field; the model answers with a code block that smolagents parsesNo tool-calling requirement; a step whose output cannot be parsed is logged as an error, and the next step is another request
ToolCallingAgent(tools=..., model=...)Chat Completions with tools (JSON schemas) and tool_choice set to requiredTool calling on the model detail page
max_steps (default 20; run() accepts an override)Bounds action steps, not requests or spend; when it is reached, one more request writes the final answerRunResult.state is max_steps_error and the last step carries AgentMaxStepsError
executor_type (default local)local, blaxel, e2b, modal, or dockerCode runs in your process or in your sandbox; the gateway sees only the model calls
stream_outputs=TrueThe same request with stream=True and stream_options include_usageStreaming on the model page; an interrupted stream is recorded as HTTP 499 client_cancelled

Budget one agent and reconcile its requests

One agent.run() is several model requests: every action step is one request; a planning step is added at step 1 and every planning_interval steps after it when that argument is set; and reaching max_steps triggers one more request that writes the final answer from the run memory. Input tokens grow with each step, because every step resends the whole run memory as messages, so max_steps bounds the number of steps, not the spend. Two retry layers can add requests: the OpenAI client inside OpenAIModel retries connection errors and 408, 409, 429, and 5xx responses twice by default, adjustable through client_kwargs={'max_retries': 0}, and the model class itself retries errors whose message contains 429 or rate limit up to 3 attempts with a 60-second base wait unless retry=False, so a persistent 429 can produce up to nine attempts for one step. Every attempt that reaches the gateway is its own request in Dashboard → Logs with its own request_id, trace, and charge. Give each agent a dedicated key with maxSpend: a looping run then gets HTTP 402 at the cap you set, neither layer retries a 402, and the wallet and your other keys are untouched. requests_per_minute on the model class throttles the client and is separate from the key's rateLimit. RunResult.token_usage and agent.monitor.get_total_token_counts() are the library's tally of the usage the API reported, reset by each run() unless reset=False; the charge is what Logs records. Reconcile by key, time, model, and token counts, and keep the request_id when reporting a failure. Router One records model-call metadata; it does not run the agent's code, its tools, or its sandbox.

Which model ID should smolagents send?

Copy the exact model ID from /models, preserving case, hyphens, and version suffixes; do not substitute a display name. Open its detail page and match the supported API endpoints, context window, and capabilities such as tool calling to the provider and features selected in smolagents. A catalog listing does not mean the client can use every feature of that model. Give each tool a dedicated API key with a maxSpend cap.

Which API protocol is smolagents using?

OpenAI-compatible describes an interface format; it does not make Chat Completions (/v1/chat/completions), Responses (/v1/responses), and Anthropic Messages (/v1/messages) interchangeable. Check the installed client version, provider configuration, and actual request path against the model detail page and API compatibility fact sheet. A successful plain-text chat does not establish support for hosted tools, conversation state, or file-editing features.

Verify the smolagents call in your request trace

Send a simple text request from smolagents, then match its trace in Dashboard → Logs by time, model, and request_id: tokens, cost, latency, and status. Next, test streaming, tool calls, and multi-turn history separately. For failures, retain the actual request path, full error message, and request_id. If there is no matching log, check client configuration and connectivity before attributing the error to the gateway or upstream.

FAQ

The run exits at Step 1 with AgentGenerationError. What is the wrapped error telling me?

AgentGenerationError: Error in generating model output wraps the exception raised by the model class, and the run exits at once instead of moving to the next step. Read the HTTP status inside it. 404 not_found means api_base lacks /v1: OpenAIModel hands api_base to the OpenAI client as base_url, the client appends /chat/completions, so https://api.router.one alone posts to /chat/completions, and the gateway's message says the base URL must end in /v1. 401 means the gateway did not accept the key; check the value of ROUTER_ONE_API_KEY in the environment that runs the file. A 400 that names a parameter is the model rejecting it: CodeAgent sends stop sequences on every step, and smolagents drops the stop parameter only for model names matching gpt-5*, o3*, o4*, or grok-*, matched on the part of the ID after the last slash. The documented switch is stop=REMOVE_PARAMETER on OpenAIModel, and the same sentinel removes any other keyword argument. Older snippets that import OpenAIServerModel still run, because it is an alias of OpenAIModel in the current source; the documented name is OpenAIModel.

Does CodeAgent need a model with tool calling, or only ToolCallingAgent?

Only ToolCallingAgent. It passes the agent's tools to the model class, and OpenAIModel turns them into the request's tools field with tool_choice set to required, so the model must support tool calling on /v1/chat/completions; check the model detail page. If the reply contains no tool_calls, smolagents falls back to parsing a tool call out of the text, which is less reliable. CodeAgent sends no tools field at all: the tools are described in the system prompt, the model answers with a Python code block, and parse_code_blobs extracts it, falling back to a markdown python fence or to bare code. So CodeAgent runs on any current chat model in the catalog; what it needs is a model that follows the code-block format, and a step whose output cannot be parsed is recorded as an error and followed by another request. Two CodeAgent options change the request: use_structured_outputs_internally=True adds a JSON response_format on every step, which needs structured-output support, and stream_outputs=True switches to streaming. With either agent, the code and the tools run in your process or in the executor you configured; the gateway serves the model calls only.

Which models can smolagents use through the gateway?

Choose a current catalog model that supports both the endpoint and the features smolagents uses. Check /models and the model detail page for the exact ID, current rates, and capabilities; a family name such as GPT or Claude is not a compatibility guarantee. Seeing a model in the picker confirms discovery, so verify an actual request too.

Models are listed, but requests fail with 400 or 404. What should I check?

Record the actual request path and error message, then check the exact model ID. A 400 can indicate invalid parameters, unsupported tools, or a model/endpoint mismatch; a 404 can indicate an incorrect path or missing resource, so it does not by itself establish that a model was retired. If the error says must be called via, use the named endpoint or select a model supported on the current endpoint. Do not add or remove /v1 or /chat/completions across all clients indiscriminately.

Does this work from Mainland China?

Yes. The gateway is reachable from Mainland China without a VPN, and the configuration is identical to the global setup.

How do I debug a 401/402/403/429?

Match the request and error message in Dashboard → Logs. For 401, check whether the key was sent and is valid; for 402, check wallet balance and maxSpend; for 403, check key permissions and access restrictions. For 429, distinguish request/token limits from upstream throttling using the error details. Keep the request_id and follow the error-codes reference.