Summary
When building a ThinkAgent that wants both server-side tool calls and structured (JSON-schema-validated) output on the final turn, today the Vercel AI SDK's experimental_output / output option is unreachable because Think's TurnConfig doesn't forward it into the wrapped streamText call.
Why this matters
Vercel AI SDK supports the pattern:
Call tools during intermediate turns; on the final turn, switch to structured-output mode so the response is guaranteed to match a schema.
This is the canonical fix for the well-known "model emits prose around JSON" failure on tool-calling agents. On Workers AI (via workers-ai-provider), responseFormat: { type: "json", schema } works but forcibly strips tools — so it can't be enabled at model-construction time for a tool-using agent. The AI SDK's per-call experimental_output avoids this by only applying structured output on the non-tool-calling terminal turn.
Think's TurnConfig (packages/think v0.2.4 surface) exposes model, system, messages, tools, activeTools, toolChoice, maxSteps, providerOptions — but no output / experimental_output. As a result, agents authored on Think fall back to prompt-level nudges ("respond with raw JSON only, no markdown fences") which fail ~10–20% of the time even on well-behaved models.
We hit this in production on the GridLink flapping-charger diagnostic agent: Kimi K2.5 frequently wraps its final answer in a ```json fence or a short apology, which trips our outputSchema validation downstream. We currently work around it with a second extractor call (no tools, Output.object()), but the right place for the fix is Think.
Proposed change
Add output (and experimental_output for legacy parity) to the TurnConfig interface:
interface TurnConfig {
model?: LanguageModel;
system?: string;
messages?: ModelMessage[];
tools?: ToolSet;
activeTools?: string[];
toolChoice?: Parameters<typeof streamText>[0]['toolChoice'];
maxSteps?: number;
providerOptions?: Record<string, unknown>;
// NEW
output?: Parameters<typeof streamText>[0]['output'];
experimental_output?: Parameters<typeof streamText>[0]['experimental_output'];
}
…and forward them in the streamText(...) call inside chat(). Both fields are already strongly typed by the AI SDK; Think can passthrough without needing its own validation layer.
Backward compatibility: both fields are optional, so existing callers see no change.
Reference
Happy to open a PR if the direction looks right.
Summary
When building a
ThinkAgentthat wants both server-side tool calls and structured (JSON-schema-validated) output on the final turn, today the Vercel AI SDK'sexperimental_output/outputoption is unreachable because Think'sTurnConfigdoesn't forward it into the wrappedstreamTextcall.Why this matters
Vercel AI SDK supports the pattern:
This is the canonical fix for the well-known "model emits prose around JSON" failure on tool-calling agents. On Workers AI (via
workers-ai-provider),responseFormat: { type: "json", schema }works but forcibly stripstools— so it can't be enabled at model-construction time for a tool-using agent. The AI SDK's per-callexperimental_outputavoids this by only applying structured output on the non-tool-calling terminal turn.Think's
TurnConfig(packages/think v0.2.4 surface) exposesmodel,system,messages,tools,activeTools,toolChoice,maxSteps,providerOptions— but nooutput/experimental_output. As a result, agents authored on Think fall back to prompt-level nudges ("respond with raw JSON only, no markdown fences") which fail ~10–20% of the time even on well-behaved models.We hit this in production on the GridLink flapping-charger diagnostic agent: Kimi K2.5 frequently wraps its final answer in a
```jsonfence or a short apology, which trips our outputSchema validation downstream. We currently work around it with a second extractor call (no tools,Output.object()), but the right place for the fix is Think.Proposed change
Add
output(andexperimental_outputfor legacy parity) to theTurnConfiginterface:…and forward them in the
streamText(...)call insidechat(). Both fields are already strongly typed by the AI SDK; Think can passthrough without needing its own validation layer.Backward compatibility: both fields are optional, so existing callers see no change.
Reference
experimental_output/Output.object({ schema }): https://sdk.vercel.ai/docs/ai-sdk-core/generating-structured-data#structured-outputs-with-toolcallingworkers-ai-providerbehavior whenresponseFormat.type === "json": https://github.com/cloudflare/ai/blob/main/packages/workers-ai-provider/src/workersai-chat-language-model.tsHappy to open a PR if the direction looks right.