Skip to content

Commit e83def4

Browse files
fix(openai): default reasoning effort for mini models (#1824)
Co-authored-by: rosetta-livekit-bot[bot] <282703043+rosetta-livekit-bot[bot]@users.noreply.github.com> Co-authored-by: Chenghao Mou <chenghao.mou@livekit.io>
1 parent 8a91a0b commit e83def4

6 files changed

Lines changed: 62 additions & 6 deletions

File tree

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@livekit/agents-plugin-openai": patch
3+
---
4+
5+
Default OpenAI reasoning effort to `none` for `gpt-5.4-mini`. `*-chat-latest` models no longer send a default `reasoning_effort` (Python parity, and the API only accepts `medium` for them).

agents/src/inference/llm.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ export interface ChatCompletionOptions extends Record<string, unknown> {
8080
presence_penalty?: number;
8181
prompt_cache_key?: string;
8282
prompt_cache_retention?: 'in_memory' | '24h';
83-
reasoning_effort?: 'minimal' | 'low' | 'medium' | 'high';
83+
reasoning_effort?: 'none' | 'minimal' | 'low' | 'medium' | 'high';
8484
safety_identifier?: string;
8585
seed?: number;
8686
service_tier?: 'auto' | 'default' | 'flex' | 'scale' | 'priority';

plugins/openai/src/llm.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,12 @@ import type {
1212
MetaChatModels,
1313
OctoChatModels,
1414
PerplexityChatModels,
15+
ReasoningEffort,
1516
TelnyxChatModels,
1617
TogetherChatModels,
1718
XAIChatModels,
1819
} from './models.js';
20+
import { defaultReasoningEffort } from './models.js';
1921

2022
export interface LLMOptions {
2123
model: string | ChatModels;
@@ -31,6 +33,7 @@ export interface LLMOptions {
3133
maxCompletionTokens?: number;
3234
serviceTier?: string;
3335
store?: boolean;
36+
reasoningEffort?: ReasoningEffort;
3437
strictToolSchema?: boolean;
3538
}
3639

@@ -66,6 +69,7 @@ export class LLM extends llm.LLM {
6669
super();
6770

6871
this.#opts = { ...defaultLLMOptions, ...opts };
72+
this.#opts.reasoningEffort ??= defaultReasoningEffort(this.#opts.model);
6973
this.#providerFmt = providerFmt;
7074
if (this.#opts.apiKey === undefined && !this.#opts.client) {
7175
throw new Error('OpenAI API key is required, whether as an argument or as $OPENAI_API_KEY');
@@ -513,6 +517,10 @@ export class LLM extends llm.LLM {
513517
extras.store = this.#opts.store;
514518
}
515519

520+
if (this.#opts.reasoningEffort !== undefined) {
521+
extras.reasoning_effort = this.#opts.reasoningEffort;
522+
}
523+
516524
parallelToolCalls =
517525
parallelToolCalls !== undefined ? parallelToolCalls : this.#opts.parallelToolCalls;
518526
if (

plugins/openai/src/models.ts

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
export type ChatModels =
66
| 'gpt-5.4'
7+
| 'gpt-5.4-mini'
78
| 'gpt-5.3-chat-latest'
89
| 'gpt-5.2'
910
| 'gpt-5.2-chat-latest'
@@ -205,16 +206,30 @@ export type MetaChatModels =
205206
| 'Llama-3.3-70B-Instruct'
206207
| 'Llama-3.3-8B-Instruct';
207208

209+
export type ReasoningEffort = 'none' | 'minimal' | 'low' | 'medium' | 'high';
210+
211+
export type Reasoning = { effort?: ReasoningEffort | null; [key: string]: unknown };
212+
208213
export function supportsReasoningEffort(model: ChatModels | string): boolean {
209214
return [
210215
'gpt-5.4',
211-
'gpt-5.3-chat-latest',
216+
'gpt-5.4-mini',
212217
'gpt-5.2',
213-
'gpt-5.2-chat-latest',
214218
'gpt-5.1',
215-
'gpt-5.1-chat-latest',
216219
'gpt-5',
217220
'gpt-5-mini',
218221
'gpt-5-nano',
219222
].includes(model);
220223
}
224+
225+
export function defaultReasoningEffort(model: ChatModels | string): ReasoningEffort | undefined {
226+
if (!supportsReasoningEffort(model)) {
227+
return undefined;
228+
}
229+
230+
if (['gpt-5.1', 'gpt-5.2', 'gpt-5.4', 'gpt-5.4-mini'].includes(model)) {
231+
return 'none';
232+
}
233+
234+
return 'minimal';
235+
}

plugins/openai/src/responses/llm.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ import {
1212
toError,
1313
} from '@livekit/agents';
1414
import OpenAI from 'openai';
15-
import type { ChatModels } from '../models.js';
15+
import type { ChatModels, Reasoning } from '../models.js';
16+
import { defaultReasoningEffort } from '../models.js';
1617
import { toResponsesTools } from '../tool_utils.js';
1718
import { WSLLM } from '../ws/llm.js';
1819

@@ -31,6 +32,8 @@ export interface LLMOptions {
3132
serviceTier?: string;
3233
/** Upper bound for the number of tokens that can be generated for a response. */
3334
maxOutputTokens?: number;
35+
/** Configuration options for reasoning models. */
36+
reasoning?: Reasoning | null;
3437

3538
/**
3639
* Whether to use the WebSocket API.
@@ -56,6 +59,13 @@ class ResponsesHttpLLM extends llm.LLM {
5659
super();
5760

5861
this.#opts = { ...defaultLLMOptions, ...opts };
62+
if (this.#opts.reasoning === undefined) {
63+
const effort = defaultReasoningEffort(this.#opts.model);
64+
if (effort !== undefined) {
65+
this.#opts.reasoning = { effort };
66+
}
67+
}
68+
5969
if (this.#opts.apiKey === undefined && this.#opts.client === undefined) {
6070
throw new Error('OpenAI API key is required, whether as an argument or as $OPENAI_API_KEY');
6171
}
@@ -132,6 +142,10 @@ class ResponsesHttpLLM extends llm.LLM {
132142
modelOptions.max_output_tokens = this.#opts.maxOutputTokens;
133143
}
134144

145+
if (this.#opts.reasoning !== undefined) {
146+
modelOptions.reasoning = this.#opts.reasoning;
147+
}
148+
135149
return new ResponsesHttpLLMStream(this, {
136150
model: this.#opts.model,
137151
client: this.#client,

plugins/openai/src/ws/llm.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ import {
1414
} from '@livekit/agents';
1515
import type OpenAI from 'openai';
1616
import { WebSocket } from 'ws';
17-
import type { ChatModels } from '../models.js';
17+
import type { ChatModels, Reasoning } from '../models.js';
18+
import { defaultReasoningEffort } from '../models.js';
1819
import { toResponsesTools } from '../tool_utils.js';
1920
import type {
2021
WsOutputItemDoneEvent,
@@ -170,6 +171,8 @@ export interface WSLLMOptions {
170171
serviceTier?: string;
171172
/** Upper bound for the number of tokens that can be generated for a response. */
172173
maxOutputTokens?: number;
174+
/** Configuration options for reasoning models. */
175+
reasoning?: Reasoning | null;
173176
}
174177

175178
const defaultLLMOptions: WSLLMOptions = {
@@ -204,6 +207,13 @@ export class WSLLM extends llm.LLM {
204207
super();
205208

206209
this.#opts = { ...defaultLLMOptions, ...opts };
210+
if (this.#opts.reasoning === undefined) {
211+
const effort = defaultReasoningEffort(this.#opts.model);
212+
if (effort !== undefined) {
213+
this.#opts.reasoning = { effort };
214+
}
215+
}
216+
207217
if (!this.#opts.apiKey) {
208218
throw new Error('OpenAI API key is required, whether as an argument or as $OPENAI_API_KEY');
209219
}
@@ -306,6 +316,10 @@ export class WSLLM extends llm.LLM {
306316
modelOptions.max_output_tokens = this.#opts.maxOutputTokens;
307317
}
308318

319+
if (this.#opts.reasoning !== undefined) {
320+
modelOptions.reasoning = this.#opts.reasoning;
321+
}
322+
309323
let inputChatCtx = chatCtx;
310324
let prevResponseId: string | undefined;
311325
const canUseStoredResponse = modelOptions.store !== false;

0 commit comments

Comments
 (0)