Skip to content
Router One

Connect Google ADK agents to Router One through the LiteLlm connector

Google's Agent Development Kit (ADK) is an open-source framework for building, evaluating, and deploying agents; in Python an LlmAgent carries a name, an instruction, tools, and a model, and adk run or adk web drives the loop. Its native path resolves model strings such as gemini-… against Google's own API. Models outside that path are reached through the LiteLlm connector, and that is where Router One goes: LiteLlm with model openai/…, api_base, and api_key turns every model call into POST /v1/chat/completions, so one key serves any chat model in the catalog with a cost and latency trace per call in Dashboard → Logs, while tools, sessions, and the agent loop keep running in your process. This guide covers the install, the three LiteLlm fields, the model-string rule, and how to budget a run.

Install google-adk and litellm, then set your credentials

Use Python 3.10 or newer. Install google-adk and litellm: the LiteLlm connector page states that ADK requires litellm>=1.84, and the connector is listed as supported in ADK Python only, so this guide does not apply to the TypeScript, Go, Java, or Kotlin ADKs. adk create my_agent scaffolds the folder that adk run expects: agent.py with a root_agent definition, __init__.py, and a .env file. The quickstart keeps the API key in that .env file and then runs both commands; adk run loads it unless ADK_DISABLE_LOAD_DOTENV is set, and variables already exported in the shell keep precedence over the file, so the macOS/Linux shell example below and a .env file are both valid places for the two values. Replace both placeholders with a Router One key and the exact ID of a current model that supports Chat Completions. The ROUTER_ONE_* names belong to this example, which reads them explicitly; OPENAI_API_KEY is not needed, because the key is passed to LiteLlm directly.

terminal
python -m pip install google-adk
python -m pip install "litellm>=1.84"
export ROUTER_ONE_API_KEY="sk-your-router-one-key"
export ROUTER_ONE_MODEL_ID="<exact-model-id-from-/models>"

Configure Google ADK to use the Router One base URL

Save this as my_agent/agent.py, keep the __init__.py that adk create generated next to it, and run adk run my_agent from the parent folder. LiteLlm is ADK's wrapper around the litellm library: every keyword argument besides model is stored and passed through to litellm's completion call, which is how api_base and api_key reach it. model must start with openai/: the LiteLLM docs define that prefix as the instruction to call an OpenAI /chat/completions endpoint, and LiteLLM splits the string at the first slash only, so everything after openai/ is sent unchanged as the request's model field. The example builds it from ROUTER_ONE_MODEL_ID, and a GPT-family ID such as openai/gpt-5.5 therefore becomes openai/openai/gpt-5.5. api_base is the /v1 base URL with nothing appended; LiteLLM uses the OpenAI Python client underneath, which adds /chat/completions itself. api_key is the Router One key; passing it here means neither OPENAI_API_KEY nor OPENAI_API_BASE needs to exist. root_agent is the only required element of an ADK agent folder, and name, instruction, and the model object are the LlmAgent fields the LiteLLM connector page uses. adk web --port 8000 from the same parent folder opens the development UI at http://localhost:8000:

my_agent/agent.py
import os

from google.adk.agents import LlmAgent
from google.adk.models.lite_llm import LiteLlm

# openai/ makes LiteLLM call an OpenAI /chat/completions endpoint; the rest of
# the string is sent unchanged as the model field (openai/openai/gpt-5.5 for a GPT ID).
model = LiteLlm(
    model="openai/" + os.environ["ROUTER_ONE_MODEL_ID"],
    api_base="https://api.router.one/v1",  # ends in /v1, nothing after it
    api_key=os.environ["ROUTER_ONE_API_KEY"],
)

# root_agent is the only required element of the agent folder.
root_agent = LlmAgent(
    name="router_one_agent",
    model=model,
    instruction="Answer in one short sentence.",
)

# From the parent folder:  adk run my_agent     (or: adk web --port 8000)

Which value goes where, and how to verify it

Everything Router One needs is inside the LiteLlm object; the rest of the agent is unchanged. The rows give each field, the value to enter, and the check that proves it, followed by the two paths this guide does not configure:

