@core-ai/openai 0.14.0 → 0.15.0

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.
@@ -0,0 +1,1852 @@
1
+ // src/model-capabilities.ts
2
+ import {
3
+ stripModelDateSuffix
4
+ } from "@core-ai/core-ai";
5
+ var STANDARD_EFFORTS = [
6
+ "low",
7
+ "medium",
8
+ "high"
9
+ ];
10
+ var MAX_EFFORTS = [
11
+ "low",
12
+ "medium",
13
+ "high",
14
+ "max"
15
+ ];
16
+ var MINIMAL_EFFORTS = [
17
+ "minimal",
18
+ "low",
19
+ "medium",
20
+ "high"
21
+ ];
22
+ var PRO_EFFORTS = [
23
+ "medium",
24
+ "high",
25
+ "max"
26
+ ];
27
+ var HIGH_EFFORT = ["high"];
28
+ function createCapabilities(supportedEfforts, restrictsSamplingParams, maxTokensParameter = "max_completion_tokens") {
29
+ return {
30
+ reasoning: {
31
+ supported: true,
32
+ supportedEfforts,
33
+ restrictsSamplingParams
34
+ },
35
+ chatCompletions: {
36
+ maxTokensParameter
37
+ }
38
+ };
39
+ }
40
+ var DEFAULT_CAPABILITIES = createCapabilities(STANDARD_EFFORTS, false);
41
+ var UNKNOWN_MODEL_CAPABILITIES = createCapabilities(
42
+ STANDARD_EFFORTS,
43
+ false,
44
+ "max_tokens"
45
+ );
46
+ var SAMPLING_RESTRICTED_STANDARD_CAPABILITIES = createCapabilities(
47
+ STANDARD_EFFORTS,
48
+ true
49
+ );
50
+ var GPT_5_MAX_REASONING_CAPABILITIES = createCapabilities(MAX_EFFORTS, true);
51
+ var GPT_5_MINIMAL_REASONING_CAPABILITIES = createCapabilities(
52
+ MINIMAL_EFFORTS,
53
+ true
54
+ );
55
+ var GPT_5_PRO_REASONING_CAPABILITIES = createCapabilities(PRO_EFFORTS, true);
56
+ var GPT_5_HIGH_REASONING_CAPABILITIES = createCapabilities(HIGH_EFFORT, true);
57
+ var NO_REASONING_EFFORT_CAPABILITIES = {
58
+ reasoning: {
59
+ supported: false,
60
+ supportedEfforts: [],
61
+ restrictsSamplingParams: false
62
+ },
63
+ chatCompletions: {
64
+ maxTokensParameter: "max_completion_tokens"
65
+ }
66
+ };
67
+ var O_SERIES_MAX_REASONING_CAPABILITIES = createCapabilities(
68
+ MAX_EFFORTS,
69
+ false
70
+ );
71
+ var MODEL_CAPABILITIES = {
72
+ "gpt-5.6-sol": GPT_5_MAX_REASONING_CAPABILITIES,
73
+ "gpt-5.6-terra": SAMPLING_RESTRICTED_STANDARD_CAPABILITIES,
74
+ "gpt-5.6-luna": GPT_5_MINIMAL_REASONING_CAPABILITIES,
75
+ "gpt-5.5": GPT_5_MAX_REASONING_CAPABILITIES,
76
+ "gpt-5.5-pro": GPT_5_PRO_REASONING_CAPABILITIES,
77
+ "gpt-5.4": GPT_5_MAX_REASONING_CAPABILITIES,
78
+ "gpt-5.4-pro": GPT_5_PRO_REASONING_CAPABILITIES,
79
+ "gpt-5.4-mini": GPT_5_MAX_REASONING_CAPABILITIES,
80
+ "gpt-5.4-nano": GPT_5_MAX_REASONING_CAPABILITIES,
81
+ "gpt-5.3-codex": GPT_5_MAX_REASONING_CAPABILITIES,
82
+ "gpt-5.2": GPT_5_MAX_REASONING_CAPABILITIES,
83
+ "gpt-5.2-codex": GPT_5_MAX_REASONING_CAPABILITIES,
84
+ "gpt-5.2-pro": GPT_5_MAX_REASONING_CAPABILITIES,
85
+ "gpt-5.1-codex": GPT_5_MAX_REASONING_CAPABILITIES,
86
+ "gpt-5.1-codex-max": GPT_5_MAX_REASONING_CAPABILITIES,
87
+ "gpt-5.1-codex-mini": GPT_5_MAX_REASONING_CAPABILITIES,
88
+ "gpt-5.1": SAMPLING_RESTRICTED_STANDARD_CAPABILITIES,
89
+ "gpt-5": GPT_5_MINIMAL_REASONING_CAPABILITIES,
90
+ "gpt-5-mini": GPT_5_MINIMAL_REASONING_CAPABILITIES,
91
+ "gpt-5-nano": GPT_5_MINIMAL_REASONING_CAPABILITIES,
92
+ "gpt-5-pro": GPT_5_HIGH_REASONING_CAPABILITIES,
93
+ "gpt-5-codex": GPT_5_MAX_REASONING_CAPABILITIES,
94
+ "o3-pro": O_SERIES_MAX_REASONING_CAPABILITIES,
95
+ o3: DEFAULT_CAPABILITIES,
96
+ "o3-mini": DEFAULT_CAPABILITIES,
97
+ "o4-mini": DEFAULT_CAPABILITIES,
98
+ o1: DEFAULT_CAPABILITIES,
99
+ "o1-mini": NO_REASONING_EFFORT_CAPABILITIES
100
+ };
101
+ var OPENAI_REASONING_EFFORT_MAP = {
102
+ minimal: "minimal",
103
+ low: "low",
104
+ medium: "medium",
105
+ high: "high",
106
+ max: "xhigh"
107
+ };
108
+ function getOpenAIModelCapabilities(modelId) {
109
+ const normalizedModelId = normalizeModelId(modelId);
110
+ return MODEL_CAPABILITIES[normalizedModelId] ?? UNKNOWN_MODEL_CAPABILITIES;
111
+ }
112
+ function normalizeModelId(modelId) {
113
+ return stripModelDateSuffix(modelId);
114
+ }
115
+ function toOpenAIReasoningEffort(effort) {
116
+ return OPENAI_REASONING_EFFORT_MAP[effort];
117
+ }
118
+
119
+ // src/provider-options.ts
120
+ import { z } from "zod";
121
+ var openaiResponsesGenerateProviderOptionsSchema = z.object({
122
+ store: z.boolean().optional(),
123
+ serviceTier: z.enum(["auto", "default", "flex", "scale", "priority"]).optional(),
124
+ include: z.array(z.string()).optional(),
125
+ parallelToolCalls: z.boolean().optional(),
126
+ user: z.string().optional()
127
+ }).strict();
128
+ var openaiChatGenerateProviderOptionsSchema = openaiResponsesGenerateProviderOptionsSchema.omit({
129
+ include: true
130
+ }).extend({
131
+ stopSequences: z.array(z.string()).optional(),
132
+ frequencyPenalty: z.number().optional(),
133
+ presencePenalty: z.number().optional(),
134
+ seed: z.number().int().optional()
135
+ }).strict();
136
+ var openaiEmbedProviderOptionsSchema = z.object({
137
+ encodingFormat: z.enum(["float", "base64"]).optional(),
138
+ user: z.string().optional()
139
+ }).strict();
140
+ var openaiImageProviderOptionsSchema = z.object({
141
+ background: z.enum(["transparent", "opaque", "auto"]).optional(),
142
+ moderation: z.enum(["low", "auto"]).optional(),
143
+ outputCompression: z.number().int().min(0).max(100).optional(),
144
+ outputFormat: z.enum(["png", "jpeg", "webp"]).optional(),
145
+ quality: z.enum(["standard", "hd", "low", "medium", "high", "auto"]).optional(),
146
+ responseFormat: z.enum(["url", "b64_json"]).optional(),
147
+ style: z.enum(["vivid", "natural"]).optional(),
148
+ user: z.string().optional()
149
+ }).strict();
150
+ function parseOpenAIProviderOptions(providerOptions, schema) {
151
+ const rawOptions = providerOptions?.openai;
152
+ if (rawOptions === void 0) {
153
+ return void 0;
154
+ }
155
+ return schema.parse(rawOptions);
156
+ }
157
+ function parseOpenAIResponsesGenerateProviderOptions(providerOptions) {
158
+ return parseOpenAIProviderOptions(
159
+ providerOptions,
160
+ openaiResponsesGenerateProviderOptionsSchema
161
+ );
162
+ }
163
+ function parseOpenAIChatGenerateProviderOptions(providerOptions) {
164
+ return parseOpenAIProviderOptions(
165
+ providerOptions,
166
+ openaiChatGenerateProviderOptionsSchema
167
+ );
168
+ }
169
+ function parseOpenAIEmbedProviderOptions(providerOptions) {
170
+ return parseOpenAIProviderOptions(
171
+ providerOptions,
172
+ openaiEmbedProviderOptionsSchema
173
+ );
174
+ }
175
+ function parseOpenAIImageProviderOptions(providerOptions) {
176
+ return parseOpenAIProviderOptions(
177
+ providerOptions,
178
+ openaiImageProviderOptionsSchema
179
+ );
180
+ }
181
+ var openaiResponsesProviderOptionsSchema = openaiResponsesGenerateProviderOptionsSchema;
182
+ var openaiCompatProviderOptionsSchema = openaiChatGenerateProviderOptionsSchema;
183
+ var openaiCompatGenerateProviderOptionsSchema = openaiChatGenerateProviderOptionsSchema;
184
+
185
+ // src/chat-completions/chat-model.ts
186
+ import { createObjectStream, createChatStream } from "@core-ai/core-ai";
187
+
188
+ // src/chat-completions/chat-adapter.ts
189
+ import { clampReasoningEffort } from "@core-ai/core-ai";
190
+
191
+ // src/shared/tools.ts
192
+ import { zodSchemaToJsonSchema } from "@core-ai/core-ai";
193
+ function convertTools(tools) {
194
+ return Object.values(tools).map((tool) => ({
195
+ type: "function",
196
+ function: {
197
+ name: tool.name,
198
+ description: tool.description,
199
+ parameters: zodSchemaToJsonSchema(tool.parameters)
200
+ }
201
+ }));
202
+ }
203
+ function convertToolChoice(choice) {
204
+ if (typeof choice === "string") {
205
+ return choice;
206
+ }
207
+ return {
208
+ type: "function",
209
+ function: {
210
+ name: choice.toolName
211
+ }
212
+ };
213
+ }
214
+
215
+ // src/shared/utils.ts
216
+ import { ValidationError } from "@core-ai/core-ai";
217
+ function safeParseJsonObject(json) {
218
+ try {
219
+ const parsed = JSON.parse(json);
220
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
221
+ return parsed;
222
+ }
223
+ return {};
224
+ } catch {
225
+ return {};
226
+ }
227
+ }
228
+ function validateOpenAIReasoningConfig(modelId, options) {
229
+ if (!options.reasoning) {
230
+ return;
231
+ }
232
+ const capabilities = getOpenAIModelCapabilities(modelId);
233
+ if (!capabilities.reasoning.restrictsSamplingParams) {
234
+ return;
235
+ }
236
+ const restrictedSamplingParams = [
237
+ { name: "temperature", value: options.temperature },
238
+ { name: "topP", value: options.topP }
239
+ ];
240
+ for (const { name, value } of restrictedSamplingParams) {
241
+ if (value === void 0) {
242
+ continue;
243
+ }
244
+ throw new ValidationError(
245
+ `OpenAI model "${modelId}" does not support ${name} when reasoning is enabled`,
246
+ void 0,
247
+ "openai"
248
+ );
249
+ }
250
+ }
251
+
252
+ // src/chat-completions/compatibility.ts
253
+ function extractCompatibleReasoningText(source) {
254
+ const { reasoning_content, reasoning } = source;
255
+ if (typeof reasoning_content === "string" && reasoning_content.length > 0) {
256
+ return reasoning_content;
257
+ }
258
+ if (typeof reasoning === "string" && reasoning.length > 0) {
259
+ return reasoning;
260
+ }
261
+ return void 0;
262
+ }
263
+
264
+ // src/chat-completions/chat-adapter.ts
265
+ function convertMessages(messages) {
266
+ return messages.map(convertMessage);
267
+ }
268
+ function convertMessage(message) {
269
+ if (message.role === "system") {
270
+ return {
271
+ role: "system",
272
+ content: message.content
273
+ };
274
+ }
275
+ if (message.role === "user") {
276
+ return {
277
+ role: "user",
278
+ content: typeof message.content === "string" ? message.content : message.content.map(convertUserContentPart)
279
+ };
280
+ }
281
+ if (message.role === "assistant") {
282
+ const text = message.parts.flatMap((part) => {
283
+ if (part.type === "text") return [part.text];
284
+ if (part.type === "reasoning" && part.text.length > 0) {
285
+ return [`<thinking>${part.text}</thinking>`];
286
+ }
287
+ return [];
288
+ }).join("\n\n");
289
+ const toolCalls = message.parts.flatMap(
290
+ (part) => part.type === "tool-call" ? [part.toolCall] : []
291
+ );
292
+ return {
293
+ role: "assistant",
294
+ content: text.length > 0 ? text : null,
295
+ ...toolCalls.length > 0 ? {
296
+ tool_calls: toolCalls.map((toolCall) => ({
297
+ id: toolCall.id,
298
+ type: "function",
299
+ function: {
300
+ name: toolCall.name,
301
+ arguments: JSON.stringify(toolCall.arguments)
302
+ }
303
+ }))
304
+ } : {}
305
+ };
306
+ }
307
+ return {
308
+ role: "tool",
309
+ tool_call_id: message.toolCallId,
310
+ content: message.content
311
+ };
312
+ }
313
+ function convertUserContentPart(part) {
314
+ if (part.type === "text") {
315
+ return {
316
+ type: "text",
317
+ text: part.text
318
+ };
319
+ }
320
+ if (part.type === "image") {
321
+ const url = part.source.type === "url" ? part.source.url : `data:${part.source.mediaType};base64,${part.source.data}`;
322
+ return {
323
+ type: "image_url",
324
+ image_url: {
325
+ url
326
+ }
327
+ };
328
+ }
329
+ return {
330
+ type: "file",
331
+ file: {
332
+ file_data: part.data,
333
+ ...part.filename ? { filename: part.filename } : {}
334
+ }
335
+ };
336
+ }
337
+ function createGenerateRequest(modelId, options, adapterOptions = {}) {
338
+ return createRequest(modelId, options, false, adapterOptions);
339
+ }
340
+ function createStreamRequest(modelId, options, adapterOptions = {}) {
341
+ return createRequest(modelId, options, true, adapterOptions);
342
+ }
343
+ function createRequest(modelId, options, stream, adapterOptions) {
344
+ const openaiOptions = parseOpenAIChatGenerateProviderOptions(
345
+ options.providerOptions
346
+ );
347
+ const structuredOutputFormat = options.structuredOutputFormat;
348
+ return {
349
+ ...createRequestBase(modelId, options, adapterOptions),
350
+ ...stream ? {
351
+ stream: true,
352
+ stream_options: {
353
+ include_usage: true
354
+ }
355
+ } : {},
356
+ ...structuredOutputFormat ? {
357
+ response_format: {
358
+ type: "json_schema",
359
+ json_schema: {
360
+ name: structuredOutputFormat.name,
361
+ ...structuredOutputFormat.description ? {
362
+ description: structuredOutputFormat.description
363
+ } : {},
364
+ strict: structuredOutputFormat.strict,
365
+ schema: structuredOutputFormat.schema
366
+ }
367
+ }
368
+ } : {},
369
+ ...mapOpenAIProviderOptionsToRequestFields(openaiOptions)
370
+ };
371
+ }
372
+ function createRequestBase(modelId, options, adapterOptions) {
373
+ validateOpenAIReasoningConfig(modelId, options);
374
+ const reasoningFields = mapReasoningToRequestFields(modelId, options);
375
+ return {
376
+ model: modelId,
377
+ messages: convertMessages(options.messages),
378
+ ...options.tools && Object.keys(options.tools).length > 0 ? { tools: convertTools(options.tools) } : {},
379
+ ...options.toolChoice ? { tool_choice: convertToolChoice(options.toolChoice) } : {},
380
+ ...reasoningFields,
381
+ ...mapSamplingToRequestFields(modelId, options, adapterOptions)
382
+ };
383
+ }
384
+ function mapSamplingToRequestFields(modelId, options, adapterOptions) {
385
+ const maxTokensParameter = adapterOptions.maxTokensParameter ?? getOpenAIModelCapabilities(modelId).chatCompletions.maxTokensParameter;
386
+ return {
387
+ ...options.temperature !== void 0 ? { temperature: options.temperature } : {},
388
+ ...options.maxTokens !== void 0 ? maxTokensParameter === "max_completion_tokens" ? { max_completion_tokens: options.maxTokens } : { max_tokens: options.maxTokens } : {},
389
+ ...options.topP !== void 0 ? { top_p: options.topP } : {}
390
+ };
391
+ }
392
+ function mapOpenAIProviderOptionsToRequestFields(options) {
393
+ return {
394
+ ...options?.store !== void 0 ? { store: options.store } : {},
395
+ ...options?.serviceTier !== void 0 ? { service_tier: options.serviceTier } : {},
396
+ ...options?.parallelToolCalls !== void 0 ? { parallel_tool_calls: options.parallelToolCalls } : {},
397
+ ...options?.user !== void 0 ? { user: options.user } : {},
398
+ ...options?.stopSequences ? { stop: options.stopSequences } : {},
399
+ ...options?.frequencyPenalty !== void 0 ? { frequency_penalty: options.frequencyPenalty } : {},
400
+ ...options?.presencePenalty !== void 0 ? { presence_penalty: options.presencePenalty } : {},
401
+ ...options?.seed !== void 0 ? { seed: options.seed } : {}
402
+ };
403
+ }
404
+ function mapGenerateResponse(response, adapterOptions = {}) {
405
+ const firstChoice = response.choices[0];
406
+ if (!firstChoice) {
407
+ return {
408
+ parts: [],
409
+ content: null,
410
+ reasoning: null,
411
+ toolCalls: [],
412
+ finishReason: "unknown",
413
+ usage: {
414
+ inputTokens: 0,
415
+ outputTokens: 0,
416
+ inputTokenDetails: {
417
+ cacheReadTokens: 0,
418
+ cacheWriteTokens: 0
419
+ },
420
+ outputTokenDetails: {}
421
+ }
422
+ };
423
+ }
424
+ const reasoningTokens = response.usage?.completion_tokens_details?.reasoning_tokens;
425
+ const content = extractTextContent(firstChoice.message.content);
426
+ const reasoning = adapterOptions.compatibility ? extractCompatibleReasoningText(firstChoice.message) ?? null : null;
427
+ const toolCalls = parseToolCalls(firstChoice.message.tool_calls);
428
+ const parts = createAssistantParts(reasoning, content, toolCalls);
429
+ return {
430
+ parts,
431
+ content,
432
+ reasoning,
433
+ toolCalls,
434
+ finishReason: mapFinishReason(firstChoice.finish_reason),
435
+ usage: {
436
+ inputTokens: response.usage?.prompt_tokens ?? 0,
437
+ outputTokens: response.usage?.completion_tokens ?? 0,
438
+ inputTokenDetails: {
439
+ cacheReadTokens: response.usage?.prompt_tokens_details?.cached_tokens ?? 0,
440
+ cacheWriteTokens: 0
441
+ },
442
+ outputTokenDetails: {
443
+ ...reasoningTokens !== void 0 ? { reasoningTokens } : {}
444
+ }
445
+ }
446
+ };
447
+ }
448
+ function parseToolCalls(calls) {
449
+ if (!calls) {
450
+ return [];
451
+ }
452
+ return calls.flatMap((toolCall) => {
453
+ if (toolCall.type !== "function") {
454
+ return [];
455
+ }
456
+ return [mapFunctionToolCall(toolCall)];
457
+ });
458
+ }
459
+ function mapFunctionToolCall(toolCall) {
460
+ return {
461
+ id: toolCall.id,
462
+ name: toolCall.function.name,
463
+ arguments: safeParseJsonObject(toolCall.function.arguments)
464
+ };
465
+ }
466
+ function mapFinishReason(reason) {
467
+ if (reason === "stop") {
468
+ return "stop";
469
+ }
470
+ if (reason === "length") {
471
+ return "length";
472
+ }
473
+ if (reason === "tool_calls" || reason === "function_call") {
474
+ return "tool-calls";
475
+ }
476
+ if (reason === "content_filter") {
477
+ return "content-filter";
478
+ }
479
+ return "unknown";
480
+ }
481
+ async function* transformStream(stream, adapterOptions = {}) {
482
+ const bufferedToolCalls = /* @__PURE__ */ new Map();
483
+ const emittedToolCalls = /* @__PURE__ */ new Set();
484
+ let finishReason = "unknown";
485
+ let textOpen = false;
486
+ let reasoningOpen = false;
487
+ let usage = {
488
+ inputTokens: 0,
489
+ outputTokens: 0,
490
+ inputTokenDetails: {
491
+ cacheReadTokens: 0,
492
+ cacheWriteTokens: 0
493
+ },
494
+ outputTokenDetails: {}
495
+ };
496
+ const startText = function* () {
497
+ if (textOpen) {
498
+ return;
499
+ }
500
+ textOpen = true;
501
+ yield { type: "text-start" };
502
+ };
503
+ const closeText = function* () {
504
+ if (!textOpen) {
505
+ return;
506
+ }
507
+ textOpen = false;
508
+ yield { type: "text-end" };
509
+ };
510
+ const startReasoning = function* () {
511
+ if (reasoningOpen) {
512
+ return;
513
+ }
514
+ reasoningOpen = true;
515
+ yield { type: "reasoning-start" };
516
+ };
517
+ const closeReasoning = function* () {
518
+ if (!reasoningOpen) {
519
+ return;
520
+ }
521
+ reasoningOpen = false;
522
+ yield { type: "reasoning-end" };
523
+ };
524
+ for await (const chunk of stream) {
525
+ if (chunk.usage) {
526
+ const reasoningTokens = chunk.usage.completion_tokens_details?.reasoning_tokens;
527
+ usage = {
528
+ inputTokens: chunk.usage.prompt_tokens ?? 0,
529
+ outputTokens: chunk.usage.completion_tokens ?? 0,
530
+ inputTokenDetails: {
531
+ cacheReadTokens: chunk.usage.prompt_tokens_details?.cached_tokens ?? 0,
532
+ cacheWriteTokens: 0
533
+ },
534
+ outputTokenDetails: {
535
+ ...reasoningTokens !== void 0 ? { reasoningTokens } : {}
536
+ }
537
+ };
538
+ }
539
+ const choice = chunk.choices[0];
540
+ if (!choice) {
541
+ continue;
542
+ }
543
+ const reasoningDelta = adapterOptions.compatibility ? extractCompatibleReasoningText(choice.delta) : void 0;
544
+ if (reasoningDelta !== void 0) {
545
+ yield* closeText();
546
+ yield* startReasoning();
547
+ yield {
548
+ type: "reasoning-delta",
549
+ text: reasoningDelta
550
+ };
551
+ }
552
+ if (choice.delta.content) {
553
+ yield* closeReasoning();
554
+ yield* startText();
555
+ yield {
556
+ type: "text-delta",
557
+ text: choice.delta.content
558
+ };
559
+ }
560
+ if (choice.delta.tool_calls) {
561
+ yield* closeReasoning();
562
+ yield* closeText();
563
+ for (const partialToolCall of choice.delta.tool_calls) {
564
+ const current = bufferedToolCalls.get(
565
+ partialToolCall.index
566
+ ) ?? {
567
+ id: partialToolCall.id ?? `tool-${partialToolCall.index}`,
568
+ name: partialToolCall.function?.name ?? "",
569
+ arguments: ""
570
+ };
571
+ const wasNew = !bufferedToolCalls.has(partialToolCall.index);
572
+ if (partialToolCall.id) {
573
+ current.id = partialToolCall.id;
574
+ }
575
+ if (partialToolCall.function?.name) {
576
+ current.name = partialToolCall.function.name;
577
+ }
578
+ if (partialToolCall.function?.arguments) {
579
+ current.arguments += partialToolCall.function.arguments;
580
+ yield {
581
+ type: "tool-call-delta",
582
+ toolCallId: current.id,
583
+ argumentsDelta: partialToolCall.function.arguments
584
+ };
585
+ }
586
+ bufferedToolCalls.set(partialToolCall.index, current);
587
+ if (wasNew) {
588
+ yield {
589
+ type: "tool-call-start",
590
+ toolCallId: current.id,
591
+ toolName: current.name
592
+ };
593
+ }
594
+ }
595
+ }
596
+ if (choice.finish_reason) {
597
+ finishReason = mapFinishReason(choice.finish_reason);
598
+ }
599
+ if (finishReason === "tool-calls") {
600
+ yield* closeReasoning();
601
+ yield* closeText();
602
+ for (const toolCall of bufferedToolCalls.values()) {
603
+ if (emittedToolCalls.has(toolCall.id)) {
604
+ continue;
605
+ }
606
+ emittedToolCalls.add(toolCall.id);
607
+ yield {
608
+ type: "tool-call-end",
609
+ toolCall: {
610
+ id: toolCall.id,
611
+ name: toolCall.name,
612
+ arguments: safeParseJsonObject(toolCall.arguments)
613
+ }
614
+ };
615
+ }
616
+ }
617
+ }
618
+ yield* closeReasoning();
619
+ yield* closeText();
620
+ yield {
621
+ type: "finish",
622
+ finishReason,
623
+ usage
624
+ };
625
+ }
626
+ function mapReasoningToRequestFields(modelId, options) {
627
+ if (!options.reasoning) {
628
+ return {};
629
+ }
630
+ const capabilities = getOpenAIModelCapabilities(modelId);
631
+ if (!capabilities.reasoning.supported) {
632
+ return {};
633
+ }
634
+ const clampedEffort = clampReasoningEffort(
635
+ options.reasoning.effort,
636
+ capabilities.reasoning.supportedEfforts
637
+ );
638
+ return {
639
+ reasoning_effort: toOpenAIReasoningEffort(clampedEffort)
640
+ };
641
+ }
642
+ function createAssistantParts(reasoning, content, toolCalls) {
643
+ const parts = [];
644
+ if (reasoning) {
645
+ parts.push({
646
+ type: "reasoning",
647
+ text: reasoning
648
+ });
649
+ }
650
+ if (content) {
651
+ parts.push({
652
+ type: "text",
653
+ text: content
654
+ });
655
+ }
656
+ for (const toolCall of toolCalls) {
657
+ parts.push({
658
+ type: "tool-call",
659
+ toolCall
660
+ });
661
+ }
662
+ return parts;
663
+ }
664
+ function extractTextContent(content) {
665
+ if (typeof content === "string") {
666
+ return content;
667
+ }
668
+ if (!Array.isArray(content)) {
669
+ return null;
670
+ }
671
+ const text = content.flatMap((item) => {
672
+ if (!item || typeof item !== "object") {
673
+ return [];
674
+ }
675
+ const textValue = item.text;
676
+ return typeof textValue === "string" ? [textValue] : [];
677
+ }).join("");
678
+ return text.length > 0 ? text : null;
679
+ }
680
+
681
+ // src/openai-error.ts
682
+ import { APIError, APIUserAbortError } from "openai";
683
+ import { AbortedError, ProviderError } from "@core-ai/core-ai";
684
+ function isOpenAIAbortError(error) {
685
+ return error instanceof APIUserAbortError || error instanceof Error && error.name === "AbortError";
686
+ }
687
+ function wrapOpenAIError(error, provider = "openai") {
688
+ if (isOpenAIAbortError(error)) {
689
+ return new AbortedError(error, provider);
690
+ }
691
+ if (error instanceof APIError) {
692
+ return new ProviderError(
693
+ error.message,
694
+ provider,
695
+ error.status,
696
+ error
697
+ );
698
+ }
699
+ return new ProviderError(
700
+ error instanceof Error ? error.message : String(error),
701
+ provider,
702
+ void 0,
703
+ error
704
+ );
705
+ }
706
+
707
+ // src/shared/structured-output.ts
708
+ import { zodTextFormat } from "openai/helpers/zod";
709
+ import {
710
+ StructuredOutputNoObjectGeneratedError,
711
+ StructuredOutputParseError,
712
+ StructuredOutputValidationError
713
+ } from "@core-ai/core-ai";
714
+ var DEFAULT_STRUCTURED_OUTPUT_NAME = "core_ai_generate_object";
715
+ var DEFAULT_STRUCTURED_OUTPUT_DESCRIPTION = "Return a JSON object that matches the requested schema.";
716
+ function getStructuredOutputName(options) {
717
+ return options.schemaName?.trim() || DEFAULT_STRUCTURED_OUTPUT_NAME;
718
+ }
719
+ function createStructuredOutputRequestOptions(options, mode = "native") {
720
+ const baseOptions = {
721
+ messages: options.messages,
722
+ reasoning: options.reasoning,
723
+ temperature: options.temperature,
724
+ maxTokens: options.maxTokens,
725
+ topP: options.topP,
726
+ providerOptions: options.providerOptions,
727
+ signal: options.signal
728
+ };
729
+ if (mode === "tool") {
730
+ const name = getStructuredOutputName(options);
731
+ return {
732
+ ...baseOptions,
733
+ tools: {
734
+ structured_output: {
735
+ name,
736
+ description: options.schemaDescription ?? DEFAULT_STRUCTURED_OUTPUT_DESCRIPTION,
737
+ parameters: options.schema
738
+ }
739
+ },
740
+ toolChoice: {
741
+ type: "tool",
742
+ toolName: name
743
+ }
744
+ };
745
+ }
746
+ return {
747
+ ...baseOptions,
748
+ structuredOutputFormat: createOpenAIStructuredOutputFormat(options)
749
+ };
750
+ }
751
+ function createOpenAIStructuredOutputFormat(options) {
752
+ const name = getStructuredOutputName(options);
753
+ const format = zodTextFormat(
754
+ options.schema,
755
+ name,
756
+ options.schemaDescription ? { description: options.schemaDescription } : void 0
757
+ );
758
+ return {
759
+ type: "json_schema",
760
+ name,
761
+ ...options.schemaDescription ? { description: options.schemaDescription } : {},
762
+ strict: true,
763
+ schema: format.schema
764
+ };
765
+ }
766
+ function extractStructuredObject(result, schema, provider, structuredOutputName) {
767
+ const structuredToolCall = result.toolCalls.find(
768
+ (toolCall) => toolCall.name === structuredOutputName
769
+ );
770
+ if (structuredToolCall) {
771
+ return validateStructuredToolArguments(
772
+ schema,
773
+ structuredToolCall.arguments,
774
+ provider
775
+ );
776
+ }
777
+ const rawOutput = result.content?.trim();
778
+ if (rawOutput && rawOutput.length > 0) {
779
+ return parseAndValidateStructuredPayload(schema, rawOutput, provider);
780
+ }
781
+ throw new StructuredOutputNoObjectGeneratedError(
782
+ "model did not emit a structured object payload",
783
+ provider
784
+ );
785
+ }
786
+ async function* transformStructuredOutputStream(stream, schema, provider, structuredOutputName) {
787
+ let validatedObject;
788
+ let contentBuffer = "";
789
+ const toolArgumentDeltas = /* @__PURE__ */ new Map();
790
+ for await (const event of stream) {
791
+ if (event.type === "text-delta") {
792
+ contentBuffer += event.text;
793
+ yield {
794
+ type: "object-delta",
795
+ text: event.text
796
+ };
797
+ continue;
798
+ }
799
+ if (event.type === "tool-call-delta") {
800
+ const previous = toolArgumentDeltas.get(event.toolCallId) ?? "";
801
+ toolArgumentDeltas.set(
802
+ event.toolCallId,
803
+ `${previous}${event.argumentsDelta}`
804
+ );
805
+ yield {
806
+ type: "object-delta",
807
+ text: event.argumentsDelta
808
+ };
809
+ continue;
810
+ }
811
+ if (event.type === "tool-call-end" && event.toolCall.name === structuredOutputName) {
812
+ validatedObject = validateStructuredToolArguments(
813
+ schema,
814
+ event.toolCall.arguments,
815
+ provider
816
+ );
817
+ yield {
818
+ type: "object",
819
+ object: validatedObject
820
+ };
821
+ continue;
822
+ }
823
+ if (event.type === "finish") {
824
+ if (validatedObject === void 0) {
825
+ const fallbackPayload = getFallbackStructuredPayload(
826
+ contentBuffer,
827
+ toolArgumentDeltas
828
+ );
829
+ if (!fallbackPayload) {
830
+ throw new StructuredOutputNoObjectGeneratedError(
831
+ "structured output stream ended without an object payload",
832
+ provider
833
+ );
834
+ }
835
+ validatedObject = parseAndValidateStructuredPayload(
836
+ schema,
837
+ fallbackPayload,
838
+ provider
839
+ );
840
+ yield {
841
+ type: "object",
842
+ object: validatedObject
843
+ };
844
+ }
845
+ yield {
846
+ type: "finish",
847
+ finishReason: event.finishReason,
848
+ usage: event.usage
849
+ };
850
+ }
851
+ }
852
+ }
853
+ function getFallbackStructuredPayload(contentBuffer, toolArgumentDeltas) {
854
+ for (const delta of toolArgumentDeltas.values()) {
855
+ const trimmed = delta.trim();
856
+ if (trimmed.length > 0) {
857
+ return trimmed;
858
+ }
859
+ }
860
+ const trimmedContent = contentBuffer.trim();
861
+ if (trimmedContent.length > 0) {
862
+ return trimmedContent;
863
+ }
864
+ return void 0;
865
+ }
866
+ function validateStructuredToolArguments(schema, toolArguments, provider) {
867
+ return validateStructuredObject(
868
+ schema,
869
+ toolArguments,
870
+ provider,
871
+ JSON.stringify(toolArguments)
872
+ );
873
+ }
874
+ function parseAndValidateStructuredPayload(schema, rawPayload, provider) {
875
+ const parsedPayload = parseJson(rawPayload, provider);
876
+ return validateStructuredObject(
877
+ schema,
878
+ parsedPayload,
879
+ provider,
880
+ rawPayload
881
+ );
882
+ }
883
+ function parseJson(rawOutput, provider) {
884
+ try {
885
+ return JSON.parse(rawOutput);
886
+ } catch (error) {
887
+ throw new StructuredOutputParseError(
888
+ "failed to parse structured output as JSON",
889
+ provider,
890
+ {
891
+ rawOutput,
892
+ cause: error
893
+ }
894
+ );
895
+ }
896
+ }
897
+ function validateStructuredObject(schema, value, provider, rawOutput) {
898
+ const parsed = schema.safeParse(value);
899
+ if (parsed.success) {
900
+ return parsed.data;
901
+ }
902
+ throw new StructuredOutputValidationError(
903
+ "structured output does not match schema",
904
+ provider,
905
+ formatZodIssues(parsed.error.issues),
906
+ {
907
+ rawOutput
908
+ }
909
+ );
910
+ }
911
+ function formatZodIssues(issues) {
912
+ return issues.map((issue) => {
913
+ const path = issue.path.length > 0 ? issue.path.map((segment) => String(segment)).join(".") : "<root>";
914
+ return `${path}: ${issue.message}`;
915
+ });
916
+ }
917
+
918
+ // src/chat-completions/chat-model.ts
919
+ function createOpenAIChatCompletionsModel(client, modelId, modelOptions = {}) {
920
+ const provider = modelOptions.providerId ?? "openai";
921
+ const structuredOutputMode = modelOptions.structuredOutputMode ?? (modelOptions.compatibility ? "tool" : "native");
922
+ const nonStandardReasoning = modelOptions.nonStandardReasoning ?? modelOptions.compatibility;
923
+ async function callOpenAIChatCompletionsApi(request, signal) {
924
+ try {
925
+ return await client.chat.completions.create(request, {
926
+ signal
927
+ });
928
+ } catch (error) {
929
+ throw wrapOpenAIError(error, provider);
930
+ }
931
+ }
932
+ async function generateChat(options) {
933
+ const request = createGenerateRequest(modelId, options, modelOptions);
934
+ const response = await callOpenAIChatCompletionsApi(request, options.signal);
935
+ return mapGenerateResponse(response, {
936
+ compatibility: nonStandardReasoning
937
+ });
938
+ }
939
+ async function streamChat(options) {
940
+ const request = createStreamRequest(modelId, options, modelOptions);
941
+ return createChatStream(
942
+ async () => transformStream(
943
+ await callOpenAIChatCompletionsApi(request, options.signal),
944
+ {
945
+ compatibility: nonStandardReasoning
946
+ }
947
+ ),
948
+ { signal: options.signal }
949
+ );
950
+ }
951
+ return {
952
+ provider,
953
+ modelId,
954
+ capabilities: getOpenAIModelCapabilities(modelId),
955
+ generate: generateChat,
956
+ stream: streamChat,
957
+ async generateObject(options) {
958
+ const structuredOptions = createStructuredOutputRequestOptions(
959
+ options,
960
+ structuredOutputMode
961
+ );
962
+ const result = await generateChat(structuredOptions);
963
+ const structuredOutputName = getStructuredOutputName(options);
964
+ const object = extractStructuredObject(
965
+ result,
966
+ options.schema,
967
+ provider,
968
+ structuredOutputName
969
+ );
970
+ return {
971
+ object,
972
+ finishReason: result.finishReason,
973
+ usage: result.usage
974
+ };
975
+ },
976
+ async streamObject(options) {
977
+ const structuredOptions = createStructuredOutputRequestOptions(
978
+ options,
979
+ structuredOutputMode
980
+ );
981
+ const stream = await streamChat(structuredOptions);
982
+ const structuredOutputName = getStructuredOutputName(options);
983
+ return createObjectStream(
984
+ transformStructuredOutputStream(
985
+ stream,
986
+ options.schema,
987
+ provider,
988
+ structuredOutputName
989
+ ),
990
+ {
991
+ signal: options.signal
992
+ }
993
+ );
994
+ }
995
+ };
996
+ }
997
+
998
+ // src/shared/provider-factory.ts
999
+ import OpenAI from "openai";
1000
+
1001
+ // src/chat-model.ts
1002
+ import { createObjectStream as createObjectStream2, createChatStream as createChatStream2 } from "@core-ai/core-ai";
1003
+
1004
+ // src/chat-adapter.ts
1005
+ import { getProviderMetadata, clampReasoningEffort as clampReasoningEffort2 } from "@core-ai/core-ai";
1006
+ var ENCRYPTED_REASONING_INCLUDE = "reasoning.encrypted_content";
1007
+ var REASONING_SUMMARY_SEPARATOR = "\n\n";
1008
+ function convertMessages2(messages) {
1009
+ return messages.flatMap(convertMessage2);
1010
+ }
1011
+ function convertMessage2(message) {
1012
+ if (message.role === "system") {
1013
+ return [
1014
+ {
1015
+ role: "developer",
1016
+ content: message.content
1017
+ }
1018
+ ];
1019
+ }
1020
+ if (message.role === "user") {
1021
+ return [
1022
+ {
1023
+ role: "user",
1024
+ content: typeof message.content === "string" ? message.content : message.content.map(convertUserContentPart2)
1025
+ }
1026
+ ];
1027
+ }
1028
+ if (message.role === "assistant") {
1029
+ return convertAssistantMessage(message.parts);
1030
+ }
1031
+ return [
1032
+ {
1033
+ type: "function_call_output",
1034
+ call_id: message.toolCallId,
1035
+ output: message.content
1036
+ }
1037
+ ];
1038
+ }
1039
+ function convertAssistantMessage(parts) {
1040
+ const items = [];
1041
+ const textParts = [];
1042
+ const flushTextBuffer = () => {
1043
+ if (textParts.length === 0) {
1044
+ return;
1045
+ }
1046
+ items.push({
1047
+ role: "assistant",
1048
+ content: textParts.join("\n\n")
1049
+ });
1050
+ textParts.length = 0;
1051
+ };
1052
+ for (const part of parts) {
1053
+ if (part.type === "text") {
1054
+ textParts.push(part.text);
1055
+ continue;
1056
+ }
1057
+ if (part.type === "reasoning") {
1058
+ if (getProviderMetadata(
1059
+ part.providerMetadata,
1060
+ "openai"
1061
+ ) == null) {
1062
+ if (part.text.length > 0) {
1063
+ textParts.push(`<thinking>${part.text}</thinking>`);
1064
+ }
1065
+ continue;
1066
+ }
1067
+ flushTextBuffer();
1068
+ const encryptedContent = getEncryptedReasoningContent(part);
1069
+ items.push({
1070
+ type: "reasoning",
1071
+ summary: [
1072
+ {
1073
+ type: "summary_text",
1074
+ text: part.text
1075
+ }
1076
+ ],
1077
+ ...encryptedContent ? { encrypted_content: encryptedContent } : {}
1078
+ });
1079
+ continue;
1080
+ }
1081
+ flushTextBuffer();
1082
+ items.push({
1083
+ type: "function_call",
1084
+ call_id: part.toolCall.id,
1085
+ name: part.toolCall.name,
1086
+ arguments: JSON.stringify(part.toolCall.arguments)
1087
+ });
1088
+ }
1089
+ flushTextBuffer();
1090
+ return items;
1091
+ }
1092
+ function getEncryptedReasoningContent(part) {
1093
+ const { encryptedContent } = getProviderMetadata(
1094
+ part.providerMetadata,
1095
+ "openai"
1096
+ ) ?? {};
1097
+ return typeof encryptedContent === "string" && encryptedContent.length > 0 ? encryptedContent : void 0;
1098
+ }
1099
+ function convertUserContentPart2(part) {
1100
+ if (part.type === "text") {
1101
+ return {
1102
+ type: "input_text",
1103
+ text: part.text
1104
+ };
1105
+ }
1106
+ if (part.type === "image") {
1107
+ const imageUrl = part.source.type === "url" ? part.source.url : `data:${part.source.mediaType};base64,${part.source.data}`;
1108
+ return {
1109
+ type: "input_image",
1110
+ image_url: imageUrl
1111
+ };
1112
+ }
1113
+ return {
1114
+ type: "input_file",
1115
+ file_data: part.data,
1116
+ ...part.filename ? { filename: part.filename } : {}
1117
+ };
1118
+ }
1119
+ function createGenerateRequest2(modelId, options) {
1120
+ return createRequest2(
1121
+ modelId,
1122
+ options,
1123
+ false
1124
+ );
1125
+ }
1126
+ function createStreamRequest2(modelId, options) {
1127
+ return createRequest2(
1128
+ modelId,
1129
+ options,
1130
+ true
1131
+ );
1132
+ }
1133
+ function createRequest2(modelId, options, stream) {
1134
+ const openaiOptions = parseOpenAIResponsesGenerateProviderOptions(
1135
+ options.providerOptions
1136
+ );
1137
+ const request = {
1138
+ ...createRequestBase2(modelId, options),
1139
+ ...stream ? { stream: true } : {},
1140
+ ...options.structuredOutputFormat ? { text: { format: options.structuredOutputFormat } } : {},
1141
+ ...mapOpenAIProviderOptionsToRequestFields2(openaiOptions)
1142
+ };
1143
+ if (options.reasoning && getOpenAIModelCapabilities(modelId).reasoning.supported) {
1144
+ request.include = mergeInclude(request.include, [
1145
+ ENCRYPTED_REASONING_INCLUDE
1146
+ ]);
1147
+ }
1148
+ return request;
1149
+ }
1150
+ function createRequestBase2(modelId, options) {
1151
+ validateOpenAIReasoningConfig(modelId, options);
1152
+ return {
1153
+ model: modelId,
1154
+ store: false,
1155
+ input: convertMessages2(options.messages),
1156
+ ...options.tools && Object.keys(options.tools).length > 0 ? { tools: convertResponseTools(options.tools) } : {},
1157
+ ...options.toolChoice ? { tool_choice: convertResponseToolChoice(options.toolChoice) } : {},
1158
+ ...mapReasoningToRequestFields2(modelId, options),
1159
+ ...mapSamplingToRequestFields2(options)
1160
+ };
1161
+ }
1162
+ function convertResponseTools(tools) {
1163
+ return convertTools(tools).map((tool) => ({
1164
+ type: "function",
1165
+ name: tool.function.name,
1166
+ description: tool.function.description,
1167
+ parameters: tool.function.parameters
1168
+ }));
1169
+ }
1170
+ function convertResponseToolChoice(choice) {
1171
+ const converted = convertToolChoice(choice);
1172
+ if (typeof converted === "string") {
1173
+ return converted;
1174
+ }
1175
+ return {
1176
+ type: "function",
1177
+ name: converted.function.name
1178
+ };
1179
+ }
1180
+ function mergeInclude(value, requiredIncludes) {
1181
+ const include = Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
1182
+ for (const requiredInclude of requiredIncludes) {
1183
+ if (!include.includes(requiredInclude)) {
1184
+ include.push(requiredInclude);
1185
+ }
1186
+ }
1187
+ return include.length > 0 ? include : void 0;
1188
+ }
1189
+ function mapSamplingToRequestFields2(options) {
1190
+ return {
1191
+ ...options.temperature !== void 0 ? { temperature: options.temperature } : {},
1192
+ ...options.maxTokens !== void 0 ? { max_output_tokens: options.maxTokens } : {},
1193
+ ...options.topP !== void 0 ? { top_p: options.topP } : {}
1194
+ };
1195
+ }
1196
+ function mapOpenAIProviderOptionsToRequestFields2(options) {
1197
+ return {
1198
+ ...options?.store !== void 0 ? { store: options.store } : {},
1199
+ ...options?.serviceTier !== void 0 ? { service_tier: options.serviceTier } : {},
1200
+ ...options?.include ? { include: options.include } : {},
1201
+ ...options?.parallelToolCalls !== void 0 ? { parallel_tool_calls: options.parallelToolCalls } : {},
1202
+ ...options?.user !== void 0 ? { user: options.user } : {}
1203
+ };
1204
+ }
1205
+ function mapGenerateResponse2(response) {
1206
+ const parts = [];
1207
+ for (const item of response.output) {
1208
+ if (isReasoningItem(item)) {
1209
+ const reasoningPart = mapReasoningPart(item);
1210
+ if (reasoningPart) {
1211
+ parts.push(reasoningPart);
1212
+ }
1213
+ continue;
1214
+ }
1215
+ if (isOutputMessage(item)) {
1216
+ parts.push(...mapMessageTextParts(item));
1217
+ continue;
1218
+ }
1219
+ if (isFunctionToolCall(item)) {
1220
+ parts.push({
1221
+ type: "tool-call",
1222
+ toolCall: {
1223
+ id: item.call_id,
1224
+ name: item.name,
1225
+ arguments: safeParseJsonObject(item.arguments)
1226
+ }
1227
+ });
1228
+ }
1229
+ }
1230
+ const content = getTextContent(parts);
1231
+ const reasoning = getReasoningText(parts);
1232
+ const toolCalls = getToolCalls(parts);
1233
+ return {
1234
+ parts,
1235
+ content,
1236
+ reasoning,
1237
+ toolCalls,
1238
+ finishReason: mapFinishReason2(response, toolCalls.length > 0),
1239
+ usage: mapUsage(response.usage)
1240
+ };
1241
+ }
1242
+ function mapReasoningPart(item) {
1243
+ const text = getReasoningSummaryText(item.summary);
1244
+ const encryptedContent = typeof item.encrypted_content === "string" && item.encrypted_content.length > 0 ? item.encrypted_content : void 0;
1245
+ if (text.length === 0 && !encryptedContent) {
1246
+ return null;
1247
+ }
1248
+ return {
1249
+ type: "reasoning",
1250
+ text,
1251
+ providerMetadata: {
1252
+ openai: { ...encryptedContent ? { encryptedContent } : {} }
1253
+ }
1254
+ };
1255
+ }
1256
+ function getReasoningSummaryText(summary) {
1257
+ return summary.map((item) => item.text).join(REASONING_SUMMARY_SEPARATOR);
1258
+ }
1259
+ function mapMessageTextParts(message) {
1260
+ return message.content.flatMap(
1261
+ (contentItem) => contentItem.type === "output_text" && contentItem.text.length > 0 ? [{ type: "text", text: contentItem.text }] : []
1262
+ );
1263
+ }
1264
+ function getTextContent(parts) {
1265
+ return getJoinedPartText(parts, "text", "");
1266
+ }
1267
+ function getReasoningText(parts) {
1268
+ return getJoinedPartText(parts, "reasoning", REASONING_SUMMARY_SEPARATOR);
1269
+ }
1270
+ function getJoinedPartText(parts, type, separator) {
1271
+ const text = parts.flatMap(
1272
+ (part) => part.type === type && "text" in part ? [part.text] : []
1273
+ ).join(separator);
1274
+ return text.length > 0 ? text : null;
1275
+ }
1276
+ function getToolCalls(parts) {
1277
+ return parts.flatMap(
1278
+ (part) => part.type === "tool-call" ? [part.toolCall] : []
1279
+ );
1280
+ }
1281
+ function mapFinishReason2(response, hasToolCalls) {
1282
+ const incompleteReason = response.incomplete_details?.reason;
1283
+ if (incompleteReason === "max_output_tokens") {
1284
+ return "length";
1285
+ }
1286
+ if (incompleteReason === "content_filter") {
1287
+ return "content-filter";
1288
+ }
1289
+ if (hasToolCalls) {
1290
+ return "tool-calls";
1291
+ }
1292
+ if (response.status === "completed") {
1293
+ return "stop";
1294
+ }
1295
+ return "unknown";
1296
+ }
1297
+ function mapUsage(usage) {
1298
+ const reasoningTokens = usage?.output_tokens_details?.reasoning_tokens;
1299
+ return {
1300
+ inputTokens: usage?.input_tokens ?? 0,
1301
+ outputTokens: usage?.output_tokens ?? 0,
1302
+ inputTokenDetails: {
1303
+ cacheReadTokens: usage?.input_tokens_details?.cached_tokens ?? 0,
1304
+ cacheWriteTokens: 0
1305
+ },
1306
+ outputTokenDetails: {
1307
+ ...reasoningTokens !== void 0 ? { reasoningTokens } : {}
1308
+ }
1309
+ };
1310
+ }
1311
+ function getReasoningSummaryKey(part) {
1312
+ return `${part.itemId}:${part.summaryIndex}`;
1313
+ }
1314
+ function isSameReasoningSummaryPart(left, right) {
1315
+ return left.itemId === right.itemId && left.summaryIndex === right.summaryIndex;
1316
+ }
1317
+ function getReasoningStartTransition(reasoningStarted) {
1318
+ if (reasoningStarted) {
1319
+ return {
1320
+ nextReasoningStarted: true,
1321
+ event: null
1322
+ };
1323
+ }
1324
+ return {
1325
+ nextReasoningStarted: true,
1326
+ event: { type: "reasoning-start" }
1327
+ };
1328
+ }
1329
+ function getReasoningEndTransition(reasoningStarted, providerMetadata) {
1330
+ if (!reasoningStarted) {
1331
+ return {
1332
+ nextReasoningStarted: false,
1333
+ event: null
1334
+ };
1335
+ }
1336
+ return {
1337
+ nextReasoningStarted: false,
1338
+ event: {
1339
+ type: "reasoning-end",
1340
+ providerMetadata
1341
+ }
1342
+ };
1343
+ }
1344
+ async function* transformStream2(stream) {
1345
+ const bufferedToolCalls = /* @__PURE__ */ new Map();
1346
+ const emittedToolCalls = /* @__PURE__ */ new Set();
1347
+ const startedToolCalls = /* @__PURE__ */ new Set();
1348
+ const seenSummaryDeltas = /* @__PURE__ */ new Set();
1349
+ const emittedReasoningItems = /* @__PURE__ */ new Set();
1350
+ let latestResponse;
1351
+ let textOpen = false;
1352
+ let reasoningStarted = false;
1353
+ let latestReasoningSummaryPart;
1354
+ const upsertBufferedToolCall = (outputIndex, getNextToolCall) => {
1355
+ const nextToolCall = getNextToolCall(
1356
+ bufferedToolCalls.get(outputIndex)
1357
+ );
1358
+ bufferedToolCalls.set(outputIndex, nextToolCall);
1359
+ return nextToolCall;
1360
+ };
1361
+ const getNextReasoningStartEvent = () => {
1362
+ const transition = getReasoningStartTransition(reasoningStarted);
1363
+ reasoningStarted = transition.nextReasoningStarted;
1364
+ return transition.event;
1365
+ };
1366
+ const startText = function* () {
1367
+ if (textOpen) {
1368
+ return;
1369
+ }
1370
+ textOpen = true;
1371
+ yield { type: "text-start" };
1372
+ };
1373
+ const closeText = function* () {
1374
+ if (!textOpen) {
1375
+ return;
1376
+ }
1377
+ textOpen = false;
1378
+ yield { type: "text-end" };
1379
+ };
1380
+ const getNextReasoningEndEvent = (providerMetadata) => {
1381
+ const transition = getReasoningEndTransition(
1382
+ reasoningStarted,
1383
+ providerMetadata
1384
+ );
1385
+ reasoningStarted = transition.nextReasoningStarted;
1386
+ if (transition.event) {
1387
+ latestReasoningSummaryPart = void 0;
1388
+ }
1389
+ return transition.event;
1390
+ };
1391
+ const getReasoningSummarySeparatorEvent = (currentPart) => {
1392
+ const previousPart = latestReasoningSummaryPart;
1393
+ latestReasoningSummaryPart = currentPart;
1394
+ if (previousPart === void 0 || isSameReasoningSummaryPart(previousPart, currentPart)) {
1395
+ return null;
1396
+ }
1397
+ return {
1398
+ type: "reasoning-delta",
1399
+ text: REASONING_SUMMARY_SEPARATOR
1400
+ };
1401
+ };
1402
+ for await (const event of stream) {
1403
+ if (event.type === "response.reasoning_summary_text.delta") {
1404
+ const summaryPart = {
1405
+ itemId: event.item_id,
1406
+ summaryIndex: event.summary_index
1407
+ };
1408
+ seenSummaryDeltas.add(getReasoningSummaryKey(summaryPart));
1409
+ emittedReasoningItems.add(event.item_id);
1410
+ const reasoningStartEvent = getNextReasoningStartEvent();
1411
+ if (reasoningStartEvent) {
1412
+ yield* closeText();
1413
+ yield reasoningStartEvent;
1414
+ }
1415
+ const separatorEvent = getReasoningSummarySeparatorEvent(summaryPart);
1416
+ if (separatorEvent) {
1417
+ yield separatorEvent;
1418
+ }
1419
+ yield {
1420
+ type: "reasoning-delta",
1421
+ text: event.delta
1422
+ };
1423
+ continue;
1424
+ }
1425
+ if (event.type === "response.reasoning_summary_text.done") {
1426
+ const summaryPart = {
1427
+ itemId: event.item_id,
1428
+ summaryIndex: event.summary_index
1429
+ };
1430
+ const key = getReasoningSummaryKey(summaryPart);
1431
+ if (!seenSummaryDeltas.has(key) && event.text.length > 0) {
1432
+ emittedReasoningItems.add(event.item_id);
1433
+ const reasoningStartEvent = getNextReasoningStartEvent();
1434
+ if (reasoningStartEvent) {
1435
+ yield* closeText();
1436
+ yield reasoningStartEvent;
1437
+ }
1438
+ const separatorEvent = getReasoningSummarySeparatorEvent(summaryPart);
1439
+ if (separatorEvent) {
1440
+ yield separatorEvent;
1441
+ }
1442
+ yield {
1443
+ type: "reasoning-delta",
1444
+ text: event.text
1445
+ };
1446
+ }
1447
+ continue;
1448
+ }
1449
+ if (event.type === "response.output_text.delta") {
1450
+ const reasoningEndEvent2 = getNextReasoningEndEvent({ openai: {} });
1451
+ if (reasoningEndEvent2) {
1452
+ yield reasoningEndEvent2;
1453
+ }
1454
+ yield* startText();
1455
+ yield {
1456
+ type: "text-delta",
1457
+ text: event.delta
1458
+ };
1459
+ continue;
1460
+ }
1461
+ if (event.type === "response.output_item.added") {
1462
+ if (!isFunctionToolCall(event.item)) {
1463
+ continue;
1464
+ }
1465
+ yield* closeText();
1466
+ const toolCallId = event.item.call_id;
1467
+ const toolCallName = event.item.name;
1468
+ const toolCallArguments = event.item.arguments;
1469
+ upsertBufferedToolCall(event.output_index, () => ({
1470
+ id: toolCallId,
1471
+ name: toolCallName,
1472
+ arguments: toolCallArguments
1473
+ }));
1474
+ const shouldStartToolCall = !startedToolCalls.has(toolCallId);
1475
+ if (shouldStartToolCall) {
1476
+ startedToolCalls.add(toolCallId);
1477
+ yield {
1478
+ type: "tool-call-start",
1479
+ toolCallId,
1480
+ toolName: toolCallName
1481
+ };
1482
+ }
1483
+ continue;
1484
+ }
1485
+ if (event.type === "response.function_call_arguments.delta") {
1486
+ yield* closeText();
1487
+ const currentToolCall = upsertBufferedToolCall(
1488
+ event.output_index,
1489
+ (bufferedToolCall) => ({
1490
+ id: bufferedToolCall?.id ?? event.item_id,
1491
+ name: bufferedToolCall?.name ?? "",
1492
+ arguments: `${bufferedToolCall?.arguments ?? ""}${event.delta}`
1493
+ })
1494
+ );
1495
+ const shouldStartToolCall = !startedToolCalls.has(
1496
+ currentToolCall.id
1497
+ );
1498
+ if (shouldStartToolCall) {
1499
+ startedToolCalls.add(currentToolCall.id);
1500
+ yield {
1501
+ type: "tool-call-start",
1502
+ toolCallId: currentToolCall.id,
1503
+ toolName: currentToolCall.name
1504
+ };
1505
+ }
1506
+ yield {
1507
+ type: "tool-call-delta",
1508
+ toolCallId: currentToolCall.id,
1509
+ argumentsDelta: event.delta
1510
+ };
1511
+ continue;
1512
+ }
1513
+ if (event.type === "response.output_item.done") {
1514
+ if (isReasoningItem(event.item)) {
1515
+ if (!emittedReasoningItems.has(event.item.id)) {
1516
+ const summaryText = getReasoningSummaryText(
1517
+ event.item.summary
1518
+ );
1519
+ if (summaryText.length > 0) {
1520
+ const reasoningStartEvent = getNextReasoningStartEvent();
1521
+ if (reasoningStartEvent) {
1522
+ yield* closeText();
1523
+ yield reasoningStartEvent;
1524
+ }
1525
+ yield {
1526
+ type: "reasoning-delta",
1527
+ text: summaryText
1528
+ };
1529
+ }
1530
+ }
1531
+ const encryptedContent = typeof event.item.encrypted_content === "string" && event.item.encrypted_content.length > 0 ? event.item.encrypted_content : void 0;
1532
+ if (encryptedContent) {
1533
+ const reasoningStartEvent = getNextReasoningStartEvent();
1534
+ if (reasoningStartEvent) {
1535
+ yield* closeText();
1536
+ yield reasoningStartEvent;
1537
+ }
1538
+ }
1539
+ const reasoningEndEvent2 = getNextReasoningEndEvent({
1540
+ openai: {
1541
+ ...encryptedContent ? { encryptedContent } : {}
1542
+ }
1543
+ });
1544
+ if (reasoningEndEvent2) {
1545
+ yield reasoningEndEvent2;
1546
+ }
1547
+ continue;
1548
+ }
1549
+ if (!isFunctionToolCall(event.item)) {
1550
+ continue;
1551
+ }
1552
+ yield* closeText();
1553
+ const toolCallId = event.item.call_id;
1554
+ const toolCallName = event.item.name;
1555
+ const toolCallArguments = event.item.arguments;
1556
+ const currentToolCall = upsertBufferedToolCall(
1557
+ event.output_index,
1558
+ (bufferedToolCall) => ({
1559
+ id: toolCallId,
1560
+ name: toolCallName,
1561
+ arguments: toolCallArguments || bufferedToolCall?.arguments || ""
1562
+ })
1563
+ );
1564
+ if (!emittedToolCalls.has(currentToolCall.id)) {
1565
+ emittedToolCalls.add(currentToolCall.id);
1566
+ yield {
1567
+ type: "tool-call-end",
1568
+ toolCall: {
1569
+ id: currentToolCall.id,
1570
+ name: currentToolCall.name,
1571
+ arguments: safeParseJsonObject(
1572
+ currentToolCall.arguments
1573
+ )
1574
+ }
1575
+ };
1576
+ }
1577
+ continue;
1578
+ }
1579
+ if (event.type === "response.completed") {
1580
+ latestResponse = event.response;
1581
+ yield* closeText();
1582
+ const reasoningEndEvent2 = getNextReasoningEndEvent({ openai: {} });
1583
+ if (reasoningEndEvent2) {
1584
+ yield reasoningEndEvent2;
1585
+ }
1586
+ for (const bufferedToolCall of bufferedToolCalls.values()) {
1587
+ if (emittedToolCalls.has(bufferedToolCall.id)) {
1588
+ continue;
1589
+ }
1590
+ emittedToolCalls.add(bufferedToolCall.id);
1591
+ yield {
1592
+ type: "tool-call-end",
1593
+ toolCall: {
1594
+ id: bufferedToolCall.id,
1595
+ name: bufferedToolCall.name,
1596
+ arguments: safeParseJsonObject(
1597
+ bufferedToolCall.arguments
1598
+ )
1599
+ }
1600
+ };
1601
+ }
1602
+ const hasToolCalls2 = bufferedToolCalls.size > 0;
1603
+ yield {
1604
+ type: "finish",
1605
+ finishReason: mapFinishReason2(latestResponse, hasToolCalls2),
1606
+ usage: mapUsage(latestResponse.usage)
1607
+ };
1608
+ return;
1609
+ }
1610
+ }
1611
+ const reasoningEndEvent = getNextReasoningEndEvent({
1612
+ openai: {}
1613
+ });
1614
+ yield* closeText();
1615
+ if (reasoningEndEvent) {
1616
+ yield reasoningEndEvent;
1617
+ }
1618
+ const hasToolCalls = bufferedToolCalls.size > 0;
1619
+ const usage = latestResponse ? mapUsage(latestResponse.usage) : mapUsage(void 0);
1620
+ const finishReason = latestResponse ? mapFinishReason2(latestResponse, hasToolCalls) : "unknown";
1621
+ yield {
1622
+ type: "finish",
1623
+ finishReason,
1624
+ usage
1625
+ };
1626
+ }
1627
+ function mapReasoningToRequestFields2(modelId, options) {
1628
+ if (!options.reasoning) {
1629
+ return {};
1630
+ }
1631
+ const capabilities = getOpenAIModelCapabilities(modelId);
1632
+ if (!capabilities.reasoning.supported) {
1633
+ return {};
1634
+ }
1635
+ const effort = toOpenAIReasoningEffort(
1636
+ clampReasoningEffort2(
1637
+ options.reasoning.effort,
1638
+ capabilities.reasoning.supportedEfforts
1639
+ )
1640
+ );
1641
+ return {
1642
+ reasoning: {
1643
+ effort,
1644
+ summary: "auto"
1645
+ }
1646
+ };
1647
+ }
1648
+ function isFunctionToolCall(item) {
1649
+ return item.type === "function_call";
1650
+ }
1651
+ function isOutputMessage(item) {
1652
+ return item.type === "message";
1653
+ }
1654
+ function isReasoningItem(item) {
1655
+ return item.type === "reasoning";
1656
+ }
1657
+
1658
+ // src/chat-model.ts
1659
+ function createOpenAIChatModel(client, modelId, providerId = "openai") {
1660
+ const provider = providerId;
1661
+ async function callOpenAIResponsesApi(request, signal) {
1662
+ try {
1663
+ return await client.responses.create(request, {
1664
+ signal
1665
+ });
1666
+ } catch (error) {
1667
+ throw wrapOpenAIError(error, provider);
1668
+ }
1669
+ }
1670
+ async function generateChat(options) {
1671
+ const request = createGenerateRequest2(modelId, options);
1672
+ const response = await callOpenAIResponsesApi(
1673
+ request,
1674
+ options.signal
1675
+ );
1676
+ return mapGenerateResponse2(response);
1677
+ }
1678
+ async function streamChat(options) {
1679
+ const request = createStreamRequest2(modelId, options);
1680
+ return createChatStream2(
1681
+ async () => transformStream2(
1682
+ await callOpenAIResponsesApi(request, options.signal)
1683
+ ),
1684
+ { signal: options.signal }
1685
+ );
1686
+ }
1687
+ return {
1688
+ provider,
1689
+ modelId,
1690
+ capabilities: getOpenAIModelCapabilities(modelId),
1691
+ generate: generateChat,
1692
+ stream: streamChat,
1693
+ async generateObject(options) {
1694
+ const structuredOptions = createStructuredOutputRequestOptions(options);
1695
+ const result = await generateChat(structuredOptions);
1696
+ const structuredOutputName = getStructuredOutputName(options);
1697
+ const object = extractStructuredObject(
1698
+ result,
1699
+ options.schema,
1700
+ provider,
1701
+ structuredOutputName
1702
+ );
1703
+ return {
1704
+ object,
1705
+ finishReason: result.finishReason,
1706
+ usage: result.usage
1707
+ };
1708
+ },
1709
+ async streamObject(options) {
1710
+ const structuredOptions = createStructuredOutputRequestOptions(options);
1711
+ const stream = await streamChat(structuredOptions);
1712
+ const structuredOutputName = getStructuredOutputName(options);
1713
+ return createObjectStream2(
1714
+ transformStructuredOutputStream(
1715
+ stream,
1716
+ options.schema,
1717
+ provider,
1718
+ structuredOutputName
1719
+ ),
1720
+ {
1721
+ signal: options.signal
1722
+ }
1723
+ );
1724
+ }
1725
+ };
1726
+ }
1727
+
1728
+ // src/embedding-model.ts
1729
+ function createOpenAIEmbeddingModel(client, modelId) {
1730
+ return {
1731
+ provider: "openai",
1732
+ modelId,
1733
+ async embed(options) {
1734
+ try {
1735
+ const openaiOptions = parseOpenAIEmbedProviderOptions(
1736
+ options.providerOptions
1737
+ );
1738
+ const response = await client.embeddings.create({
1739
+ model: modelId,
1740
+ input: options.input,
1741
+ ...options.dimensions !== void 0 ? { dimensions: options.dimensions } : {},
1742
+ ...mapOpenAIEmbedProviderOptionsToRequestFields(
1743
+ openaiOptions
1744
+ )
1745
+ });
1746
+ return {
1747
+ embeddings: response.data.slice().sort((a, b) => a.index - b.index).map((item) => item.embedding),
1748
+ usage: {
1749
+ inputTokens: response.usage.prompt_tokens
1750
+ }
1751
+ };
1752
+ } catch (error) {
1753
+ throw wrapOpenAIError(error);
1754
+ }
1755
+ }
1756
+ };
1757
+ }
1758
+ function mapOpenAIEmbedProviderOptionsToRequestFields(options) {
1759
+ return {
1760
+ ...options?.encodingFormat !== void 0 ? { encoding_format: options.encodingFormat } : {},
1761
+ ...options?.user !== void 0 ? { user: options.user } : {}
1762
+ };
1763
+ }
1764
+
1765
+ // src/image-model.ts
1766
+ function createOpenAIImageModel(client, modelId) {
1767
+ return {
1768
+ provider: "openai",
1769
+ modelId,
1770
+ async generate(options) {
1771
+ try {
1772
+ const openaiOptions = parseOpenAIImageProviderOptions(
1773
+ options.providerOptions
1774
+ );
1775
+ const request = {
1776
+ model: modelId,
1777
+ prompt: options.prompt,
1778
+ ...options.n !== void 0 ? { n: options.n } : {},
1779
+ ...options.size !== void 0 ? { size: options.size } : {},
1780
+ ...mapOpenAIImageProviderOptionsToRequestFields(
1781
+ openaiOptions
1782
+ )
1783
+ };
1784
+ const response = await client.images.generate(
1785
+ request
1786
+ );
1787
+ return {
1788
+ images: (response.data ?? []).map((image) => ({
1789
+ base64: image.b64_json ?? void 0,
1790
+ url: image.url ?? void 0,
1791
+ revisedPrompt: image.revised_prompt ?? void 0
1792
+ }))
1793
+ };
1794
+ } catch (error) {
1795
+ throw wrapOpenAIError(error);
1796
+ }
1797
+ }
1798
+ };
1799
+ }
1800
+ function mapOpenAIImageProviderOptionsToRequestFields(options) {
1801
+ return {
1802
+ ...options?.background !== void 0 ? { background: options.background } : {},
1803
+ ...options?.moderation !== void 0 ? { moderation: options.moderation } : {},
1804
+ ...options?.outputCompression !== void 0 ? { output_compression: options.outputCompression } : {},
1805
+ ...options?.outputFormat !== void 0 ? { output_format: options.outputFormat } : {},
1806
+ ...options?.quality !== void 0 ? { quality: options.quality } : {},
1807
+ ...options?.responseFormat !== void 0 ? { response_format: options.responseFormat } : {},
1808
+ ...options?.style !== void 0 ? { style: options.style } : {},
1809
+ ...options?.user !== void 0 ? { user: options.user } : {}
1810
+ };
1811
+ }
1812
+
1813
+ // src/shared/provider-factory.ts
1814
+ function createOpenAIProvider(options, factoryOptions = {}) {
1815
+ const client = options.client ?? new OpenAI({
1816
+ apiKey: options.apiKey,
1817
+ baseURL: options.baseURL
1818
+ });
1819
+ const providerId = factoryOptions.providerId ?? "openai";
1820
+ const compatibilityEnabled = factoryOptions.compatibility === true || typeof factoryOptions.compatibility === "object";
1821
+ const compatibilityOptions = typeof factoryOptions.compatibility === "object" ? factoryOptions.compatibility : void 0;
1822
+ const createResponsesModel = (modelId) => createOpenAIChatModel(client, modelId, providerId);
1823
+ const createChatCompletionsModel = (modelId) => createOpenAIChatCompletionsModel(client, modelId, {
1824
+ providerId,
1825
+ compatibility: compatibilityEnabled,
1826
+ nonStandardReasoning: compatibilityEnabled && (compatibilityOptions?.reasoning ?? true),
1827
+ structuredOutputMode: compatibilityOptions?.structuredOutputMode,
1828
+ maxTokensParameter: compatibilityOptions?.maxTokensParameter
1829
+ });
1830
+ const chat = {
1831
+ chatModel: createChatCompletionsModel
1832
+ };
1833
+ return {
1834
+ chatModel: factoryOptions.defaultApi === "chat-completions" ? createChatCompletionsModel : createResponsesModel,
1835
+ chat,
1836
+ embeddingModel: (modelId) => createOpenAIEmbeddingModel(client, modelId),
1837
+ imageModel: (modelId) => createOpenAIImageModel(client, modelId)
1838
+ };
1839
+ }
1840
+
1841
+ export {
1842
+ getOpenAIModelCapabilities,
1843
+ openaiResponsesGenerateProviderOptionsSchema,
1844
+ openaiChatGenerateProviderOptionsSchema,
1845
+ openaiEmbedProviderOptionsSchema,
1846
+ openaiImageProviderOptionsSchema,
1847
+ openaiResponsesProviderOptionsSchema,
1848
+ openaiCompatProviderOptionsSchema,
1849
+ openaiCompatGenerateProviderOptionsSchema,
1850
+ createOpenAIChatCompletionsModel,
1851
+ createOpenAIProvider
1852
+ };