@aigne/openai 0.16.16 → 1.74.0-beta

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.
Files changed (59) hide show
  1. package/README.md +11 -11
  2. package/dist/_virtual/rolldown_runtime.cjs +29 -0
  3. package/dist/index.cjs +10 -0
  4. package/dist/index.d.cts +4 -0
  5. package/dist/index.d.mts +4 -0
  6. package/dist/index.mjs +5 -0
  7. package/dist/openai-chat-model.cjs +371 -0
  8. package/dist/openai-chat-model.d.cts +165 -0
  9. package/dist/openai-chat-model.d.cts.map +1 -0
  10. package/dist/openai-chat-model.d.mts +165 -0
  11. package/dist/openai-chat-model.d.mts.map +1 -0
  12. package/dist/openai-chat-model.mjs +368 -0
  13. package/dist/openai-chat-model.mjs.map +1 -0
  14. package/dist/openai-image-model.cjs +123 -0
  15. package/dist/openai-image-model.d.cts +57 -0
  16. package/dist/openai-image-model.d.cts.map +1 -0
  17. package/dist/openai-image-model.d.mts +57 -0
  18. package/dist/openai-image-model.d.mts.map +1 -0
  19. package/dist/openai-image-model.mjs +123 -0
  20. package/dist/openai-image-model.mjs.map +1 -0
  21. package/dist/openai-video-model.cjs +112 -0
  22. package/dist/openai-video-model.d.cts +95 -0
  23. package/dist/openai-video-model.d.cts.map +1 -0
  24. package/dist/openai-video-model.d.mts +95 -0
  25. package/dist/openai-video-model.d.mts.map +1 -0
  26. package/dist/openai-video-model.mjs +112 -0
  27. package/dist/openai-video-model.mjs.map +1 -0
  28. package/dist/openai.cjs +14 -0
  29. package/dist/openai.mjs +13 -0
  30. package/dist/openai.mjs.map +1 -0
  31. package/package.json +29 -30
  32. package/CHANGELOG.md +0 -2448
  33. package/lib/cjs/index.d.ts +0 -3
  34. package/lib/cjs/index.js +0 -19
  35. package/lib/cjs/openai-chat-model.d.ts +0 -160
  36. package/lib/cjs/openai-chat-model.js +0 -465
  37. package/lib/cjs/openai-image-model.d.ts +0 -55
  38. package/lib/cjs/openai-image-model.js +0 -110
  39. package/lib/cjs/openai-video-model.d.ts +0 -92
  40. package/lib/cjs/openai-video-model.js +0 -118
  41. package/lib/cjs/openai.d.ts +0 -4
  42. package/lib/cjs/openai.js +0 -17
  43. package/lib/cjs/package.json +0 -3
  44. package/lib/dts/index.d.ts +0 -3
  45. package/lib/dts/openai-chat-model.d.ts +0 -160
  46. package/lib/dts/openai-image-model.d.ts +0 -55
  47. package/lib/dts/openai-video-model.d.ts +0 -92
  48. package/lib/dts/openai.d.ts +0 -4
  49. package/lib/esm/index.d.ts +0 -3
  50. package/lib/esm/index.js +0 -3
  51. package/lib/esm/openai-chat-model.d.ts +0 -160
  52. package/lib/esm/openai-chat-model.js +0 -459
  53. package/lib/esm/openai-image-model.d.ts +0 -55
  54. package/lib/esm/openai-image-model.js +0 -106
  55. package/lib/esm/openai-video-model.d.ts +0 -92
  56. package/lib/esm/openai-video-model.js +0 -114
  57. package/lib/esm/openai.d.ts +0 -4
  58. package/lib/esm/openai.js +0 -10
  59. package/lib/esm/package.json +0 -3
@@ -1,3 +0,0 @@
1
- export * from "./openai-chat-model.js";
2
- export * from "./openai-image-model.js";
3
- export * from "./openai-video-model.js";
package/lib/cjs/index.js DELETED
@@ -1,19 +0,0 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
- for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
- };
16
- Object.defineProperty(exports, "__esModule", { value: true });
17
- __exportStar(require("./openai-chat-model.js"), exports);
18
- __exportStar(require("./openai-image-model.js"), exports);
19
- __exportStar(require("./openai-video-model.js"), exports);
@@ -1,160 +0,0 @@
1
- import { type AgentInvokeOptions, type AgentProcessResult, ChatModel, type ChatModelInput, type ChatModelInputMessage, type ChatModelInputTool, type ChatModelOptions, type ChatModelOutput } from "@aigne/core";
2
- import { type PromiseOrValue } from "@aigne/core/utils/type-utils.js";
3
- import type { ClientOptions, OpenAI } from "openai";
4
- import type { ChatCompletionMessageParam, ChatCompletionTool } from "openai/resources";
5
- import { z } from "zod";
6
- export interface OpenAIChatModelCapabilities {
7
- supportsNativeStructuredOutputs: boolean;
8
- supportsEndWithSystemMessage: boolean;
9
- supportsToolsUseWithJsonSchema: boolean;
10
- supportsParallelToolCalls: boolean;
11
- supportsToolsEmptyParameters: boolean;
12
- supportsToolStreaming: boolean;
13
- supportsTemperature: boolean;
14
- }
15
- /**
16
- * Configuration options for OpenAI Chat Model
17
- */
18
- export interface OpenAIChatModelOptions extends ChatModelOptions {
19
- /**
20
- * API key for OpenAI API
21
- *
22
- * If not provided, will look for OPENAI_API_KEY in environment variables
23
- */
24
- apiKey?: string;
25
- /**
26
- * Base URL for OpenAI API
27
- *
28
- * Useful for proxies or alternate endpoints
29
- */
30
- baseURL?: string;
31
- /**
32
- * Client options for OpenAI API
33
- */
34
- clientOptions?: Partial<ClientOptions>;
35
- }
36
- /**
37
- * @hidden
38
- */
39
- export declare const openAIChatModelOptionsSchema: z.ZodObject<{
40
- apiKey: z.ZodOptional<z.ZodString>;
41
- baseURL: z.ZodOptional<z.ZodString>;
42
- model: z.ZodOptional<z.ZodString>;
43
- modelOptions: z.ZodOptional<z.ZodObject<{
44
- model: z.ZodOptional<z.ZodString>;
45
- temperature: z.ZodOptional<z.ZodNumber>;
46
- topP: z.ZodOptional<z.ZodNumber>;
47
- frequencyPenalty: z.ZodOptional<z.ZodNumber>;
48
- presencePenalty: z.ZodOptional<z.ZodNumber>;
49
- parallelToolCalls: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
50
- }, "strip", z.ZodTypeAny, {
51
- parallelToolCalls: boolean;
52
- model?: string | undefined;
53
- temperature?: number | undefined;
54
- topP?: number | undefined;
55
- frequencyPenalty?: number | undefined;
56
- presencePenalty?: number | undefined;
57
- }, {
58
- model?: string | undefined;
59
- temperature?: number | undefined;
60
- topP?: number | undefined;
61
- frequencyPenalty?: number | undefined;
62
- presencePenalty?: number | undefined;
63
- parallelToolCalls?: boolean | undefined;
64
- }>>;
65
- }, "strip", z.ZodTypeAny, {
66
- model?: string | undefined;
67
- apiKey?: string | undefined;
68
- baseURL?: string | undefined;
69
- modelOptions?: {
70
- parallelToolCalls: boolean;
71
- model?: string | undefined;
72
- temperature?: number | undefined;
73
- topP?: number | undefined;
74
- frequencyPenalty?: number | undefined;
75
- presencePenalty?: number | undefined;
76
- } | undefined;
77
- }, {
78
- model?: string | undefined;
79
- apiKey?: string | undefined;
80
- baseURL?: string | undefined;
81
- modelOptions?: {
82
- model?: string | undefined;
83
- temperature?: number | undefined;
84
- topP?: number | undefined;
85
- frequencyPenalty?: number | undefined;
86
- presencePenalty?: number | undefined;
87
- parallelToolCalls?: boolean | undefined;
88
- } | undefined;
89
- }>;
90
- /**
91
- * Implementation of the ChatModel interface for OpenAI's API
92
- *
93
- * This model provides access to OpenAI's capabilities including:
94
- * - Text generation
95
- * - Tool use with parallel tool calls
96
- * - JSON structured output
97
- * - Image understanding
98
- *
99
- * Default model: 'gpt-4o-mini'
100
- *
101
- * @example
102
- * Here's how to create and use an OpenAI chat model:
103
- * {@includeCode ../test/openai-chat-model.test.ts#example-openai-chat-model}
104
- *
105
- * @example
106
- * Here's an example with streaming response:
107
- * {@includeCode ../test/openai-chat-model.test.ts#example-openai-chat-model-stream}
108
- */
109
- export declare class OpenAIChatModel extends ChatModel {
110
- options?: OpenAIChatModelOptions | undefined;
111
- constructor(options?: OpenAIChatModelOptions | undefined);
112
- /**
113
- * @hidden
114
- */
115
- protected _client?: OpenAI;
116
- protected apiKeyEnvName: string;
117
- protected apiKeyDefault: string | undefined;
118
- protected supportsNativeStructuredOutputs: boolean;
119
- protected supportsToolsUseWithJsonSchema: boolean;
120
- protected supportsParallelToolCalls: boolean;
121
- protected supportsToolsEmptyParameters: boolean;
122
- protected supportsToolStreaming: boolean;
123
- protected supportsTemperature: boolean;
124
- get client(): OpenAI;
125
- get credential(): {
126
- url: string | undefined;
127
- apiKey: string | undefined;
128
- model: string;
129
- };
130
- /**
131
- * Process the input and generate a response
132
- * @param input The input to process
133
- * @returns The generated response
134
- */
135
- process(input: ChatModelInput, options: AgentInvokeOptions): PromiseOrValue<AgentProcessResult<ChatModelOutput>>;
136
- private getReasoningEffort;
137
- private _process;
138
- private getParallelToolCalls;
139
- protected getRunMessages(input: ChatModelInput): Promise<ChatCompletionMessageParam[]>;
140
- private getRunResponseFormat;
141
- private requestStructuredOutput;
142
- private extractResultFromStream;
143
- /**
144
- * Controls how optional fields are handled in JSON schema conversion
145
- * - "anyOf": All fields are required but can be null (default)
146
- * - "optional": Fields marked as optional in schema remain optional
147
- */
148
- protected optionalFieldMode?: "anyOf" | "optional";
149
- protected jsonSchemaToOpenAIJsonSchema(schema: Record<string, unknown>): Record<string, unknown>;
150
- }
151
- /**
152
- * @hidden
153
- */
154
- export declare function contentsFromInputMessages(messages: ChatModelInputMessage[]): Promise<ChatCompletionMessageParam[]>;
155
- /**
156
- * @hidden
157
- */
158
- export declare function toolsFromInputTools(tools?: ChatModelInputTool[], options?: {
159
- addTypeToEmptyParameters?: boolean;
160
- }): ChatCompletionTool[] | undefined;
@@ -1,465 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.OpenAIChatModel = exports.openAIChatModelOptionsSchema = void 0;
4
- exports.contentsFromInputMessages = contentsFromInputMessages;
5
- exports.toolsFromInputTools = toolsFromInputTools;
6
- const core_1 = require("@aigne/core");
7
- const logger_js_1 = require("@aigne/core/utils/logger.js");
8
- const model_utils_js_1 = require("@aigne/core/utils/model-utils.js");
9
- const prompts_js_1 = require("@aigne/core/utils/prompts.js");
10
- const stream_utils_js_1 = require("@aigne/core/utils/stream-utils.js");
11
- const type_utils_js_1 = require("@aigne/core/utils/type-utils.js");
12
- const uuid_1 = require("@aigne/uuid");
13
- const zod_1 = require("zod");
14
- const openai_js_1 = require("./openai.js");
15
- const CHAT_MODEL_OPENAI_DEFAULT_MODEL = "gpt-4o-mini";
16
- const OPENAI_CHAT_MODEL_CAPABILITIES = {
17
- "o4-mini": { supportsParallelToolCalls: false, supportsTemperature: false },
18
- "o3-mini": { supportsParallelToolCalls: false, supportsTemperature: false },
19
- };
20
- /**
21
- * @hidden
22
- */
23
- exports.openAIChatModelOptionsSchema = zod_1.z.object({
24
- apiKey: zod_1.z.string().optional(),
25
- baseURL: zod_1.z.string().optional(),
26
- model: zod_1.z.string().optional(),
27
- modelOptions: zod_1.z
28
- .object({
29
- model: zod_1.z.string().optional(),
30
- temperature: zod_1.z.number().optional(),
31
- topP: zod_1.z.number().optional(),
32
- frequencyPenalty: zod_1.z.number().optional(),
33
- presencePenalty: zod_1.z.number().optional(),
34
- parallelToolCalls: zod_1.z.boolean().optional().default(true),
35
- })
36
- .optional(),
37
- });
38
- /**
39
- * Implementation of the ChatModel interface for OpenAI's API
40
- *
41
- * This model provides access to OpenAI's capabilities including:
42
- * - Text generation
43
- * - Tool use with parallel tool calls
44
- * - JSON structured output
45
- * - Image understanding
46
- *
47
- * Default model: 'gpt-4o-mini'
48
- *
49
- * @example
50
- * Here's how to create and use an OpenAI chat model:
51
- * {@includeCode ../test/openai-chat-model.test.ts#example-openai-chat-model}
52
- *
53
- * @example
54
- * Here's an example with streaming response:
55
- * {@includeCode ../test/openai-chat-model.test.ts#example-openai-chat-model-stream}
56
- */
57
- class OpenAIChatModel extends core_1.ChatModel {
58
- options;
59
- constructor(options) {
60
- super();
61
- this.options = options;
62
- if (options)
63
- (0, type_utils_js_1.checkArguments)(this.name, exports.openAIChatModelOptionsSchema, options);
64
- const preset = options?.model ? OPENAI_CHAT_MODEL_CAPABILITIES[options.model] : undefined;
65
- Object.assign(this, preset);
66
- }
67
- /**
68
- * @hidden
69
- */
70
- _client;
71
- apiKeyEnvName = "OPENAI_API_KEY";
72
- apiKeyDefault;
73
- supportsNativeStructuredOutputs = true;
74
- supportsToolsUseWithJsonSchema = true;
75
- supportsParallelToolCalls = true;
76
- supportsToolsEmptyParameters = true;
77
- supportsToolStreaming = true;
78
- supportsTemperature = true;
79
- get client() {
80
- const { apiKey, url } = this.credential;
81
- if (!apiKey)
82
- throw new Error(`${this.name} requires an API key. Please provide it via \`options.apiKey\`, or set the \`${this.apiKeyEnvName}\` environment variable`);
83
- this._client ??= new openai_js_1.CustomOpenAI({
84
- baseURL: url,
85
- apiKey,
86
- ...this.options?.clientOptions,
87
- });
88
- return this._client;
89
- }
90
- get credential() {
91
- return {
92
- url: this.options?.baseURL || process.env.OPENAI_BASE_URL,
93
- apiKey: this.options?.apiKey || process.env[this.apiKeyEnvName] || this.apiKeyDefault,
94
- model: this.options?.model || CHAT_MODEL_OPENAI_DEFAULT_MODEL,
95
- };
96
- }
97
- /**
98
- * Process the input and generate a response
99
- * @param input The input to process
100
- * @returns The generated response
101
- */
102
- process(input, options) {
103
- return this._process(input, options);
104
- }
105
- getReasoningEffort(effort) {
106
- if (typeof effort === "number") {
107
- if (effort > 5000)
108
- return "high";
109
- if (effort > 1000)
110
- return "medium";
111
- if (effort > 500)
112
- return "low";
113
- if (effort > 0)
114
- return "minimal";
115
- return undefined;
116
- }
117
- return effort;
118
- }
119
- async _process(input, _options) {
120
- const { modelOptions = {} } = input;
121
- const messages = await this.getRunMessages(input);
122
- const model = modelOptions?.model || this.credential.model;
123
- const body = {
124
- model,
125
- temperature: this.supportsTemperature ? modelOptions.temperature : undefined,
126
- top_p: modelOptions.topP,
127
- frequency_penalty: modelOptions.frequencyPenalty,
128
- presence_penalty: modelOptions.presencePenalty,
129
- messages,
130
- stream_options: { include_usage: true },
131
- stream: true,
132
- reasoning_effort: this.getReasoningEffort(modelOptions.reasoningEffort),
133
- };
134
- if (model.includes("gpt-5") || model.includes("o1-")) {
135
- delete body.temperature;
136
- delete body.top_p;
137
- }
138
- // For models that do not support tools use with JSON schema in same request,
139
- // so we need to handle the case where tools are not used and responseFormat is json
140
- if (!input.tools?.length && input.responseFormat?.type === "json_schema") {
141
- return await this.requestStructuredOutput(body, input.responseFormat);
142
- }
143
- const { jsonMode, responseFormat } = await this.getRunResponseFormat(input);
144
- const stream = (await this.client.chat.completions.create({
145
- ...body,
146
- tools: toolsFromInputTools(input.tools, {
147
- addTypeToEmptyParameters: !this.supportsToolsEmptyParameters,
148
- }),
149
- tool_choice: input.toolChoice,
150
- parallel_tool_calls: this.getParallelToolCalls(input, modelOptions),
151
- response_format: responseFormat,
152
- }));
153
- if (input.responseFormat?.type !== "json_schema") {
154
- return await this.extractResultFromStream(body, stream, false, true);
155
- }
156
- const result = await this.extractResultFromStream(body, stream, jsonMode);
157
- // Just return the result if it has tool calls
158
- if (result.toolCalls?.length || result.json)
159
- return result;
160
- // Try to parse the text response as JSON
161
- // If it matches the json_schema, return it as json
162
- const json = (0, core_1.safeParseJSON)(result.text || "");
163
- const validated = this.validateJsonSchema(input.responseFormat.jsonSchema.schema, json, {
164
- safe: true,
165
- });
166
- if (validated.success) {
167
- return { ...result, json: validated.data, text: undefined };
168
- }
169
- logger_js_1.logger.warn(`${this.name}: Text response does not match JSON schema, trying to use tool to extract json `, {
170
- text: result.text,
171
- });
172
- const output = await this.requestStructuredOutput(body, input.responseFormat);
173
- return { ...output, usage: (0, model_utils_js_1.mergeUsage)(result.usage, output.usage) };
174
- }
175
- getParallelToolCalls(input, modelOptions) {
176
- if (!this.supportsParallelToolCalls)
177
- return undefined;
178
- if (!input.tools?.length)
179
- return undefined;
180
- return modelOptions.parallelToolCalls;
181
- }
182
- async getRunMessages(input) {
183
- const messages = await contentsFromInputMessages(input.messages);
184
- if (input.responseFormat?.type === "json_schema") {
185
- if (!this.supportsNativeStructuredOutputs ||
186
- (!this.supportsToolsUseWithJsonSchema && input.tools?.length)) {
187
- messages.unshift({
188
- role: "system",
189
- content: (0, prompts_js_1.getJsonOutputPrompt)(input.responseFormat.jsonSchema.schema),
190
- });
191
- }
192
- }
193
- return messages;
194
- }
195
- async getRunResponseFormat(input) {
196
- if (!this.supportsToolsUseWithJsonSchema && input.tools?.length)
197
- return { jsonMode: false, responseFormat: undefined };
198
- if (!this.supportsNativeStructuredOutputs) {
199
- const jsonMode = input.responseFormat?.type === "json_schema";
200
- return {
201
- jsonMode,
202
- responseFormat: jsonMode ? { type: "json_object" } : undefined,
203
- };
204
- }
205
- if (input.responseFormat?.type === "json_schema") {
206
- return {
207
- jsonMode: true,
208
- responseFormat: {
209
- type: "json_schema",
210
- json_schema: {
211
- ...input.responseFormat.jsonSchema,
212
- schema: this.jsonSchemaToOpenAIJsonSchema(input.responseFormat.jsonSchema.schema),
213
- },
214
- },
215
- };
216
- }
217
- return { jsonMode: false, responseFormat: undefined };
218
- }
219
- async requestStructuredOutput(body, responseFormat) {
220
- if (responseFormat?.type !== "json_schema") {
221
- throw new Error("Expected json_schema response format");
222
- }
223
- const { jsonMode, responseFormat: resolvedResponseFormat } = await this.getRunResponseFormat({
224
- responseFormat,
225
- });
226
- const res = (await this.client.chat.completions.create({
227
- ...body,
228
- response_format: resolvedResponseFormat,
229
- }));
230
- return this.extractResultFromStream(body, res, jsonMode);
231
- }
232
- async extractResultFromStream(body, stream, jsonMode, streaming) {
233
- const result = new ReadableStream({
234
- start: async (controller) => {
235
- try {
236
- controller.enqueue({
237
- delta: {
238
- json: {
239
- modelOptions: {
240
- reasoningEffort: body.reasoning_effort,
241
- },
242
- },
243
- },
244
- });
245
- let text = "";
246
- let refusal = "";
247
- const toolCalls = [];
248
- let model;
249
- for await (const chunk of stream) {
250
- const choice = chunk.choices?.[0];
251
- const delta = choice?.delta;
252
- if (!model) {
253
- model = chunk.model;
254
- controller.enqueue({
255
- delta: {
256
- json: {
257
- model,
258
- },
259
- },
260
- });
261
- }
262
- if (delta?.tool_calls?.length) {
263
- for (const call of delta.tool_calls) {
264
- if (this.supportsToolStreaming && call.index !== undefined) {
265
- handleToolCallDelta(toolCalls, call);
266
- }
267
- else {
268
- handleCompleteToolCall(toolCalls, call);
269
- }
270
- }
271
- }
272
- if (delta && "reasoning" in delta && typeof delta.reasoning === "string") {
273
- controller.enqueue({ delta: { text: { thoughts: delta.reasoning } } });
274
- }
275
- if (delta?.content) {
276
- text += delta.content;
277
- if (!jsonMode) {
278
- controller.enqueue({
279
- delta: {
280
- text: {
281
- text: delta.content,
282
- },
283
- },
284
- });
285
- }
286
- }
287
- if (delta?.refusal) {
288
- refusal += delta.refusal;
289
- }
290
- if (chunk.usage) {
291
- const usage = {
292
- inputTokens: chunk.usage.prompt_tokens,
293
- outputTokens: chunk.usage.completion_tokens,
294
- };
295
- // Parse cache statistics if available
296
- const inputDetails = chunk.usage.prompt_tokens_details;
297
- if (inputDetails?.cached_tokens) {
298
- usage.cacheReadInputTokens = inputDetails.cached_tokens;
299
- }
300
- controller.enqueue({
301
- delta: {
302
- json: {
303
- usage,
304
- },
305
- },
306
- });
307
- }
308
- }
309
- if (jsonMode && text) {
310
- controller.enqueue({
311
- delta: {
312
- json: {
313
- json: (0, core_1.safeParseJSON)(text),
314
- },
315
- },
316
- });
317
- }
318
- if (toolCalls.length) {
319
- controller.enqueue({
320
- delta: {
321
- json: {
322
- toolCalls: toolCalls.map(({ args, ...c }) => ({
323
- ...c,
324
- function: { ...c.function, arguments: args ? (0, core_1.safeParseJSON)(args) : {} },
325
- })),
326
- },
327
- },
328
- });
329
- }
330
- if (refusal) {
331
- controller.error(new Error(`Got refusal from LLM: ${refusal}`));
332
- }
333
- controller.close();
334
- }
335
- catch (error) {
336
- controller.error(error);
337
- }
338
- },
339
- });
340
- return streaming ? result : await (0, stream_utils_js_1.agentResponseStreamToObject)(result);
341
- }
342
- /**
343
- * Controls how optional fields are handled in JSON schema conversion
344
- * - "anyOf": All fields are required but can be null (default)
345
- * - "optional": Fields marked as optional in schema remain optional
346
- */
347
- optionalFieldMode = "anyOf";
348
- jsonSchemaToOpenAIJsonSchema(schema) {
349
- if (schema?.type === "object") {
350
- const s = schema;
351
- const required = this.optionalFieldMode === "anyOf" ? Object.keys(s.properties) : s.required;
352
- return {
353
- ...schema,
354
- properties: Object.fromEntries(Object.entries(s.properties).map(([key, value]) => {
355
- const valueSchema = this.jsonSchemaToOpenAIJsonSchema(value);
356
- // NOTE: All fields must be required https://platform.openai.com/docs/guides/structured-outputs/all-fields-must-be-required
357
- return [
358
- key,
359
- this.optionalFieldMode === "optional" || s.required?.includes(key)
360
- ? valueSchema
361
- : { anyOf: [valueSchema, { type: ["null"] }] },
362
- ];
363
- })),
364
- required,
365
- };
366
- }
367
- if (schema?.type === "array") {
368
- const { items } = schema;
369
- return {
370
- ...schema,
371
- items: this.jsonSchemaToOpenAIJsonSchema(items),
372
- };
373
- }
374
- return schema;
375
- }
376
- }
377
- exports.OpenAIChatModel = OpenAIChatModel;
378
- // Create role mapper for OpenAI (uses standard mapping)
379
- const mapRole = (0, core_1.createRoleMapper)(core_1.STANDARD_ROLE_MAP);
380
- /**
381
- * @hidden
382
- */
383
- async function contentsFromInputMessages(messages) {
384
- return Promise.all(messages.map(async (i) => ({
385
- role: mapRole(i.role),
386
- content: typeof i.content === "string"
387
- ? i.content
388
- : i.content &&
389
- (await Promise.all(i.content.map(async (c) => {
390
- switch (c.type) {
391
- case "text":
392
- return { type: "text", text: c.text };
393
- case "url":
394
- return { type: "image_url", image_url: { url: c.url } };
395
- case "file":
396
- return {
397
- type: "image_url",
398
- image_url: {
399
- url: `data:${c.mimeType || "image/png"};base64,${c.data}`,
400
- },
401
- };
402
- case "local": {
403
- throw new Error(`Unsupported local file: ${c.path}, it should be converted to base64 at ChatModel`);
404
- }
405
- }
406
- }))).filter(type_utils_js_1.isNonNullable),
407
- tool_calls: i.toolCalls?.map((i) => ({
408
- ...i,
409
- function: {
410
- ...i.function,
411
- arguments: JSON.stringify(i.function.arguments),
412
- },
413
- })),
414
- tool_call_id: i.toolCallId,
415
- name: i.name,
416
- })));
417
- }
418
- /**
419
- * @hidden
420
- */
421
- function toolsFromInputTools(tools, options) {
422
- return tools?.length
423
- ? tools.map((i) => {
424
- const parameters = i.function.parameters;
425
- if (options?.addTypeToEmptyParameters && Object.keys(parameters).length === 0) {
426
- parameters.type = "object";
427
- }
428
- return {
429
- type: "function",
430
- function: {
431
- name: i.function.name,
432
- description: i.function.description,
433
- parameters,
434
- },
435
- };
436
- })
437
- : undefined;
438
- }
439
- function handleToolCallDelta(toolCalls, call) {
440
- toolCalls[call.index] ??= {
441
- id: call.id || (0, uuid_1.v7)(),
442
- type: "function",
443
- function: { name: "", arguments: {} },
444
- args: "",
445
- };
446
- const c = toolCalls[call.index];
447
- if (!c)
448
- throw new Error("Tool call not found");
449
- if (call.type)
450
- c.type = call.type;
451
- c.function.name = c.function.name + (call.function?.name || "");
452
- c.args = c.args.concat(call.function?.arguments || "");
453
- }
454
- function handleCompleteToolCall(toolCalls, call) {
455
- toolCalls.push({
456
- id: call.id || (0, uuid_1.v7)(),
457
- type: "function",
458
- function: {
459
- name: call.function?.name || "",
460
- arguments: (0, core_1.safeParseJSON)(call.function?.arguments || "{}"),
461
- },
462
- args: call.function?.arguments || "",
463
- });
464
- }
465
- // safeParseJSON is now imported from @aigne/core