Google ADK fieldValueWhat to verify
LiteLlm(model=…)openai/ followed by the exact catalog ID: openai/anthropic/claude-sonnet-5, openai/deepseek-v4.1-flash, or openai/openai/gpt-5.5 for a GPT-family IDThe model in each Logs trace is the ID without the leading openai/; LiteLLM splits at the first slash only and keeps the rest, including a prefix that belongs to the ID
LiteLlm(api_base=…)https://api.router.one/v1Ends in /v1 with nothing after it; the LiteLLM docs tie a Not Found error to a missing /v1 postfix and say not to add anything to the base URL
LiteLlm(api_key=…)A Router One key created for this agent, with maxSpend, read from ROUTER_ONE_API_KEYThe first trace appears in Dashboard → Logs under that key; the argument is used before OPENAI_API_KEY, so no environment value can replace it
LlmAgent(model=…)The LiteLlm object, not a stringA model string is resolved by ADK's registry: gemini-… strings go to Google's API with GOOGLE_API_KEY and never reach the gateway; ADK's warning about Gemini via LiteLLM is keyed to gemini/ and vertex_ai/ strings and does not apply to openai/google/… IDs
LlmAgent(tools=[…])Plain Python functions; ADK wraps each one as a FunctionTool and builds its schema from the signature and docstringTool calling on the model detail page; the function runs in your process, and each tool result goes back to the model as another Chat Completions request
RunConfig(streaming_mode=…)StreamingMode.NONE unless you pass StreamingMode.SSE to runner.run_asyncStreaming on the model detail page; adk run passes no RunConfig, so it uses the non-streaming call
Not configured by this guidegemini-… model strings and the native Gemini model class; ADK Go's experimental openaimodel packageThe Go package targets the OpenAI Responses API, which the gateway serves natively only for the currently listed GPT-family and DeepSeek IDs

Budget one agent and reconcile its model calls

One user turn can produce several model requests: the connector makes one LiteLLM call per LLM call in the run, and each function-tool result goes back to the model as a further Chat Completions request. RunConfig.max_llm_calls bounds the number of LLM calls in one run — 500 unless you pass a value or set ADK_MAX_LLM_CALLS, and a value of 0 or less removes the bound; it counts calls, not money. Retries are separate requests too: when a request carries http_options.retry_options, ADK forwards its attempts value as LiteLLM's num_retries, and every attempt that reaches the gateway is its own request with its own request_id, trace, and charge. Give each agent a dedicated Router One key with maxSpend; a run that reaches the cap gets HTTP 402 on its next call while the wallet and other keys are untouched. The Event history in adk web shows the events of a run, and adk run --save_session writes them to a JSON file; neither shows a bill. The charge per call is the trace in Dashboard → Logs — model, input and output tokens, cost, latency, status, and request_id — so reconcile by key, time, exact model, and request_id, and keep the request_id when reporting a failure. Router One serves the model requests and records their traces; sessions, state, tool execution, and the agent loop stay in your process.

Which model ID should Google ADK 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 Google ADK. 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 Google ADK 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 Google ADK call in your request trace

Send a simple text request from Google ADK, 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

LiteLLM raises BadRequestError: LLM Provider NOT provided, or the call goes somewhere other than Router One. What is wrong with the model string?

The first path segment of the model string is LiteLLM's provider switch, and only openai/ selects the OpenAI Chat Completions handler that sends the request to api_base. Without it, a bare catalog ID that LiteLLM cannot match to one of its own provider model lists ends in BadRequestError with the message LLM Provider NOT provided. Pass in the LLM provider you are trying to call; an ID that LiteLLM does recognize as another provider's model, or whose first segment is a LiteLLM provider name such as anthropic/claude-sonnet-5, is handed to that provider's own handler instead, so no Chat Completions request reaches the gateway. Keep openai/ as the first segment for every family: LiteLLM removes only that one prefix and sends the rest as the model field, which is why a GPT-family ID reads openai/openai/gpt-5.5. Two more checks from the LiteLLM docs and source: api_base must end in /v1 with nothing appended, because the OpenAI client inside LiteLLM adds /chat/completions itself and a missing /v1 surfaces as a Not Found error; and the api_key and api_base arguments are used before OPENAI_API_KEY, OPENAI_BASE_URL, and OPENAI_API_BASE, so a stray environment value cannot redirect the two values in agent.py.

Do streaming and tool calling work through the LiteLlm connector?

Both are handled by the connector, and both depend on the model. Streaming is decided per run rather than per model object: RunConfig.streaming_mode defaults to StreamingMode.NONE, and the flow passes stream=True to the connector only when it is StreamingMode.SSE; adk run calls the runner without a RunConfig, so it uses the non-streaming call. With SSE the connector adds stream=True and stream_options with include_usage to the LiteLLM call, reads the chunks, and reassembles tool calls by index, returning an error when a tool call's arguments were cut off before they parsed as JSON. Tools travel as tool definitions in the same Chat Completions request: ADK builds each definition from the function's signature and docstring, runs the function in your process, and sends the result back to the model. The ADK vLLM page states that the serving endpoint must support OpenAI-compatible tool calling, so confirm tool calling on the model detail page before adding tools, and test a plain reply, then streaming, then one tool call as separate requests in Logs. The connector's handling of Anthropic thinking blocks is documented for the anthropic/ LiteLLM route, not for the openai/ route used here.

Which models can Google ADK use through the gateway?

Choose a current catalog model that supports both the endpoint and the features Google ADK 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.