Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .changeset/fix-reasoning-streaming.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
'@tanstack/ai-openai': patch
---

Fix reasoning token streaming for `gpt-5-mini` and `gpt-5-nano` models

- Added `OpenAIReasoningOptions` to type definitions for `gpt-5-mini` and `gpt-5-nano` models
- Fixed `summary` option placement in `OpenAIReasoningOptions` (moved inside `reasoning` object to match OpenAI SDK)
- Added handler for `response.reasoning_summary_text.delta` events to stream reasoning summaries
- Added model-specific `reasoning.summary` types: `concise` only available for `computer-use-preview`
- Added `OpenAIReasoningOptionsWithConcise` for `computer-use-preview` model
3 changes: 2 additions & 1 deletion docs/adapters/openai.md
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,8 @@ Enable reasoning for models that support it (e.g., GPT-5). This allows the model
```typescript
providerOptions: {
reasoning: {
effort: "medium", // "minimal" | "low" | "medium" | "high"
effort: "medium", // "none" | "minimal" | "low" | "medium" | "high"
summary: "detailed", // "auto" | "detailed" (optional)
},
}
```
Expand Down
7 changes: 7 additions & 0 deletions packages/typescript/ai-openai/src/model-meta.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type {
OpenAIBaseOptions,
OpenAIMetadataOptions,
OpenAIReasoningOptions,
OpenAIReasoningOptionsWithConcise,
OpenAIStreamingOptions,
OpenAIStructuredOutputOptions,
OpenAIToolsOptions,
Expand Down Expand Up @@ -198,6 +199,7 @@ const GPT5_MINI = {
},
} as const satisfies ModelMeta<
OpenAIBaseOptions &
OpenAIReasoningOptions &
OpenAIStructuredOutputOptions &
OpenAIToolsOptions &
OpenAIStreamingOptions &
Expand Down Expand Up @@ -233,6 +235,7 @@ const GPT5_NANO = {
},
} as const satisfies ModelMeta<
OpenAIBaseOptions &
OpenAIReasoningOptions &
OpenAIStructuredOutputOptions &
OpenAIToolsOptions &
OpenAIStreamingOptions &
Expand Down Expand Up @@ -815,6 +818,7 @@ const COMPUTER_USE_PREVIEW = {
},
} as const satisfies ModelMeta<
OpenAIBaseOptions &
OpenAIReasoningOptionsWithConcise &
OpenAIToolsOptions &
OpenAIStreamingOptions &
OpenAIMetadataOptions
Expand Down Expand Up @@ -1787,11 +1791,13 @@ export type OpenAIChatModelProviderOptionsByName = {
OpenAIStreamingOptions &
OpenAIMetadataOptions
[GPT5_MINI.name]: OpenAIBaseOptions &
OpenAIReasoningOptions &
OpenAIStructuredOutputOptions &
OpenAIToolsOptions &
OpenAIStreamingOptions &
OpenAIMetadataOptions
[GPT5_NANO.name]: OpenAIBaseOptions &
OpenAIReasoningOptions &
OpenAIStructuredOutputOptions &
OpenAIToolsOptions &
OpenAIStreamingOptions &
Expand Down Expand Up @@ -1920,6 +1926,7 @@ export type OpenAIChatModelProviderOptionsByName = {

// Special models
[COMPUTER_USE_PREVIEW.name]: OpenAIBaseOptions &
OpenAIReasoningOptionsWithConcise &
OpenAIToolsOptions &
OpenAIStreamingOptions &
OpenAIMetadataOptions
Expand Down
23 changes: 23 additions & 0 deletions packages/typescript/ai-openai/src/openai-adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,29 @@ export class OpenAI extends BaseAdapter<
}
}

