Skip to content

Let a reasoning model behind OPENAI_BASE_URL answer after its first tool call - #91

Open
chingizmamedov wants to merge 1 commit into
CopilotKit:mainfrom
chingizmamedov:fix/compatible-endpoint-store-false
Open

chingizmamedov wants to merge 1 commit into
CopilotKit:mainfrom
chingizmamedov:fix/compatible-endpoint-store-false

Conversation

@chingizmamedov

@chingizmamedov chingizmamedov commented Oct 28, 2026 •

Copy link
Copy Markdown

What this changes

With OPENAI_BASE_URL pointing at an OpenAI-compatible endpoint, a built-in Bot on a model that reasons before it answers calls its first tool and then fails the turn:

input[2]: unknown input item type: "item_reference"

I hit this running OpenDuet's own Bot on a local model: Ollama 0.32.4 serving gemma4:26b-mlx, configured as .env.example describes (OPENAI_BASE_URL=http://localhost:11434/v1, BOT_MODEL=gemma4:26b-mlx).

builtInAgentConfiguration hands the runtime an openai/<model> string. resolveModel turns that into createOpenAI({ baseURL: OPENAI_BASE_URL })(model), which uses the Responses API with store left at its default of true. On the second step the AI SDK then sends the first step's reasoning back as { type: "item_reference", id }, a pointer to a response it expects the server to have stored. OpenAI keeps that response; an endpoint like Ollama keeps nothing and refuses the request. Models that don't reason are unaffected, because nothing is referenced.

The fix passes providerOptions: { openai: { store: false } } when the built-in Bot's provider is OpenAI and OPENAI_BASE_URL is set, so the SDK sends each earlier item in full. It checks the same variable resolveModel reads to choose the endpoint, at the same moment. A plan model (a model sign-in) is left alone. Without OPENAI_BASE_URL, the configuration is exactly what it was: the existing configures an OpenAI built-in agent test still passes with toEqual.

Trade-off. On a gateway that fronts OpenAI itself, responses are no longer stored. With store: false, the AI SDK also drops reasoning that has no encrypted content, so a reasoning model's thinking isn't carried from one step of a turn to the next. Asking for include: ["reasoning.encrypted_content"] as well would keep it, and Ollama accepts that field. I left it out because I believe OpenAI rejects it for models that don't reason, which would break those behind a gateway. Happy to add it if that belief is wrong, or to gate this behind an explicit setting if you'd rather not change the default for gateways.

Where it belongs. The change is in OpenBot's platform code under openbot/, and the same lines exist on CopilotKit/openbot main. I'm happy to open the same change there instead, or as well, if that's where you'd like platform fixes to land first. I left openbot/CHANGELOG.md alone, since OpenDuet's PRs don't edit it; the OpenBot version of this PR would add a line under Unreleased.

Changes

  • openbot/server/src/copilot.ts: the store: false option, 11 lines.
  • openbot/server/tests/copilot.test.ts: a regression test, described below.
  • openbot/docs/configuration.md: one paragraph under "OpenAI-compatible endpoints" on what a compatible endpoint is sent.

Where it runs

  • New state that outlives a request? None. It's one static request option, chosen each time a built-in agent is built.
  • What happens on the second replica? The same thing: every process reads the same environment and builds the same option.
  • Boundary and audit: tool execution is untouched, so every acting call still goes through the gateway. There are no new refusals or failures, and nothing new is trusted from the client.

Proof

Regression test ("a compatible endpoint is sent the earlier step in full, not by reference"). It runs a built-in Bot against aimock, with a fixture that reasons and then calls a tool. A small proxy captures the raw Responses request bodies, because aimock's journal keeps a normalised copy without store or input. The test asserts that both requests carry store: false and that the second request's input has no item_reference. Without the fix, the store assertion fails, and so does the item_reference assertion on its own.

Checks (on this branch, in openbot/):

  • bun test passes 93 tests across copilot, task-skill-prompt and model-providers.
  • bun run format:check, lint, typecheck and typecheck:tests pass.
  • bun run typecheck in server/ passes.

By hand (same Ollama setup):

Result
Before reasoning → tool call → tool result → AI_APICallError: input[2]: unknown input item type: "item_reference"
After reasoning → tool call → tool result → reasoning → "You have 3 unread emails from Kenji." (2.5 s)

That run uses the AI SDK path the built-in Bot takes: createOpenAI(...)(model), streamText, one tool, three steps. In a running OpenDuet on the same model, a Chat turn that called the morning-brief tool now answers after the tool result.

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review 🔄 Running since 2026-10-28T19:29:01.813055Z 2af1741 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

…ool call

With OPENAI_BASE_URL set, a built-in Bot on a reasoning model (Gemma 4 on
Ollama, for one) called its first tool and then failed the turn with
"unknown input item type: item_reference". The AI SDK's Responses provider
defaults to store: true and sends the earlier step's reasoning back as an
item_reference to a stored response, which an endpoint other than OpenAI
does not keep.

The built-in Bot now passes providerOptions.openai.store = false when
OPENAI_BASE_URL is set, so each earlier item is sent in full. Without
OPENAI_BASE_URL nothing changes.
@chingizmamedov
chingizmamedov force-pushed the fix/compatible-endpoint-store-false branch from 2af1741 to adacc2a Compare October 28, 2026 19:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant