OpenAI and Ollama in the same box

A box can hold several model providers at once. Each model call goes to one of them. A pick in Studio names its provider, and a call that names only a model id goes to the one provider that lists that id. This page puts OpenAI, with a key, and Ollama, with none, on the same box and runs one agent on each.

A model provider is an OpenAI-compatible endpoint the box sends model calls to. The box is the container that runs your agents: stackbone dev on your machine, or the one you build with stackbone package. The Gateway page covers a box with one provider. This page adds a second one beside it.

What you need first

You need Why
An OpenAI API key OpenAI refuses a model call without one.
Ollama, with a model pulled ollama pull qwen3:4b gives the second agent its model.
A running box Studio configures the providers of the box it is open on.

Add OpenAI as the first provider

Open Settings › Model provider and pick OpenAI in the Provider list on the left. It fills the base URL with https://api.openai.com/v1. Paste your key into API key, click Test connection, then Save.

This is the box's first provider. Once saved, it gets a row in the Active section at the top of the left column, above the provider list. Click the row to open its endpoint and key on the right. The first provider can also come from the box's environment. A box that exports MODEL_PROVIDER_BASE_URL and MODEL_PROVIDER_API_KEY uses those values instead of the form. Gateway explains each field.

Add Ollama beside it

Pick Ollama in the provider list, under the Active rows. Once the box has a first provider, a pick in that list adds a provider and leaves the first one as it is. The add form opens on the right, with Ollama chosen and its base URL filled in.

Field What to enter
Name Ollama. Studio shows the provider under this name.
Id Filled in from the name, so ollama. 2 to 32 lowercase letters, digits and -, starting with a letter and not ending with -. It cannot change once saved.
Provider Ollama, from your pick.
Base URL http://localhost:11434/v1, from your pick. Keep the /v1, the path Ollama serves its OpenAI-compatible API on.
API key Leave it empty. Ollama answers without a key.

Click Add provider to save it. OpenAI stays in place, and the box now holds both. Ollama gets its own row under Active, and its form stays open on the right.

To add a second Ollama host, open the first Ollama row and click Add another Ollama in its form. Studio opens a new add form with Ollama and its usual base URL filled in. Give the host a name and id of its own.

For an endpoint without a named preset, click + Add a custom provider under the provider list. This line stays available after you add a custom provider.

localhost is correct for stackbone dev, which runs on your machine. Inside a container, localhost is the container itself. See Reach the gateway from the container for the address to use there.

Read the Active rows

Each row is one provider. Its tint shows how its last model listing went.

On a row What it means
A green tint The provider answered its last model listing.
An amber tint and not responding The provider's last model listing failed. The reason follows it.
No tint The box has not read this provider's model list yet.

When two providers list the same model id, a served twice warning appears in the header next to the provider count. Click it to list every overlapping id and the providers that serve it. Say you add LM Studio as well, and both it and Ollama serve qwen3:4b. A call that names qwen3:4b alone is then refused with model_ambiguous, because the box cannot choose for you. Pick the model with its provider in Studio, or keep it on one provider only.

Edit, test or remove a provider

Click a row to open that provider on the right. The first provider opens the form you saved it with. Any other provider opens its edit form, with Test connection and Remove provider under it.

Test connection asks the open provider for its models, and saves nothing.

For providers added beside the first, the edit form shows the key field empty, because the box never sends a stored key back to the browser. Leave the field empty to keep the stored key. To clear it, click Remove key and save. For the first provider, saving with an empty key clears the stored key, as the Gateway page explains.

Remove provider deletes the open provider and its key, after you confirm. An agent whose model only that provider serves stops answering, and a pick that names the provider fails until you pick the model again. On the first provider, it removes only what Studio saved. Gateway covers a first provider that the box's environment sets.

If the box cannot read the providers you added, Studio lists the first provider's models with a warning that the box could not read its other model providers. Until you fix it, a call that names only a model id goes to the first provider, and a pick that names another provider is refused with model_provider_not_responding.

Run one agent on each

Name each model by the id its provider lists. One agent runs on OpenAI:

deep-agents/writer/index.ts
import { defineDeepAgent } from '@stackbone/sdk/deep';

export default defineDeepAgent({
  name: 'writer',
  model: 'gpt-4o',
});

The other runs on Ollama:

deep-agents/triage/index.ts
import { defineDeepAgent } from '@stackbone/sdk/deep';

export default defineDeepAgent({
  name: 'triage',
  model: 'qwen3:4b',
});

The box reads every provider's model list. It sends each call to the provider whose list holds the id the call names. gpt-4o is on OpenAI's list and qwen3:4b is on Ollama's, so each agent reaches its own provider. Adding the second provider takes no code change and no rebuild. A model you pass to stackbone.ai routes the same way, for chat and for embeddings.