// Handle reasoning summary deltas (when using reasoning.summary option)
// response.reasoning_summary_text.delta provides incremental summary updates
if (
chunk.type === 'response.reasoning_summary_text.delta' &&
chunk.delta
) {
const summaryDelta =
typeof chunk.delta === 'string' ? chunk.delta : ''

if (summaryDelta) {
accumulatedReasoning += summaryDelta
hasStreamedReasoningDeltas = true
yield {
type: 'thinking',
id: responseId || generateId(),
model: model || options.model,
timestamp,
delta: summaryDelta,
content: accumulatedReasoning,
}
}
}

// handle content_part added events for text, reasoning and refusals
if (chunk.type === 'response.content_part.added') {
const contentPart = chunk.part
Expand Down
42 changes: 38 additions & 4 deletions packages/typescript/ai-openai/src/text/text-provider-options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,14 @@ https://platform.openai.com/docs/api-reference/responses/create#responses_create
}

// Feature fragments that can be stitched per-model

// Shared base types for reasoning options
type ReasoningEffort = 'none' | 'minimal' | 'low' | 'medium' | 'high'
type ReasoningSummary = 'auto' | 'detailed'

/**
* Reasoning options for most models (excludes 'concise' summary).
*/
export interface OpenAIReasoningOptions {
/**
* Reasoning controls for models that support it.
Expand All @@ -138,13 +146,39 @@ export interface OpenAIReasoningOptions {
* All models before gpt-5.1 default to medium reasoning effort, and do not support none.
* The gpt-5-pro model defaults to (and only supports) high reasoning effort.
*/
effort?: 'none' | 'minimal' | 'low' | 'medium' | 'high'
effort?: ReasoningEffort
/**
* A summary of the reasoning performed by the model. This can be useful for debugging and understanding the model's reasoning process.
* https://platform.openai.com/docs/api-reference/responses/create#responses_create-reasoning-summary
*/
summary?: ReasoningSummary
}
}

/**
* Reasoning options for computer-use-preview model (includes 'concise' summary).
*/
export interface OpenAIReasoningOptionsWithConcise {
/**
* A summary of the reasoning performed by the model. This can be useful for debugging and understanding the model's reasoning process
* https://platform.openai.com/docs/api-reference/responses/create#responses_create-reasoning-summary
* Reasoning controls for models that support it.
* Lets you guide how much chain-of-thought computation to spend.
* https://platform.openai.com/docs/api-reference/responses/create#responses_create-reasoning
* https://platform.openai.com/docs/guides/reasoning
*/
summary?: 'auto' | 'concise' | 'detailed'
reasoning?: {
/**
* gpt-5.1 defaults to none, which does not perform reasoning. The supported reasoning values for gpt-5.1 are none, low, medium, and high. Tool calls are supported for all reasoning values in gpt-5.1.
* All models before gpt-5.1 default to medium reasoning effort, and do not support none.
* The gpt-5-pro model defaults to (and only supports) high reasoning effort.
*/
effort?: ReasoningEffort
/**
* A summary of the reasoning performed by the model. This can be useful for debugging and understanding the model's reasoning process.
* `concise` is only supported for `computer-use-preview` models.
* https://platform.openai.com/docs/api-reference/responses/create#responses_create-reasoning-summary
*/
summary?: ReasoningSummary | 'concise'
}
}

