@aigne/openai 0.16.4-beta.3 → 0.16.4-beta.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,32 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.16.4-beta.5](https://github.com/AIGNE-io/aigne-framework/compare/openai-v0.16.4-beta.4...openai-v0.16.4-beta.5) (2025-10-29)
4
+
5
+
6
+ ### Features
7
+
8
+ * add reasoningEffort option for chat model ([#680](https://github.com/AIGNE-io/aigne-framework/issues/680)) ([f69d232](https://github.com/AIGNE-io/aigne-framework/commit/f69d232d714d4a3e4946bdc8c6598747c9bcbd57))
9
+
10
+
11
+ ### Dependencies
12
+
13
+ * The following workspace dependencies were updated
14
+ * dependencies
15
+ * @aigne/core bumped to 1.65.0-beta.4
16
+ * devDependencies
17
+ * @aigne/test-utils bumped to 0.5.57-beta.5
18
+
19
+ ## [0.16.4-beta.4](https://github.com/AIGNE-io/aigne-framework/compare/openai-v0.16.4-beta.3...openai-v0.16.4-beta.4) (2025-10-28)
20
+
21
+
22
+ ### Dependencies
23
+
24
+ * The following workspace dependencies were updated
25
+ * dependencies
26
+ * @aigne/core bumped to 1.65.0-beta.3
27
+ * devDependencies
28
+ * @aigne/test-utils bumped to 0.5.57-beta.4
29
+
3
30
  ## [0.16.4-beta.3](https://github.com/AIGNE-io/aigne-framework/compare/openai-v0.16.4-beta.2...openai-v0.16.4-beta.3) (2025-10-28)
4
31
 
5
32
 
@@ -1,4 +1,4 @@
1
- import { type AgentInvokeOptions, type AgentProcessResult, ChatModel, type ChatModelInput, type ChatModelInputMessage, type ChatModelInputTool, type ChatModelOptions, type ChatModelOutput } from "@aigne/core";
1
+ import { type AgentInvokeOptions, type AgentProcessResult, ChatModel, type ChatModelInput, type ChatModelInputMessage, type ChatModelInputOptions, type ChatModelInputTool, type ChatModelOptions, type ChatModelOutput } from "@aigne/core";
2
2
  import { type PromiseOrValue } from "@aigne/core/utils/type-utils.js";
3
3
  import type { ClientOptions, OpenAI } from "openai";
4
4
  import type { ChatCompletionMessageParam, ChatCompletionTool } from "openai/resources";
@@ -127,13 +127,14 @@ export declare class OpenAIChatModel extends ChatModel {
127
127
  apiKey: string | undefined;
128
128
  model: string;
129
129
  };
130
- get modelOptions(): Omit<import("@aigne/core").ChatModelInputOptions, "model"> | undefined;
130
+ get modelOptions(): Omit<ChatModelInputOptions, "model"> | undefined;
131
131
  /**
132
132
  * Process the input and generate a response
133
133
  * @param input The input to process
134
134
  * @returns The generated response
135
135
  */
136
136
  process(input: ChatModelInput, _options: AgentInvokeOptions): PromiseOrValue<AgentProcessResult<ChatModelOutput>>;
137
+ private getReasoningEffort;
137
138
  private _process;
138
139
  private getParallelToolCalls;
139
140
  protected getRunMessages(input: ChatModelInput): Promise<ChatCompletionMessageParam[]>;
@@ -105,6 +105,20 @@ class OpenAIChatModel extends core_1.ChatModel {
105
105
  process(input, _options) {
106
106
  return this._process(input);
107
107
  }
108
+ getReasoningEffort(effort) {
109
+ if (typeof effort === "number") {
110
+ if (effort > 5000)
111
+ return "high";
112
+ if (effort > 1000)
113
+ return "medium";
114
+ if (effort > 500)
115
+ return "low";
116
+ if (effort > 0)
117
+ return "minimal";
118
+ return undefined;
119
+ }
120
+ return effort;
121
+ }
108
122
  async _process(input) {
109
123
  const messages = await this.getRunMessages(input);
110
124
  const model = input.modelOptions?.model || this.credential.model;
@@ -121,6 +135,7 @@ class OpenAIChatModel extends core_1.ChatModel {
121
135
  include_usage: true,
122
136
  },
123
137
  stream: true,
138
+ reasoning_effort: this.getReasoningEffort(input.modelOptions?.reasoningEffort ?? this.modelOptions?.reasoningEffort),
124
139
  };
125
140
  // For models that do not support tools use with JSON schema in same request,
126
141
  // so we need to handle the case where tools are not used and responseFormat is json
@@ -138,9 +153,9 @@ class OpenAIChatModel extends core_1.ChatModel {
138
153
  response_format: responseFormat,
139
154
  }));
140
155
  if (input.responseFormat?.type !== "json_schema") {
141
- return await this.extractResultFromStream(stream, false, true);
156
+ return await this.extractResultFromStream(body, stream, false, true);
142
157
  }
143
- const result = await this.extractResultFromStream(stream, jsonMode);
158
+ const result = await this.extractResultFromStream(body, stream, jsonMode);
144
159
  // Just return the result if it has tool calls
145
160
  if (result.toolCalls?.length || result.json)
146
161
  return result;
@@ -214,18 +229,28 @@ class OpenAIChatModel extends core_1.ChatModel {
214
229
  ...body,
215
230
  response_format: resolvedResponseFormat,
216
231
  }));