Ids match exactly. qwen3 does not reach a provider that lists qwen3:latest, and gpt-4o-mini does not reach one that lists only gpt-4o. Copy the id from the model list on the Model provider screen, which shows each model with the name of the provider that serves it.

A model you pull after the box read Ollama's list still works. A call for an id the box does not know makes it read the lists again first.

A box with one provider works as it always did: every id goes to that provider, listed or not.

Pick a provider for each model in Studio

Every model picker in Studio picks a provider and a model.

Picker Where
An agent's model, each subagent's model, a workflow's model keys The Models block of the agent or workflow in Studio's catalogue
Default model Settings › Workspace
Guardrail classifier, Eval judge, Persona simulator Settings › Model defaults

A model that two providers serve shows once per provider, each row with its provider's name. A closed picker shows the model, then the provider, such as qwen3:4b · Ollama. A pick that names a provider goes to that provider, even when another one serves the same id.

A pick saved with no provider, such as one made while the box had only OpenAI, keeps working while one provider serves its id. When a second provider serves the same id, the picker shows no provider chosen and a warning under it: "Pick a provider, or every call to this model fails." Open the picker and choose the row of the provider you want.

Update the SDK for agent and workflow picks

Your workspace's @stackbone/sdk reads the picks of agents, subagents and workflow keys, and the Default model they fall back to. Only an SDK that reads the provider of a pick sends the call to that provider. An older one sends the model id alone. That still works while one provider serves the id, and fails with model_ambiguous once two do.

Update the SDK in your project:

pnpm add @stackbone/sdk@latest

On a box built with stackbone package, package and rebuild after the update, as Ship a change to a packaged box shows.

The guardrail classifier, the eval judge, the persona simulator and the Studio copilot run inside the box, which reads their picks itself. Their picks need no SDK update.

Choose embeddings per collection

Create a RAG collection in Studio with its own embedding provider and model. Two providers can serve the same model id, because the collection saves both identities. A keyless Ollama endpoint is valid. The box probes the selected model and saves its returned dimension before creating the configured collection. A failed probe on an updated box saves no configuration.

Uploads, Studio queries and an updated SDK use that saved configuration. A 1536-dimension collection and a 768-dimension collection can share the box. The collection details show hnsw or exact search as available. To move an existing collection between providers, click Change model and choose its target provider and embedding model. The model ID can stay the same when both providers serve it.

The box checks the saved source of every version before accepting the change. It then pauses queries and conflicting writes while it rebuilds published, unpublished and older versions. Studio shows the target and completed count. A failure retains that progress and keeps the collection paused. Fix the provider, then click Retry rebuild. The collection becomes available only after all versions finish with the target model. See Change a collection's model for source fallback and version guarantees.

Update the box and CLI before using collection model changes. The CLI supplies and compiles the managed rebuild workflow with the updated SDK. Then update the SDK in your local project and compile workflows that contain old RAG code again. A new runtime image can supply the SDK to a deep-agent bundle that keeps it external, without changing the bundle. New TypeScript calls that omit model also require updated SDK declarations in your local project. An older CLI may reject collection information that reports exact. See Configure a collection for override rules and the delivery requirements.

Fix the errors

The box refuses a call that no single provider can take, before it asks any provider. When the provider it picked does not answer, it says so too. Each error uses OpenAI's error shape and names the providers involved.

Status code When Fix
400 model_ambiguous Two or more providers list the id, and the call names no provider. The message ends "so the box cannot tell which one to call." Pick the model with its provider in Studio, and update the SDK. Or keep the model on one provider.
404 model_not_found The pick names a provider the box does not have, such as one removed in Studio. The message names it, "which is not configured on this box". Pick the model again in Studio.
404 model_not_found No provider lists the id. The message starts "No model provider on this box serves the model" and names the providers it checked. Write the id exactly as the provider's model list spells it.
503 model_provider_not_responding No provider lists the id, and a provider did not answer its last listing. That provider may serve the id, so the message names it. Open that provider's row, click Test connection to see why it fails, and bring it back.
503 model_provider_not_responding The pick names a provider other than the first, and the box could not read the providers you added. The message says the box "could not read its model providers". The box's own store of providers failed, not the provider. Check the box's logs.
502 model_provider_unreachable The call went to a provider, and the provider did not answer. The message names it. Check the base URL works from where the box runs.

A deep agent's turn fails with the box's message. A stackbone.ai call returns ai_validation_error for the 400 and ai_provider_error for the others, with the box's code in error.meta.openrouterCode.

Read more

  • Gateway: the first provider's form, and every caller that sends model calls through the box.
  • stackbone.models: how a pick in Studio outranks the model in your code.
  • Run on Ollama: Ollama on its own, the container address, and shipping a change to a packaged box.
  • Troubleshooting: when the box itself does not register.
BUILT WITH ❤️ FROM CANADA AND SPAIN