export interface OpenAIStructuredOutputOptions {
Expand Down
32 changes: 18 additions & 14 deletions packages/typescript/ai-openai/tests/model-meta.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import type {
import type {
OpenAIBaseOptions,
OpenAIReasoningOptions,
OpenAIReasoningOptionsWithConcise,
OpenAIStructuredOutputOptions,
OpenAIToolsOptions,
OpenAIStreamingOptions,
Expand Down Expand Up @@ -111,12 +112,12 @@ describe('OpenAI Chat Model Provider Options Type Assertions', () => {
})
})

describe('Models WITH structured output AND tools but WITHOUT reasoning (Standard)', () => {
it('gpt-5-mini should have structured output and tools but NOT reasoning', () => {
describe('Models WITH reasoning AND structured output AND tools (gpt-5-mini/nano)', () => {
it('gpt-5-mini should have reasoning, structured output and tools', () => {
type Options = OpenAIChatModelProviderOptionsByName['gpt-5-mini']

// Should NOT have reasoning options
expectTypeOf<Options>().not.toExtend<OpenAIReasoningOptions>()
// Should have reasoning options
expectTypeOf<Options>().toExtend<OpenAIReasoningOptions>()

// Should have structured output options
expectTypeOf<Options>().toExtend<OpenAIStructuredOutputOptions>()
Expand All @@ -131,16 +132,18 @@ describe('OpenAI Chat Model Provider Options Type Assertions', () => {
expectTypeOf<Options>().toExtend<BaseOptions>()
})

it('gpt-5-nano should have structured output and tools but NOT reasoning', () => {
it('gpt-5-nano should have reasoning, structured output and tools', () => {
type Options = OpenAIChatModelProviderOptionsByName['gpt-5-nano']

expectTypeOf<Options>().not.toExtend<OpenAIReasoningOptions>()
expectTypeOf<Options>().toExtend<OpenAIReasoningOptions>()
expectTypeOf<Options>().toExtend<OpenAIStructuredOutputOptions>()
expectTypeOf<Options>().toExtend<OpenAIToolsOptions>()
expectTypeOf<Options>().toExtend<OpenAIStreamingOptions>()
expectTypeOf<Options>().toExtend<BaseOptions>()
})
})

describe('Models WITH structured output AND tools but WITHOUT reasoning (Standard)', () => {
it('gpt-5-codex should have structured output and tools but NOT reasoning', () => {
type Options = OpenAIChatModelProviderOptionsByName['gpt-5-codex']

Expand Down Expand Up @@ -439,11 +442,12 @@ describe('OpenAI Chat Model Provider Options Type Assertions', () => {
expectTypeOf<Options>().toExtend<BaseOptions>()
})

it('computer-use-preview should have tools but NOT structured output', () => {
it('computer-use-preview should have tools and reasoning with concise but NOT structured output', () => {
type Options =
OpenAIChatModelProviderOptionsByName['computer-use-preview']

expectTypeOf<Options>().not.toExtend<OpenAIReasoningOptions>()
// Should have reasoning options with 'concise' summary support
expectTypeOf<Options>().toExtend<OpenAIReasoningOptionsWithConcise>()
expectTypeOf<Options>().not.toExtend<OpenAIStructuredOutputOptions>()
expectTypeOf<Options>().toExtend<OpenAIToolsOptions>()
expectTypeOf<Options>().toExtend<OpenAIStreamingOptions>()
Expand Down Expand Up @@ -698,13 +702,16 @@ describe('OpenAI Chat Model Provider Options Type Assertions', () => {
>().toExtend<FullFeaturedOptions>()
})

it('standard models should NOT extend reasoning options but should extend structured output and tools', () => {
it('gpt-5-mini and gpt-5-nano should extend FullFeaturedOptions (reasoning + structured output + tools)', () => {
expectTypeOf<
OpenAIChatModelProviderOptionsByName['gpt-5-mini']
>().toExtend<StandardOptions>()
>().toExtend<FullFeaturedOptions>()
expectTypeOf<
OpenAIChatModelProviderOptionsByName['gpt-5-nano']
>().toExtend<StandardOptions>()
>().toExtend<FullFeaturedOptions>()
})

it('standard models should NOT extend reasoning options but should extend structured output and tools', () => {
expectTypeOf<
OpenAIChatModelProviderOptionsByName['gpt-4.1']
>().toExtend<StandardOptions>()
Expand All @@ -719,9 +726,6 @@ describe('OpenAI Chat Model Provider Options Type Assertions', () => {
>().toExtend<StandardOptions>()

// Verify these do NOT extend reasoning options (discrimination already tested above)
expectTypeOf<
OpenAIChatModelProviderOptionsByName['gpt-5-mini']
>().not.toExtend<OpenAIReasoningOptions>()
expectTypeOf<
OpenAIChatModelProviderOptionsByName['gpt-4.1']
>().not.toExtend<OpenAIReasoningOptions>()
Expand Down
Loading