217
- return this.extractResultFromStream(res, jsonMode);
232
+ return this.extractResultFromStream(body, res, jsonMode);
218
233
  }
219
- async extractResultFromStream(stream, jsonMode, streaming) {
234
+ async extractResultFromStream(body, stream, jsonMode, streaming) {
220
235
  const result = new ReadableStream({
221
236
  start: async (controller) => {
222
237
  try {
238
+ controller.enqueue({
239
+ delta: {
240
+ json: {
241
+ modelOptions: {
242
+ reasoningEffort: body.reasoning_effort,
243
+ },
244
+ },
245
+ },
246
+ });
223
247
  let text = "";
224
248
  let refusal = "";
225
249
  const toolCalls = [];
226
250
  let model;
227
251
  for await (const chunk of stream) {
228
252
  const choice = chunk.choices?.[0];
253
+ const delta = choice?.delta;
229
254
  if (!model) {
230
255
  model = chunk.model;
231
256
  controller.enqueue({
@@ -236,8 +261,8 @@ class OpenAIChatModel extends core_1.ChatModel {
236
261
  },
237
262
  });
238
263
  }
239
- if (choice?.delta.tool_calls?.length) {
240
- for (const call of choice.delta.tool_calls) {
264
+ if (delta?.tool_calls?.length) {
265
+ for (const call of delta.tool_calls) {
241
266
  if (this.supportsToolStreaming && call.index !== undefined) {
242
267
  handleToolCallDelta(toolCalls, call);
243
268
  }
@@ -246,27 +271,23 @@ class OpenAIChatModel extends core_1.ChatModel {
246
271
  }
247
272
  }
248
273
  }
249
- if (choice?.delta.content) {
250
- text += choice.delta.content;
274
+ if (delta && "reasoning" in delta && typeof delta.reasoning === "string") {
275
+ controller.enqueue({ delta: { text: { thoughts: delta.reasoning } } });
276
+ }
277
+ if (delta?.content) {
278
+ text += delta.content;
251
279
  if (!jsonMode) {
252
280
  controller.enqueue({
253
281
  delta: {
254
282
  text: {
255
- text: choice.delta.content,
283
+ text: delta.content,
256
284
  },
257
285
  },
258
286
  });
259
287
  }
260
288
  }
261
- if (choice?.delta.refusal) {
262
- refusal += choice.delta.refusal;
263
- if (!jsonMode) {
264
- controller.enqueue({
265
- delta: {
266
- text: { text: choice.delta.refusal },
267
- },
268
- });
269
- }
289
+ if (delta?.refusal) {
290
+ refusal += delta.refusal;
270
291
  }
271
292
  if (chunk.usage) {
272
293
  controller.enqueue({
@@ -281,7 +302,6 @@ class OpenAIChatModel extends core_1.ChatModel {
281
302
  });
282
303
  }
283
304
  }
284
- text = text || refusal;
285
305
  if (jsonMode && text) {
286
306
  controller.enqueue({
287
307
  delta: {
@@ -303,6 +323,9 @@ class OpenAIChatModel extends core_1.ChatModel {
303
323
  },
304
324
  });
305
325
  }
326
+ if (refusal) {
327
+ controller.error(new Error(`Got refusal from LLM: ${refusal}`));
328
+ }
306
329
  controller.close();
307
330
  }
308
331
  catch (error) {
@@ -1,4 +1,4 @@
1
- import { type AgentInvokeOptions, type AgentProcessResult, ChatModel, type ChatModelInput, type ChatModelInputMessage, type ChatModelInputTool, type ChatModelOptions, type ChatModelOutput } from "@aigne/core";
1
+ import { type AgentInvokeOptions, type AgentProcessResult, ChatModel, type ChatModelInput, type ChatModelInputMessage, type ChatModelInputOptions, type ChatModelInputTool, type ChatModelOptions, type ChatModelOutput } from "@aigne/core";
2
2
  import { type PromiseOrValue } from "@aigne/core/utils/type-utils.js";
3
3
  import type { ClientOptions, OpenAI } from "openai";
4
4
  import type { ChatCompletionMessageParam, ChatCompletionTool } from "openai/resources";
@@ -127,13 +127,14 @@ export declare class OpenAIChatModel extends ChatModel {
127
127
  apiKey: string | undefined;
128
128
  model: string;
129
129
  };
130
- get modelOptions(): Omit<import("@aigne/core").ChatModelInputOptions, "model"> | undefined;
130
+ get modelOptions(): Omit<ChatModelInputOptions, "model"> | undefined;
131
131
  /**
132
132
  * Process the input and generate a response
133
133
  * @param input The input to process
134
134
  * @returns The generated response
135
135
  */
136
136
  process(input: ChatModelInput, _options: AgentInvokeOptions): PromiseOrValue<AgentProcessResult<ChatModelOutput>>;
137
+ private getReasoningEffort;
137
138
  private _process;
138
139
  private getParallelToolCalls;
139
140
  protected getRunMessages(input: ChatModelInput): Promise<ChatCompletionMessageParam[]>;
@@ -1,4 +1,4 @@
1
- import { type AgentInvokeOptions, type AgentProcessResult, ChatModel, type ChatModelInput, type ChatModelInputMessage, type ChatModelInputTool, type ChatModelOptions, type ChatModelOutput } from "@aigne/core";
1
+ import { type AgentInvokeOptions, type AgentProcessResult, ChatModel, type ChatModelInput, type ChatModelInputMessage, type ChatModelInputOptions, type ChatModelInputTool, type ChatModelOptions, type ChatModelOutput } from "@aigne/core";
2
2
  import { type PromiseOrValue } from "@aigne/core/utils/type-utils.js";
3
3
  import type { ClientOptions, OpenAI } from "openai";
4
4
  import type { ChatCompletionMessageParam, ChatCompletionTool } from "openai/resources";
@@ -127,13 +127,14 @@ export declare class OpenAIChatModel extends ChatModel {
127
127
  apiKey: string | undefined;
128
128
  model: string;
129
129
  };
130
- get modelOptions(): Omit<import("@aigne/core").ChatModelInputOptions, "model"> | undefined;
130
+ get modelOptions(): Omit<ChatModelInputOptions, "model"> | undefined;
131
131
  /**
132
132
  * Process the input and generate a response
133
133
  * @param input The input to process
134
134
  * @returns The generated response
135
135
  */
136
136
  process(input: ChatModelInput, _options: AgentInvokeOptions): PromiseOrValue<AgentProcessResult<ChatModelOutput>>;
137
+ private getReasoningEffort;
137
138
  private _process;
138
139
  private getParallelToolCalls;
139
140
  protected getRunMessages(input: ChatModelInput): Promise<ChatCompletionMessageParam[]>;
@@ -100,6 +100,20 @@ export class OpenAIChatModel extends ChatModel {
100
100
  process(input, _options) {
101
101
  return this._process(input);
102
102
  }
103
+ getReasoningEffort(effort) {
104
+ if (typeof effort === "number") {
105
+ if (effort > 5000)
106
+ return "high";
107
+ if (effort > 1000)
108
+ return "medium";
109
+ if (effort > 500)
110
+ return "low";
111
+ if (effort > 0)
112
+ return "minimal";
113
+ return undefined;
114
+ }
115
+ return effort;
116
+ }
103
117
  async _process(input) {
104
118
  const messages = await this.getRunMessages(input);
105
119
  const model = input.modelOptions?.model || this.credential.model;
@@ -116,6 +130,7 @@ export class OpenAIChatModel extends ChatModel {
116
130
  include_usage: true,
117
131
  },
118
132
  stream: true,
133
+ reasoning_effort: this.getReasoningEffort(input.modelOptions?.reasoningEffort ?? this.modelOptions?.reasoningEffort),
119
134
  };
120
135
  // For models that do not support tools use with JSON schema in same request,
121
136
  // so we need to handle the case where tools are not used and responseFormat is json
@@ -133,9 +148,9 @@ export class OpenAIChatModel extends ChatModel {
133
148
  response_format: responseFormat,
134
149
  }));
135
150
  if (input.responseFormat?.type !== "json_schema") {
136
- return await this.extractResultFromStream(stream, false, true);
151
+ return await this.extractResultFromStream(body, stream, false, true);
137
152
  }
138
- const result = await this.extractResultFromStream(stream, jsonMode);
153
+ const result = await this.extractResultFromStream(body, stream, jsonMode);
139
154
  // Just return the result if it has tool calls
140
155
  if (result.toolCalls?.length || result.json)
141
156
  return result;
@@ -209,18 +224,28 @@ export class OpenAIChatModel extends ChatModel {
209
224
  ...body,
210
225
  response_format: resolvedResponseFormat,
211
226
  }));
212
- return this.extractResultFromStream(res, jsonMode);
227
+ return this.extractResultFromStream(body, res, jsonMode);
213
228
  }
214
- async extractResultFromStream(stream, jsonMode, streaming) {
229
+ async extractResultFromStream(body, stream, jsonMode, streaming) {
215
230
  const result = new ReadableStream({
216
231
  start: async (controller) => {
217
232
  try {
233
+ controller.enqueue({
234
+ delta: {
235
+ json: {
236
+ modelOptions: {
237
+ reasoningEffort: body.reasoning_effort,
238
+ },
239
+ },
240
+ },
241
+ });
218
242
  let text = "";
219
243
  let refusal = "";
220
244
  const toolCalls = [];
221
245
  let model;
222
246
  for await (const chunk of stream) {
223
247
  const choice = chunk.choices?.[0];
248
+ const delta = choice?.delta;
224
249
  if (!model) {
225
250
  model = chunk.model;
226
251
  controller.enqueue({
@@ -231,8 +256,8 @@ export class OpenAIChatModel extends ChatModel {
231
256
  },
232
257
  });
233
258
  }
234
- if (choice?.delta.tool_calls?.length) {
235
- for (const call of choice.delta.tool_calls) {
259
+ if (delta?.tool_calls?.length) {
260
+ for (const call of delta.tool_calls) {
236
261
  if (this.supportsToolStreaming && call.index !== undefined) {
237
262
  handleToolCallDelta(toolCalls, call);
238
263
  }
@@ -241,27 +266,23 @@ export class OpenAIChatModel extends ChatModel {
241
266
  }
242
267
  }
243
268
  }
244
- if (choice?.delta.content) {
245
- text += choice.delta.content;
269
+ if (delta && "reasoning" in delta && typeof delta.reasoning === "string") {
270
+ controller.enqueue({ delta: { text: { thoughts: delta.reasoning } } });
271
+ }
272
+ if (delta?.content) {
273
+ text += delta.content;
246
274
  if (!jsonMode) {
247
275
  controller.enqueue({
248
276
  delta: {
249
277
  text: {
250
- text: choice.delta.content,
278
+ text: delta.content,
251
279
  },
252
280
  },
253
281
  });
254
282
  }
255
283
  }
256
- if (choice?.delta.refusal) {
257
- refusal += choice.delta.refusal;
258
- if (!jsonMode) {
259
- controller.enqueue({
260
- delta: {
261
- text: { text: choice.delta.refusal },
262
- },
263
- });
264
- }
284
+ if (delta?.refusal) {
285
+ refusal += delta.refusal;
265
286
  }
266
287
  if (chunk.usage) {
267
288
  controller.enqueue({
@@ -276,7 +297,6 @@ export class OpenAIChatModel extends ChatModel {
276
297
  });
277
298
  }
278
299
  }
279
- text = text || refusal;
280
300
  if (jsonMode && text) {
281
301
  controller.enqueue({
282
302
  delta: {
@@ -298,6 +318,9 @@ export class OpenAIChatModel extends ChatModel {
298
318
  },
299
319
  });
300
320
  }
321
+ if (refusal) {
322
+ controller.error(new Error(`Got refusal from LLM: ${refusal}`));
323
+ }
301
324
  controller.close();
302
325
  }
303
326
  catch (error) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aigne/openai",
3
- "version": "0.16.4-beta.3",
3
+ "version": "0.16.4-beta.5",
4
4
  "description": "AIGNE OpenAI SDK for integrating with OpenAI's GPT models and API services",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -38,7 +38,7 @@
38
38
  "@aigne/uuid": "^13.0.1",
39
39
  "openai": "^6.5.0",
40
40
  "zod": "^3.25.67",
41
- "@aigne/core": "^1.65.0-beta.2",
41
+ "@aigne/core": "^1.65.0-beta.4",
42
42
  "@aigne/platform-helpers": "^0.6.3"
43
43
  },
44
44
  "devDependencies": {
@@ -47,7 +47,7 @@
47
47
  "npm-run-all": "^4.1.5",
48
48
  "rimraf": "^6.0.1",
49
49
  "typescript": "^5.9.2",
50
- "@aigne/test-utils": "^0.5.57-beta.3"
50
+ "@aigne/test-utils": "^0.5.57-beta.5"
51
51
  },
52
52
  "scripts": {
53
53
  "lint": "tsc --noEmit",