@aigne/openai 0.14.1 → 0.14.3
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,37 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.14.3](https://github.com/AIGNE-io/aigne-framework/compare/openai-v0.14.2...openai-v0.14.3) (2025-09-08)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Bug Fixes
|
|
7
|
+
|
|
8
|
+
* support optional field sturectured output for gemini ([#468](https://github.com/AIGNE-io/aigne-framework/issues/468)) ([70c6279](https://github.com/AIGNE-io/aigne-framework/commit/70c62795039a2862e3333f26707329489bf938de))
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Dependencies
|
|
12
|
+
|
|
13
|
+
* The following workspace dependencies were updated
|
|
14
|
+
* dependencies
|
|
15
|
+
* @aigne/core bumped to 1.58.3
|
|
16
|
+
* devDependencies
|
|
17
|
+
* @aigne/test-utils bumped to 0.5.47
|
|
18
|
+
|
|
19
|
+
## [0.14.2](https://github.com/AIGNE-io/aigne-framework/compare/openai-v0.14.1...openai-v0.14.2) (2025-09-05)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
### Bug Fixes
|
|
23
|
+
|
|
24
|
+
* **model:** transform local file to base64 before request llm ([#462](https://github.com/AIGNE-io/aigne-framework/issues/462)) ([58ef5d7](https://github.com/AIGNE-io/aigne-framework/commit/58ef5d77046c49f3c4eed15b7f0cc283cbbcd74a))
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
### Dependencies
|
|
28
|
+
|
|
29
|
+
* The following workspace dependencies were updated
|
|
30
|
+
* dependencies
|
|
31
|
+
* @aigne/core bumped to 1.58.2
|
|
32
|
+
* devDependencies
|
|
33
|
+
* @aigne/test-utils bumped to 0.5.46
|
|
34
|
+
|
|
3
35
|
## [0.14.1](https://github.com/AIGNE-io/aigne-framework/compare/openai-v0.14.0...openai-v0.14.1) (2025-09-05)
|
|
4
36
|
|
|
5
37
|
|
|
@@ -151,6 +151,13 @@ export declare class OpenAIChatModel extends ChatModel {
|
|
|
151
151
|
private getRunResponseFormat;
|
|
152
152
|
private requestStructuredOutput;
|
|
153
153
|
private extractResultFromStream;
|
|
154
|
+
/**
|
|
155
|
+
* Controls how optional fields are handled in JSON schema conversion
|
|
156
|
+
* - "anyOf": All fields are required but can be null (default)
|
|
157
|
+
* - "optional": Fields marked as optional in schema remain optional
|
|
158
|
+
*/
|
|
159
|
+
protected optionalFieldMode?: "anyOf" | "optional";
|
|
160
|
+
protected jsonSchemaToOpenAIJsonSchema(schema: Record<string, unknown>): Record<string, unknown>;
|
|
154
161
|
}
|
|
155
162
|
/**
|
|
156
163
|
* @hidden
|
|
@@ -162,7 +169,3 @@ export declare function contentsFromInputMessages(messages: ChatModelInputMessag
|
|
|
162
169
|
export declare function toolsFromInputTools(tools?: ChatModelInputTool[], options?: {
|
|
163
170
|
addTypeToEmptyParameters?: boolean;
|
|
164
171
|
}): ChatCompletionTool[] | undefined;
|
|
165
|
-
/**
|
|
166
|
-
* @hidden
|
|
167
|
-
*/
|
|
168
|
-
export declare function jsonSchemaToOpenAIJsonSchema(schema: Record<string, unknown>): Record<string, unknown>;
|
|
@@ -3,14 +3,12 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.OpenAIChatModel = exports.openAIChatModelOptionsSchema = void 0;
|
|
4
4
|
exports.contentsFromInputMessages = contentsFromInputMessages;
|
|
5
5
|
exports.toolsFromInputTools = toolsFromInputTools;
|
|
6
|
-
exports.jsonSchemaToOpenAIJsonSchema = jsonSchemaToOpenAIJsonSchema;
|
|
7
6
|
const core_1 = require("@aigne/core");
|
|
8
7
|
const logger_js_1 = require("@aigne/core/utils/logger.js");
|
|
9
8
|
const model_utils_js_1 = require("@aigne/core/utils/model-utils.js");
|
|
10
9
|
const prompts_js_1 = require("@aigne/core/utils/prompts.js");
|
|
11
10
|
const stream_utils_js_1 = require("@aigne/core/utils/stream-utils.js");
|
|
12
11
|
const type_utils_js_1 = require("@aigne/core/utils/type-utils.js");
|
|
13
|
-
const index_js_1 = require("@aigne/platform-helpers/nodejs/index.js");
|
|
14
12
|
const ajv_1 = require("ajv");
|
|
15
13
|
const uuid_1 = require("uuid");
|
|
16
14
|
const zod_1 = require("zod");
|
|
@@ -197,7 +195,7 @@ class OpenAIChatModel extends core_1.ChatModel {
|
|
|
197
195
|
type: "json_schema",
|
|
198
196
|
json_schema: {
|
|
199
197
|
...input.responseFormat.jsonSchema,
|
|
200
|
-
schema: jsonSchemaToOpenAIJsonSchema(input.responseFormat.jsonSchema.schema),
|
|
198
|
+
schema: this.jsonSchemaToOpenAIJsonSchema(input.responseFormat.jsonSchema.schema),
|
|
201
199
|
},
|
|
202
200
|
},
|
|
203
201
|
};
|
|
@@ -313,6 +311,40 @@ class OpenAIChatModel extends core_1.ChatModel {
|
|
|
313
311
|
});
|
|
314
312
|
return streaming ? result : await (0, stream_utils_js_1.agentResponseStreamToObject)(result);
|
|
315
313
|
}
|
|
314
|
+
/**
|
|
315
|
+
* Controls how optional fields are handled in JSON schema conversion
|
|
316
|
+
* - "anyOf": All fields are required but can be null (default)
|
|
317
|
+
* - "optional": Fields marked as optional in schema remain optional
|
|
318
|
+
*/
|
|
319
|
+
optionalFieldMode = "anyOf";
|
|
320
|
+
jsonSchemaToOpenAIJsonSchema(schema) {
|
|
321
|
+
if (schema?.type === "object") {
|
|
322
|
+
const s = schema;
|
|
323
|
+
const required = this.optionalFieldMode === "anyOf" ? Object.keys(s.properties) : s.required;
|
|
324
|
+
return {
|
|
325
|
+
...schema,
|
|
326
|
+
properties: Object.fromEntries(Object.entries(s.properties).map(([key, value]) => {
|
|
327
|
+
const valueSchema = this.jsonSchemaToOpenAIJsonSchema(value);
|
|
328
|
+
// NOTE: All fields must be required https://platform.openai.com/docs/guides/structured-outputs/all-fields-must-be-required
|
|
329
|
+
return [
|
|
330
|
+
key,
|
|
331
|
+
this.optionalFieldMode === "optional" || s.required?.includes(key)
|
|
332
|
+
? valueSchema
|
|
333
|
+
: { anyOf: [valueSchema, { type: ["null"] }] },
|
|
334
|
+
];
|
|
335
|
+
})),
|
|
336
|
+
required,
|
|
337
|
+
};
|
|
338
|
+
}
|
|
339
|
+
if (schema?.type === "array") {
|
|
340
|
+
const { items } = schema;
|
|
341
|
+
return {
|
|
342
|
+
...schema,
|
|
343
|
+
items: this.jsonSchemaToOpenAIJsonSchema(items),
|
|
344
|
+
};
|
|
345
|
+
}
|
|
346
|
+
return schema;
|
|
347
|
+
}
|
|
316
348
|
}
|
|
317
349
|
exports.OpenAIChatModel = OpenAIChatModel;
|
|
318
350
|
// Create role mapper for OpenAI (uses standard mapping)
|
|
@@ -338,13 +370,7 @@ async function contentsFromInputMessages(messages) {
|
|
|
338
370
|
file: { file_data: c.data, filename: c.filename },
|
|
339
371
|
};
|
|
340
372
|
case "local": {
|
|
341
|
-
|
|
342
|
-
type: "file",
|
|
343
|
-
file: {
|
|
344
|
-
file_data: await index_js_1.nodejs.fs.readFile(c.path, "base64"),
|
|
345
|
-
filename: c.filename,
|
|
346
|
-
},
|
|
347
|
-
};
|
|
373
|
+
throw new Error(`Unsupported local file: ${c.path}, it should be converted to base64 at ChatModel`);
|
|
348
374
|
}
|
|
349
375
|
}
|
|
350
376
|
}))).filter(type_utils_js_1.isNonNullable),
|
|
@@ -380,34 +406,6 @@ function toolsFromInputTools(tools, options) {
|
|
|
380
406
|
})
|
|
381
407
|
: undefined;
|
|
382
408
|
}
|
|
383
|
-
/**
|
|
384
|
-
* @hidden
|
|
385
|
-
*/
|
|
386
|
-
function jsonSchemaToOpenAIJsonSchema(schema) {
|
|
387
|
-
if (schema?.type === "object") {
|
|
388
|
-
const { required, properties } = schema;
|
|
389
|
-
return {
|
|
390
|
-
...schema,
|
|
391
|
-
properties: Object.fromEntries(Object.entries(properties).map(([key, value]) => {
|
|
392
|
-
const valueSchema = jsonSchemaToOpenAIJsonSchema(value);
|
|
393
|
-
// NOTE: All fields must be required https://platform.openai.com/docs/guides/structured-outputs/all-fields-must-be-required
|
|
394
|
-
return [
|
|
395
|
-
key,
|
|
396
|
-
required?.includes(key) ? valueSchema : { anyOf: [valueSchema, { type: ["null"] }] },
|
|
397
|
-
];
|
|
398
|
-
})),
|
|
399
|
-
required: Object.keys(properties),
|
|
400
|
-
};
|
|
401
|
-
}
|
|
402
|
-
if (schema?.type === "array") {
|
|
403
|
-
const { items } = schema;
|
|
404
|
-
return {
|
|
405
|
-
...schema,
|
|
406
|
-
items: jsonSchemaToOpenAIJsonSchema(items),
|
|
407
|
-
};
|
|
408
|
-
}
|
|
409
|
-
return schema;
|
|
410
|
-
}
|
|
411
409
|
function handleToolCallDelta(toolCalls, call) {
|
|
412
410
|
toolCalls[call.index] ??= {
|
|
413
411
|
id: call.id || (0, uuid_1.v7)(),
|
|
@@ -151,6 +151,13 @@ export declare class OpenAIChatModel extends ChatModel {
|
|
|
151
151
|
private getRunResponseFormat;
|
|
152
152
|
private requestStructuredOutput;
|
|
153
153
|
private extractResultFromStream;
|
|
154
|
+
/**
|
|
155
|
+
* Controls how optional fields are handled in JSON schema conversion
|
|
156
|
+
* - "anyOf": All fields are required but can be null (default)
|
|
157
|
+
* - "optional": Fields marked as optional in schema remain optional
|
|
158
|
+
*/
|
|
159
|
+
protected optionalFieldMode?: "anyOf" | "optional";
|
|
160
|
+
protected jsonSchemaToOpenAIJsonSchema(schema: Record<string, unknown>): Record<string, unknown>;
|
|
154
161
|
}
|
|
155
162
|
/**
|
|
156
163
|
* @hidden
|
|
@@ -162,7 +169,3 @@ export declare function contentsFromInputMessages(messages: ChatModelInputMessag
|
|
|
162
169
|
export declare function toolsFromInputTools(tools?: ChatModelInputTool[], options?: {
|
|
163
170
|
addTypeToEmptyParameters?: boolean;
|
|
164
171
|
}): ChatCompletionTool[] | undefined;
|
|
165
|
-
/**
|
|
166
|
-
* @hidden
|
|
167
|
-
*/
|
|
168
|
-
export declare function jsonSchemaToOpenAIJsonSchema(schema: Record<string, unknown>): Record<string, unknown>;
|
|
@@ -151,6 +151,13 @@ export declare class OpenAIChatModel extends ChatModel {
|
|
|
151
151
|
private getRunResponseFormat;
|
|
152
152
|
private requestStructuredOutput;
|
|
153
153
|
private extractResultFromStream;
|
|
154
|
+
/**
|
|
155
|
+
* Controls how optional fields are handled in JSON schema conversion
|
|
156
|
+
* - "anyOf": All fields are required but can be null (default)
|
|
157
|
+
* - "optional": Fields marked as optional in schema remain optional
|
|
158
|
+
*/
|
|
159
|
+
protected optionalFieldMode?: "anyOf" | "optional";
|
|
160
|
+
protected jsonSchemaToOpenAIJsonSchema(schema: Record<string, unknown>): Record<string, unknown>;
|
|
154
161
|
}
|
|
155
162
|
/**
|
|
156
163
|
* @hidden
|
|
@@ -162,7 +169,3 @@ export declare function contentsFromInputMessages(messages: ChatModelInputMessag
|
|
|
162
169
|
export declare function toolsFromInputTools(tools?: ChatModelInputTool[], options?: {
|
|
163
170
|
addTypeToEmptyParameters?: boolean;
|
|
164
171
|
}): ChatCompletionTool[] | undefined;
|
|
165
|
-
/**
|
|
166
|
-
* @hidden
|
|
167
|
-
*/
|
|
168
|
-
export declare function jsonSchemaToOpenAIJsonSchema(schema: Record<string, unknown>): Record<string, unknown>;
|
|
@@ -4,7 +4,6 @@ import { mergeUsage } from "@aigne/core/utils/model-utils.js";
|
|
|
4
4
|
import { getJsonOutputPrompt } from "@aigne/core/utils/prompts.js";
|
|
5
5
|
import { agentResponseStreamToObject } from "@aigne/core/utils/stream-utils.js";
|
|
6
6
|
import { checkArguments, isNonNullable, } from "@aigne/core/utils/type-utils.js";
|
|
7
|
-
import { nodejs } from "@aigne/platform-helpers/nodejs/index.js";
|
|
8
7
|
import { Ajv } from "ajv";
|
|
9
8
|
import { v7 } from "uuid";
|
|
10
9
|
import { z } from "zod";
|
|
@@ -191,7 +190,7 @@ export class OpenAIChatModel extends ChatModel {
|
|
|
191
190
|
type: "json_schema",
|
|
192
191
|
json_schema: {
|
|
193
192
|
...input.responseFormat.jsonSchema,
|
|
194
|
-
schema: jsonSchemaToOpenAIJsonSchema(input.responseFormat.jsonSchema.schema),
|
|
193
|
+
schema: this.jsonSchemaToOpenAIJsonSchema(input.responseFormat.jsonSchema.schema),
|
|
195
194
|
},
|
|
196
195
|
},
|
|
197
196
|
};
|
|
@@ -307,6 +306,40 @@ export class OpenAIChatModel extends ChatModel {
|
|
|
307
306
|
});
|
|
308
307
|
return streaming ? result : await agentResponseStreamToObject(result);
|
|
309
308
|
}
|
|
309
|
+
/**
|
|
310
|
+
* Controls how optional fields are handled in JSON schema conversion
|
|
311
|
+
* - "anyOf": All fields are required but can be null (default)
|
|
312
|
+
* - "optional": Fields marked as optional in schema remain optional
|
|
313
|
+
*/
|
|
314
|
+
optionalFieldMode = "anyOf";
|
|
315
|
+
jsonSchemaToOpenAIJsonSchema(schema) {
|
|
316
|
+
if (schema?.type === "object") {
|
|
317
|
+
const s = schema;
|
|
318
|
+
const required = this.optionalFieldMode === "anyOf" ? Object.keys(s.properties) : s.required;
|
|
319
|
+
return {
|
|
320
|
+
...schema,
|
|
321
|
+
properties: Object.fromEntries(Object.entries(s.properties).map(([key, value]) => {
|
|
322
|
+
const valueSchema = this.jsonSchemaToOpenAIJsonSchema(value);
|
|
323
|
+
// NOTE: All fields must be required https://platform.openai.com/docs/guides/structured-outputs/all-fields-must-be-required
|
|
324
|
+
return [
|
|
325
|
+
key,
|
|
326
|
+
this.optionalFieldMode === "optional" || s.required?.includes(key)
|
|
327
|
+
? valueSchema
|
|
328
|
+
: { anyOf: [valueSchema, { type: ["null"] }] },
|
|
329
|
+
];
|
|
330
|
+
})),
|
|
331
|
+
required,
|
|
332
|
+
};
|
|
333
|
+
}
|
|
334
|
+
if (schema?.type === "array") {
|
|
335
|
+
const { items } = schema;
|
|
336
|
+
return {
|
|
337
|
+
...schema,
|
|
338
|
+
items: this.jsonSchemaToOpenAIJsonSchema(items),
|
|
339
|
+
};
|
|
340
|
+
}
|
|
341
|
+
return schema;
|
|
342
|
+
}
|
|
310
343
|
}
|
|
311
344
|
// Create role mapper for OpenAI (uses standard mapping)
|
|
312
345
|
const mapRole = createRoleMapper(STANDARD_ROLE_MAP);
|
|
@@ -331,13 +364,7 @@ export async function contentsFromInputMessages(messages) {
|
|
|
331
364
|
file: { file_data: c.data, filename: c.filename },
|
|
332
365
|
};
|
|
333
366
|
case "local": {
|
|
334
|
-
|
|
335
|
-
type: "file",
|
|
336
|
-
file: {
|
|
337
|
-
file_data: await nodejs.fs.readFile(c.path, "base64"),
|
|
338
|
-
filename: c.filename,
|
|
339
|
-
},
|
|
340
|
-
};
|
|
367
|
+
throw new Error(`Unsupported local file: ${c.path}, it should be converted to base64 at ChatModel`);
|
|
341
368
|
}
|
|
342
369
|
}
|
|
343
370
|
}))).filter(isNonNullable),
|
|
@@ -373,34 +400,6 @@ export function toolsFromInputTools(tools, options) {
|
|
|
373
400
|
})
|
|
374
401
|
: undefined;
|
|
375
402
|
}
|
|
376
|
-
/**
|
|
377
|
-
* @hidden
|
|
378
|
-
*/
|
|
379
|
-
export function jsonSchemaToOpenAIJsonSchema(schema) {
|
|
380
|
-
if (schema?.type === "object") {
|
|
381
|
-
const { required, properties } = schema;
|
|
382
|
-
return {
|
|
383
|
-
...schema,
|
|
384
|
-
properties: Object.fromEntries(Object.entries(properties).map(([key, value]) => {
|
|
385
|
-
const valueSchema = jsonSchemaToOpenAIJsonSchema(value);
|
|
386
|
-
// NOTE: All fields must be required https://platform.openai.com/docs/guides/structured-outputs/all-fields-must-be-required
|
|
387
|
-
return [
|
|
388
|
-
key,
|
|
389
|
-
required?.includes(key) ? valueSchema : { anyOf: [valueSchema, { type: ["null"] }] },
|
|
390
|
-
];
|
|
391
|
-
})),
|
|
392
|
-
required: Object.keys(properties),
|
|
393
|
-
};
|
|
394
|
-
}
|
|
395
|
-
if (schema?.type === "array") {
|
|
396
|
-
const { items } = schema;
|
|
397
|
-
return {
|
|
398
|
-
...schema,
|
|
399
|
-
items: jsonSchemaToOpenAIJsonSchema(items),
|
|
400
|
-
};
|
|
401
|
-
}
|
|
402
|
-
return schema;
|
|
403
|
-
}
|
|
404
403
|
function handleToolCallDelta(toolCalls, call) {
|
|
405
404
|
toolCalls[call.index] ??= {
|
|
406
405
|
id: call.id || v7(),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aigne/openai",
|
|
3
|
-
"version": "0.14.
|
|
3
|
+
"version": "0.14.3",
|
|
4
4
|
"description": "AIGNE OpenAI SDK for integrating with OpenAI's GPT models and API services",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"openai": "^5.8.3",
|
|
40
40
|
"uuid": "^11.1.0",
|
|
41
41
|
"zod": "^3.25.67",
|
|
42
|
-
"@aigne/core": "^1.58.
|
|
42
|
+
"@aigne/core": "^1.58.3",
|
|
43
43
|
"@aigne/platform-helpers": "^0.6.2"
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
"npm-run-all": "^4.1.5",
|
|
49
49
|
"rimraf": "^6.0.1",
|
|
50
50
|
"typescript": "^5.8.3",
|
|
51
|
-
"@aigne/test-utils": "^0.5.
|
|
51
|
+
"@aigne/test-utils": "^0.5.47"
|
|
52
52
|
},
|
|
53
53
|
"scripts": {
|
|
54
54
|
"lint": "tsc --noEmit",
|