@yourgpt/llm-sdk 2.5.1-beta.5 → 2.6.0-beta.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.
- package/dist/adapters/index.d.mts +51 -0
- package/dist/adapters/index.d.ts +51 -0
- package/dist/adapters/index.js +248 -42
- package/dist/adapters/index.mjs +248 -42
- package/dist/index.js +123 -27
- package/dist/index.mjs +123 -27
- package/dist/providers/azure/index.js +8 -3
- package/dist/providers/azure/index.mjs +8 -3
- package/dist/providers/fireworks/index.js +248 -42
- package/dist/providers/fireworks/index.mjs +248 -42
- package/dist/providers/google/index.js +248 -42
- package/dist/providers/google/index.mjs +248 -42
- package/dist/providers/openai/index.js +248 -42
- package/dist/providers/openai/index.mjs +248 -42
- package/dist/providers/openrouter/index.js +248 -42
- package/dist/providers/openrouter/index.mjs +248 -42
- package/dist/providers/togetherai/index.js +248 -42
- package/dist/providers/togetherai/index.mjs +248 -42
- package/dist/providers/xai/index.js +248 -42
- package/dist/providers/xai/index.mjs +248 -42
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -1050,6 +1050,14 @@ var StreamResult = class {
|
|
|
1050
1050
|
}
|
|
1051
1051
|
if (event.requiresAction) {
|
|
1052
1052
|
collected.requiresAction = true;
|
|
1053
|
+
const resolvedToolIds = new Set(
|
|
1054
|
+
(event.messages ?? []).filter((m) => m.role === "tool" && m.tool_call_id).map((m) => m.tool_call_id)
|
|
1055
|
+
);
|
|
1056
|
+
if (resolvedToolIds.size > 0) {
|
|
1057
|
+
collected.toolCalls = collected.toolCalls.filter(
|
|
1058
|
+
(tc) => !resolvedToolIds.has(tc.id)
|
|
1059
|
+
);
|
|
1060
|
+
}
|
|
1053
1061
|
}
|
|
1054
1062
|
if (event.usage) {
|
|
1055
1063
|
this.capturedUsage = event.usage;
|
|
@@ -2201,7 +2209,13 @@ var Runtime = class {
|
|
|
2201
2209
|
// Not used when rawMessages is provided
|
|
2202
2210
|
rawMessages: request.messages,
|
|
2203
2211
|
actions: nativeToolSearch ? void 0 : this.convertToolsToActions(effectiveSelectedTools),
|
|
2204
|
-
|
|
2212
|
+
// Always supplied. `actions` drives the Chat Completions path; adapters
|
|
2213
|
+
// that must use /v1/responses (native tool search, or gpt-5.4+ where
|
|
2214
|
+
// Chat Completions rejects function tools alongside reasoning) need the
|
|
2215
|
+
// richer ToolDefinition shape. Gating this on nativeToolSearch meant the
|
|
2216
|
+
// reasoning-model route could never see any tools, so it silently never
|
|
2217
|
+
// engaged and those requests still 400'd on Chat Completions.
|
|
2218
|
+
toolDefinitions: effectiveSelectedTools,
|
|
2205
2219
|
systemPrompt,
|
|
2206
2220
|
config: request.config,
|
|
2207
2221
|
signal,
|
|
@@ -2351,6 +2365,49 @@ var Runtime = class {
|
|
|
2351
2365
|
const clientToolCalls = toolCalls.filter(
|
|
2352
2366
|
(tc) => !serverToolIds.has(tc.id)
|
|
2353
2367
|
);
|
|
2368
|
+
const serverToolResultMessages = serverToolResults.map((tr) => {
|
|
2369
|
+
const aiContent = buildToolResultForAI(tr.tool, tr.result, tr.args);
|
|
2370
|
+
const content = typeof aiContent === "string" ? aiContent : JSON.stringify(serializeToolResultContent(aiContent));
|
|
2371
|
+
return {
|
|
2372
|
+
role: "tool",
|
|
2373
|
+
content,
|
|
2374
|
+
tool_call_id: tr.id
|
|
2375
|
+
};
|
|
2376
|
+
});
|
|
2377
|
+
if (serverToolResults.length > 0 && clientToolCalls.length > 0) {
|
|
2378
|
+
if (debug) {
|
|
2379
|
+
console.log(
|
|
2380
|
+
`[Copilot SDK] Mixed turn: ${serverToolResults.length} server + ${clientToolCalls.length} client tool(s). Suspending for client tools.`
|
|
2381
|
+
);
|
|
2382
|
+
}
|
|
2383
|
+
const assistantMessage = {
|
|
2384
|
+
role: "assistant",
|
|
2385
|
+
content: accumulatedText || null,
|
|
2386
|
+
tool_calls: toolCalls.map((tc) => ({
|
|
2387
|
+
id: tc.id,
|
|
2388
|
+
type: "function",
|
|
2389
|
+
function: {
|
|
2390
|
+
name: tc.name,
|
|
2391
|
+
arguments: JSON.stringify(tc.args)
|
|
2392
|
+
},
|
|
2393
|
+
...tc.extra_content ? { extra_content: tc.extra_content } : {}
|
|
2394
|
+
}))
|
|
2395
|
+
};
|
|
2396
|
+
newMessages.push(assistantMessage);
|
|
2397
|
+
newMessages.push(...serverToolResultMessages);
|
|
2398
|
+
yield {
|
|
2399
|
+
type: "tool_calls",
|
|
2400
|
+
toolCalls: clientToolCalls,
|
|
2401
|
+
assistantMessage
|
|
2402
|
+
};
|
|
2403
|
+
yield {
|
|
2404
|
+
type: "done",
|
|
2405
|
+
requiresAction: true,
|
|
2406
|
+
messages: newMessages,
|
|
2407
|
+
usage: adapterUsage
|
|
2408
|
+
};
|
|
2409
|
+
return;
|
|
2410
|
+
}
|
|
2354
2411
|
if (serverToolResults.length > 0) {
|
|
2355
2412
|
if (debug) {
|
|
2356
2413
|
console.log(
|
|
@@ -2373,17 +2430,7 @@ var Runtime = class {
|
|
|
2373
2430
|
};
|
|
2374
2431
|
})
|
|
2375
2432
|
};
|
|
2376
|
-
const toolResultMessages =
|
|
2377
|
-
(tr) => {
|
|
2378
|
-
const aiContent = buildToolResultForAI(tr.tool, tr.result, tr.args);
|
|
2379
|
-
const content = typeof aiContent === "string" ? aiContent : JSON.stringify(serializeToolResultContent(aiContent));
|
|
2380
|
-
return {
|
|
2381
|
-
role: "tool",
|
|
2382
|
-
content,
|
|
2383
|
-
tool_call_id: tr.id
|
|
2384
|
-
};
|
|
2385
|
-
}
|
|
2386
|
-
);
|
|
2433
|
+
const toolResultMessages = serverToolResultMessages;
|
|
2387
2434
|
newMessages.push(assistantWithToolCalls);
|
|
2388
2435
|
newMessages.push(...toolResultMessages);
|
|
2389
2436
|
const messagesWithResults = [
|
|
@@ -2530,7 +2577,13 @@ var Runtime = class {
|
|
|
2530
2577
|
messages: [],
|
|
2531
2578
|
rawMessages: conversationMessages,
|
|
2532
2579
|
actions: nativeToolSearch ? void 0 : this.convertToolsToActions(effectiveSelectedTools),
|
|
2533
|
-
|
|
2580
|
+
// Always supplied. `actions` drives the Chat Completions path; adapters
|
|
2581
|
+
// that must use /v1/responses (native tool search, or gpt-5.4+ where
|
|
2582
|
+
// Chat Completions rejects function tools alongside reasoning) need the
|
|
2583
|
+
// richer ToolDefinition shape. Gating this on nativeToolSearch meant the
|
|
2584
|
+
// reasoning-model route could never see any tools, so it silently never
|
|
2585
|
+
// engaged and those requests still 400'd on Chat Completions.
|
|
2586
|
+
toolDefinitions: effectiveSelectedTools,
|
|
2534
2587
|
systemPrompt,
|
|
2535
2588
|
config: request.config,
|
|
2536
2589
|
signal,
|
|
@@ -2638,6 +2691,48 @@ var Runtime = class {
|
|
|
2638
2691
|
}
|
|
2639
2692
|
}
|
|
2640
2693
|
}
|
|
2694
|
+
const serverToolResultMessages = serverToolResults.map((tr) => {
|
|
2695
|
+
const aiContent = buildToolResultForAI(
|
|
2696
|
+
tr.tool,
|
|
2697
|
+
tr.result,
|
|
2698
|
+
tr.args
|
|
2699
|
+
);
|
|
2700
|
+
const content = typeof aiContent === "string" ? aiContent : JSON.stringify(serializeToolResultContent(aiContent));
|
|
2701
|
+
return {
|
|
2702
|
+
role: "tool",
|
|
2703
|
+
content,
|
|
2704
|
+
tool_call_id: tr.id
|
|
2705
|
+
};
|
|
2706
|
+
});
|
|
2707
|
+
if (serverToolResults.length > 0 && clientToolCalls.length > 0) {
|
|
2708
|
+
const assistantMessage = {
|
|
2709
|
+
role: "assistant",
|
|
2710
|
+
content: result.content || null,
|
|
2711
|
+
tool_calls: result.toolCalls.map((tc) => ({
|
|
2712
|
+
id: tc.id,
|
|
2713
|
+
type: "function",
|
|
2714
|
+
function: {
|
|
2715
|
+
name: tc.name,
|
|
2716
|
+
arguments: JSON.stringify(tc.args)
|
|
2717
|
+
},
|
|
2718
|
+
...tc.extra_content ? { extra_content: tc.extra_content } : {}
|
|
2719
|
+
}))
|
|
2720
|
+
};
|
|
2721
|
+
newMessages.push(assistantMessage);
|
|
2722
|
+
newMessages.push(...serverToolResultMessages);
|
|
2723
|
+
yield {
|
|
2724
|
+
type: "tool_calls",
|
|
2725
|
+
toolCalls: clientToolCalls,
|
|
2726
|
+
assistantMessage
|
|
2727
|
+
};
|
|
2728
|
+
yield {
|
|
2729
|
+
type: "done",
|
|
2730
|
+
requiresAction: true,
|
|
2731
|
+
messages: newMessages,
|
|
2732
|
+
usage: accumulatedUsage.total_tokens > 0 ? accumulatedUsage : void 0
|
|
2733
|
+
};
|
|
2734
|
+
return;
|
|
2735
|
+
}
|
|
2641
2736
|
if (serverToolResults.length > 0) {
|
|
2642
2737
|
const assistantWithToolCalls = {
|
|
2643
2738
|
role: "assistant",
|
|
@@ -2652,19 +2747,7 @@ var Runtime = class {
|
|
|
2652
2747
|
...tc.extra_content ? { extra_content: tc.extra_content } : {}
|
|
2653
2748
|
}))
|
|
2654
2749
|
};
|
|
2655
|
-
const toolResultMessages =
|
|
2656
|
-
const aiContent = buildToolResultForAI(
|
|
2657
|
-
tr.tool,
|
|
2658
|
-
tr.result,
|
|
2659
|
-
tr.args
|
|
2660
|
-
);
|
|
2661
|
-
const content = typeof aiContent === "string" ? aiContent : JSON.stringify(serializeToolResultContent(aiContent));
|
|
2662
|
-
return {
|
|
2663
|
-
role: "tool",
|
|
2664
|
-
content,
|
|
2665
|
-
tool_call_id: tr.id
|
|
2666
|
-
};
|
|
2667
|
-
});
|
|
2750
|
+
const toolResultMessages = serverToolResultMessages;
|
|
2668
2751
|
newMessages.push(assistantWithToolCalls);
|
|
2669
2752
|
newMessages.push(...toolResultMessages);
|
|
2670
2753
|
conversationMessages = [
|
|
@@ -3003,10 +3086,23 @@ var Runtime = class {
|
|
|
3003
3086
|
result: event.result || event.error
|
|
3004
3087
|
});
|
|
3005
3088
|
break;
|
|
3006
|
-
case "done":
|
|
3089
|
+
case "done": {
|
|
3007
3090
|
messages = event.messages || [];
|
|
3008
3091
|
requiresAction = event.requiresAction || false;
|
|
3092
|
+
if (requiresAction) {
|
|
3093
|
+
const resolvedToolIds = new Set(
|
|
3094
|
+
messages.filter((m) => m.role === "tool" && m.tool_call_id).map((m) => m.tool_call_id)
|
|
3095
|
+
);
|
|
3096
|
+
if (resolvedToolIds.size > 0) {
|
|
3097
|
+
for (let i = toolCalls.length - 1; i >= 0; i--) {
|
|
3098
|
+
if (resolvedToolIds.has(toolCalls[i].id)) {
|
|
3099
|
+
toolCalls.splice(i, 1);
|
|
3100
|
+
}
|
|
3101
|
+
}
|
|
3102
|
+
}
|
|
3103
|
+
}
|
|
3009
3104
|
break;
|
|
3105
|
+
}
|
|
3010
3106
|
case "error":
|
|
3011
3107
|
error = { message: event.message, code: event.code };
|
|
3012
3108
|
break;
|
|
@@ -99,14 +99,19 @@ function normalizeObjectJsonSchema(schema) {
|
|
|
99
99
|
(value) => typeof value === "string"
|
|
100
100
|
) : [];
|
|
101
101
|
normalized.required = Array.from(/* @__PURE__ */ new Set([...required, ...propertyKeys]));
|
|
102
|
-
|
|
103
|
-
normalized.additionalProperties = false;
|
|
104
|
-
}
|
|
102
|
+
normalized.additionalProperties = false;
|
|
105
103
|
} else if (type === "array" && normalized.items && typeof normalized.items === "object") {
|
|
106
104
|
normalized.items = normalizeObjectJsonSchema(
|
|
107
105
|
normalized.items
|
|
108
106
|
);
|
|
109
107
|
}
|
|
108
|
+
for (const keyword of ["anyOf", "oneOf", "allOf"]) {
|
|
109
|
+
const branches = normalized[keyword];
|
|
110
|
+
if (!Array.isArray(branches)) continue;
|
|
111
|
+
normalized[keyword] = branches.map(
|
|
112
|
+
(branch) => branch && typeof branch === "object" ? normalizeObjectJsonSchema(branch) : branch
|
|
113
|
+
);
|
|
114
|
+
}
|
|
110
115
|
return normalized;
|
|
111
116
|
}
|
|
112
117
|
function toOpenAIResponseFormat(rf) {
|
|
@@ -97,14 +97,19 @@ function normalizeObjectJsonSchema(schema) {
|
|
|
97
97
|
(value) => typeof value === "string"
|
|
98
98
|
) : [];
|
|
99
99
|
normalized.required = Array.from(/* @__PURE__ */ new Set([...required, ...propertyKeys]));
|
|
100
|
-
|
|
101
|
-
normalized.additionalProperties = false;
|
|
102
|
-
}
|
|
100
|
+
normalized.additionalProperties = false;
|
|
103
101
|
} else if (type === "array" && normalized.items && typeof normalized.items === "object") {
|
|
104
102
|
normalized.items = normalizeObjectJsonSchema(
|
|
105
103
|
normalized.items
|
|
106
104
|
);
|
|
107
105
|
}
|
|
106
|
+
for (const keyword of ["anyOf", "oneOf", "allOf"]) {
|
|
107
|
+
const branches = normalized[keyword];
|
|
108
|
+
if (!Array.isArray(branches)) continue;
|
|
109
|
+
normalized[keyword] = branches.map(
|
|
110
|
+
(branch) => branch && typeof branch === "object" ? normalizeObjectJsonSchema(branch) : branch
|
|
111
|
+
);
|
|
112
|
+
}
|
|
108
113
|
return normalized;
|
|
109
114
|
}
|
|
110
115
|
function toOpenAIResponseFormat(rf) {
|
|
@@ -91,14 +91,19 @@ function normalizeObjectJsonSchema(schema) {
|
|
|
91
91
|
(value) => typeof value === "string"
|
|
92
92
|
) : [];
|
|
93
93
|
normalized.required = Array.from(/* @__PURE__ */ new Set([...required, ...propertyKeys]));
|
|
94
|
-
|
|
95
|
-
normalized.additionalProperties = false;
|
|
96
|
-
}
|
|
94
|
+
normalized.additionalProperties = false;
|
|
97
95
|
} else if (type === "array" && normalized.items && typeof normalized.items === "object") {
|
|
98
96
|
normalized.items = normalizeObjectJsonSchema(
|
|
99
97
|
normalized.items
|
|
100
98
|
);
|
|
101
99
|
}
|
|
100
|
+
for (const keyword of ["anyOf", "oneOf", "allOf"]) {
|
|
101
|
+
const branches = normalized[keyword];
|
|
102
|
+
if (!Array.isArray(branches)) continue;
|
|
103
|
+
normalized[keyword] = branches.map(
|
|
104
|
+
(branch) => branch && typeof branch === "object" ? normalizeObjectJsonSchema(branch) : branch
|
|
105
|
+
);
|
|
106
|
+
}
|
|
102
107
|
return normalized;
|
|
103
108
|
}
|
|
104
109
|
function isOpenAIReasoningModel(modelId) {
|
|
@@ -474,18 +479,79 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
474
479
|
return this.client;
|
|
475
480
|
}
|
|
476
481
|
shouldUseResponsesApi(request) {
|
|
477
|
-
|
|
482
|
+
const hasTools = Array.isArray(request.toolDefinitions) && request.toolDefinitions.length > 0;
|
|
483
|
+
if (!hasTools) return false;
|
|
484
|
+
if (request.providerToolOptions?.openai?.nativeToolSearch?.enabled === true && request.providerToolOptions.openai.nativeToolSearch.useResponsesApi !== false) {
|
|
485
|
+
return true;
|
|
486
|
+
}
|
|
487
|
+
return this.requiresResponsesApiForTools(
|
|
488
|
+
request.config?.model || this.model
|
|
489
|
+
);
|
|
490
|
+
}
|
|
491
|
+
/**
|
|
492
|
+
* Whether this model rejects function tools on /v1/chat/completions.
|
|
493
|
+
*
|
|
494
|
+
* Applies to OpenAI proper only — the adapter is reused for OpenAI-compatible
|
|
495
|
+
* providers (Azure, xAI, Google, Fireworks) whose endpoints have no such
|
|
496
|
+
* restriction and may not implement /v1/responses at all.
|
|
497
|
+
*/
|
|
498
|
+
requiresResponsesApiForTools(modelId) {
|
|
499
|
+
if (!modelId) return false;
|
|
500
|
+
if (!/^gpt-5\.(?:[4-9]|\d{2,})/i.test(modelId)) return false;
|
|
501
|
+
const baseUrl = this.config.baseUrl;
|
|
502
|
+
if (!baseUrl) return true;
|
|
503
|
+
try {
|
|
504
|
+
return new URL(baseUrl).hostname === "api.openai.com";
|
|
505
|
+
} catch {
|
|
506
|
+
return false;
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
/**
|
|
510
|
+
* Convert one message's content to Responses content parts.
|
|
511
|
+
*
|
|
512
|
+
* The Responses API does not accept Chat Completions content blocks: input
|
|
513
|
+
* parts are `input_text` / `input_image` (with a bare `image_url` string)
|
|
514
|
+
* rather than `text` / `image_url: { url }`. Passing the Chat Completions
|
|
515
|
+
* shape through unchanged makes the API reject the request, so anything
|
|
516
|
+
* array-shaped has to be translated rather than forwarded.
|
|
517
|
+
*/
|
|
518
|
+
toResponsesContent(content, role) {
|
|
519
|
+
const textType = role === "assistant" ? "output_text" : "input_text";
|
|
520
|
+
if (typeof content === "string") return content;
|
|
521
|
+
if (!Array.isArray(content)) {
|
|
522
|
+
return content == null ? "" : JSON.stringify(content);
|
|
523
|
+
}
|
|
524
|
+
return content.map((part) => {
|
|
525
|
+
const p = part;
|
|
526
|
+
if (p?.type === "text") return { type: textType, text: p.text ?? "" };
|
|
527
|
+
if (p?.type === "image_url") {
|
|
528
|
+
const url = typeof p.image_url === "string" ? p.image_url : p.image_url?.url;
|
|
529
|
+
return {
|
|
530
|
+
type: "input_image",
|
|
531
|
+
image_url: url,
|
|
532
|
+
...p.image_url?.detail ? { detail: p.image_url.detail } : {}
|
|
533
|
+
};
|
|
534
|
+
}
|
|
535
|
+
return p;
|
|
536
|
+
});
|
|
478
537
|
}
|
|
479
538
|
buildResponsesInput(request) {
|
|
480
539
|
const sourceMessages = request.rawMessages && request.rawMessages.length > 0 ? request.rawMessages : formatMessagesForOpenAI(request.messages, void 0);
|
|
481
540
|
const input = [];
|
|
482
541
|
for (const message of sourceMessages) {
|
|
483
542
|
if (message.role === "system") {
|
|
543
|
+
if (!request.systemPrompt && message.content) {
|
|
544
|
+
input.push({
|
|
545
|
+
type: "message",
|
|
546
|
+
role: "developer",
|
|
547
|
+
content: this.toResponsesContent(message.content, "developer")
|
|
548
|
+
});
|
|
549
|
+
}
|
|
484
550
|
continue;
|
|
485
551
|
}
|
|
486
552
|
if (message.role === "assistant") {
|
|
487
|
-
const content =
|
|
488
|
-
if (content) {
|
|
553
|
+
const content = this.toResponsesContent(message.content, "assistant");
|
|
554
|
+
if (typeof content === "string" ? content : Array.isArray(content) && content.length > 0) {
|
|
489
555
|
input.push({
|
|
490
556
|
type: "message",
|
|
491
557
|
role: "assistant",
|
|
@@ -511,15 +577,67 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
511
577
|
});
|
|
512
578
|
continue;
|
|
513
579
|
}
|
|
580
|
+
const role = message.role === "developer" ? "developer" : "user";
|
|
514
581
|
input.push({
|
|
515
582
|
type: "message",
|
|
516
|
-
role
|
|
517
|
-
content:
|
|
583
|
+
role,
|
|
584
|
+
content: this.toResponsesContent(message.content, role)
|
|
518
585
|
});
|
|
519
586
|
}
|
|
520
587
|
return input;
|
|
521
588
|
}
|
|
522
|
-
|
|
589
|
+
/**
|
|
590
|
+
* Build the web_search tool for the Responses API, or undefined when web
|
|
591
|
+
* search is off.
|
|
592
|
+
*
|
|
593
|
+
* The Chat Completions path uses `web_search_preview`; the Responses API
|
|
594
|
+
* names the same built-in `web_search`. Without this, enabling web search
|
|
595
|
+
* alongside a gpt-5.4+ function tool silently did nothing, because routing
|
|
596
|
+
* to /v1/responses skipped the Chat Completions tool assembly entirely.
|
|
597
|
+
*/
|
|
598
|
+
buildResponsesWebSearchTool(request) {
|
|
599
|
+
const webSearchConfig = request.webSearch ?? this.config.webSearch;
|
|
600
|
+
if (!webSearchConfig) return void 0;
|
|
601
|
+
const tool = { type: "web_search" };
|
|
602
|
+
const wsConfig = typeof webSearchConfig === "object" ? webSearchConfig : {};
|
|
603
|
+
if (wsConfig.userLocation) {
|
|
604
|
+
tool.search_context_size = "medium";
|
|
605
|
+
}
|
|
606
|
+
return tool;
|
|
607
|
+
}
|
|
608
|
+
/**
|
|
609
|
+
* Extract url_citation annotations from Responses output items.
|
|
610
|
+
*
|
|
611
|
+
* Annotations hang off the output_text content parts rather than arriving as
|
|
612
|
+
* their own stream events, so both the streaming and non-streaming paths read
|
|
613
|
+
* them from the same place.
|
|
614
|
+
*/
|
|
615
|
+
collectResponsesCitations(output, startIndex = 0) {
|
|
616
|
+
if (!Array.isArray(output)) return [];
|
|
617
|
+
const citations = [];
|
|
618
|
+
let index = startIndex;
|
|
619
|
+
for (const item of output) {
|
|
620
|
+
if (item?.type !== "message" || !Array.isArray(item.content)) continue;
|
|
621
|
+
for (const part of item.content) {
|
|
622
|
+
if (!Array.isArray(part?.annotations)) continue;
|
|
623
|
+
for (const annotation of part.annotations) {
|
|
624
|
+
const url = annotation?.url;
|
|
625
|
+
if (annotation?.type !== "url_citation" || !url) continue;
|
|
626
|
+
index++;
|
|
627
|
+
const domain = extractDomain(url);
|
|
628
|
+
citations.push({
|
|
629
|
+
index,
|
|
630
|
+
url,
|
|
631
|
+
title: annotation.title || domain,
|
|
632
|
+
domain,
|
|
633
|
+
favicon: domain ? `https://www.google.com/s2/favicons?domain=${domain}&sz=32` : void 0
|
|
634
|
+
});
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
return citations;
|
|
639
|
+
}
|
|
640
|
+
buildResponsesTools(tools, includeToolSearch, webSearchTool) {
|
|
523
641
|
const nativeTools = tools.filter((tool) => tool.available !== false).map((tool) => ({
|
|
524
642
|
type: "function",
|
|
525
643
|
name: tool.name,
|
|
@@ -534,10 +652,18 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
534
652
|
strict: true,
|
|
535
653
|
defer_loading: tool.deferLoading === true
|
|
536
654
|
}));
|
|
537
|
-
|
|
655
|
+
const builtins = [];
|
|
656
|
+
if (includeToolSearch) builtins.push({ type: "tool_search" });
|
|
657
|
+
if (webSearchTool) builtins.push(webSearchTool);
|
|
658
|
+
return [...builtins, ...nativeTools];
|
|
538
659
|
}
|
|
539
660
|
parseResponsesResult(response) {
|
|
540
|
-
const
|
|
661
|
+
const textFromOutput = Array.isArray(response?.output) ? response.output.filter((item) => item?.type === "message").flatMap(
|
|
662
|
+
(item) => Array.isArray(item.content) ? item.content.filter(
|
|
663
|
+
(part) => part?.type === "output_text" && typeof part.text === "string"
|
|
664
|
+
).map((part) => part.text) : []
|
|
665
|
+
).join("") : "";
|
|
666
|
+
const content = textFromOutput || (typeof response?.output_text === "string" ? response.output_text : "");
|
|
541
667
|
const toolCalls = Array.isArray(response?.output) ? response.output.filter((item) => item?.type === "function_call").map((item) => ({
|
|
542
668
|
id: item.call_id ?? item.id ?? generateToolCallId(),
|
|
543
669
|
name: item.name,
|
|
@@ -560,59 +686,139 @@ var OpenAIAdapter = class _OpenAIAdapter {
|
|
|
560
686
|
rawResponse: response
|
|
561
687
|
};
|
|
562
688
|
}
|
|
563
|
-
|
|
564
|
-
const client = await this.getClient();
|
|
689
|
+
buildResponsesPayload(request, stream) {
|
|
565
690
|
const openaiToolOptions = request.providerToolOptions?.openai;
|
|
566
691
|
const responsesTextFormat = toOpenAIResponsesTextFormat(
|
|
567
692
|
request.config?.responseFormat
|
|
568
693
|
);
|
|
694
|
+
const modelIdForPayload = request.config?.model || this.model;
|
|
569
695
|
const payload = {
|
|
570
|
-
model:
|
|
696
|
+
model: modelIdForPayload,
|
|
571
697
|
instructions: request.systemPrompt,
|
|
572
698
|
input: this.buildResponsesInput(request),
|
|
573
|
-
tools: this.buildResponsesTools(
|
|
699
|
+
tools: this.buildResponsesTools(
|
|
700
|
+
request.toolDefinitions ?? [],
|
|
701
|
+
request.providerToolOptions?.openai?.nativeToolSearch?.enabled === true,
|
|
702
|
+
this.buildResponsesWebSearchTool(request)
|
|
703
|
+
),
|
|
574
704
|
tool_choice: openaiToolOptions?.toolChoice === "required" ? "required" : openaiToolOptions?.toolChoice === "auto" ? "auto" : void 0,
|
|
575
705
|
parallel_tool_calls: openaiToolOptions?.parallelToolCalls,
|
|
576
|
-
|
|
706
|
+
// Reasoning models reject `temperature` outright — sending it would swap one
|
|
707
|
+
// 400 for another. isOpenAIReasoningModel already encodes that family test.
|
|
708
|
+
...isOpenAIReasoningModel(modelIdForPayload) ? {} : {
|
|
709
|
+
temperature: request.config?.temperature ?? this.config.temperature
|
|
710
|
+
},
|
|
577
711
|
max_output_tokens: request.config?.maxTokens ?? this.config.maxTokens,
|
|
578
712
|
...responsesTextFormat ? { text: { format: responsesTextFormat } } : {},
|
|
579
|
-
stream
|
|
713
|
+
stream
|
|
580
714
|
};
|
|
715
|
+
return payload;
|
|
716
|
+
}
|
|
717
|
+
async completeWithResponses(request) {
|
|
718
|
+
const client = await this.getClient();
|
|
719
|
+
const payload = this.buildResponsesPayload(request, false);
|
|
581
720
|
logProviderPayload("openai", "request payload", payload, request.debug);
|
|
582
721
|
const response = await client.responses.create(payload);
|
|
583
722
|
logProviderPayload("openai", "response payload", response, request.debug);
|
|
584
723
|
return this.parseResponsesResult(response);
|
|
585
724
|
}
|
|
725
|
+
/**
|
|
726
|
+
* Stream from /v1/responses as real SSE.
|
|
727
|
+
*
|
|
728
|
+
* The Responses API emits semantic events rather than Chat Completions
|
|
729
|
+
* deltas, so the interesting ones are:
|
|
730
|
+
* response.output_text.delta — incremental assistant text
|
|
731
|
+
* response.output_item.added — a function_call item begins (name/id)
|
|
732
|
+
* response.function_call_arguments.delta / .done — tool arguments
|
|
733
|
+
* response.completed — terminal, carries usage
|
|
734
|
+
*
|
|
735
|
+
* Tool calls are buffered and flushed as contiguous start→args→end triples
|
|
736
|
+
* because the runtime pairs action:args with the action:end that follows it.
|
|
737
|
+
*/
|
|
738
|
+
async *streamWithResponses(request) {
|
|
739
|
+
const client = await this.getClient();
|
|
740
|
+
const payload = this.buildResponsesPayload(request, true);
|
|
741
|
+
logProviderPayload("openai", "request payload", payload, request.debug);
|
|
742
|
+
const stream = await client.responses.create(payload);
|
|
743
|
+
const toolCalls = /* @__PURE__ */ new Map();
|
|
744
|
+
let usage;
|
|
745
|
+
const citations = [];
|
|
746
|
+
function* flushToolCalls() {
|
|
747
|
+
for (const call of toolCalls.values()) {
|
|
748
|
+
yield { type: "action:start", id: call.id, name: call.name };
|
|
749
|
+
yield { type: "action:args", id: call.id, args: call.arguments };
|
|
750
|
+
yield { type: "action:end", id: call.id, name: call.name };
|
|
751
|
+
}
|
|
752
|
+
toolCalls.clear();
|
|
753
|
+
}
|
|
754
|
+
for await (const event of stream) {
|
|
755
|
+
if (request.signal?.aborted) break;
|
|
756
|
+
logProviderPayload("openai", "stream chunk", event, request.debug);
|
|
757
|
+
const index = event?.output_index ?? 0;
|
|
758
|
+
switch (event?.type) {
|
|
759
|
+
case "response.output_text.delta":
|
|
760
|
+
if (event.delta) {
|
|
761
|
+
yield { type: "message:delta", content: event.delta };
|
|
762
|
+
}
|
|
763
|
+
break;
|
|
764
|
+
case "response.output_item.added":
|
|
765
|
+
if (event.item?.type === "function_call") {
|
|
766
|
+
toolCalls.set(index, {
|
|
767
|
+
id: event.item.call_id ?? event.item.id ?? generateToolCallId(),
|
|
768
|
+
name: event.item.name ?? "",
|
|
769
|
+
arguments: event.item.arguments ?? ""
|
|
770
|
+
});
|
|
771
|
+
}
|
|
772
|
+
break;
|
|
773
|
+
case "response.function_call_arguments.delta": {
|
|
774
|
+
const existing = toolCalls.get(index);
|
|
775
|
+
if (existing && event.delta) existing.arguments += event.delta;
|
|
776
|
+
break;
|
|
777
|
+
}
|
|
778
|
+
case "response.function_call_arguments.done": {
|
|
779
|
+
const existing = toolCalls.get(index);
|
|
780
|
+
if (existing && typeof event.arguments === "string") {
|
|
781
|
+
existing.arguments = event.arguments;
|
|
782
|
+
}
|
|
783
|
+
break;
|
|
784
|
+
}
|
|
785
|
+
case "response.completed":
|
|
786
|
+
case "response.incomplete": {
|
|
787
|
+
const found = this.collectResponsesCitations(
|
|
788
|
+
event.response?.output,
|
|
789
|
+
citations.length
|
|
790
|
+
);
|
|
791
|
+
if (found.length > 0) citations.push(...found);
|
|
792
|
+
const u = event.response?.usage;
|
|
793
|
+
if (u) {
|
|
794
|
+
usage = {
|
|
795
|
+
prompt_tokens: u.input_tokens ?? 0,
|
|
796
|
+
completion_tokens: u.output_tokens ?? 0,
|
|
797
|
+
total_tokens: u.total_tokens ?? (u.input_tokens ?? 0) + (u.output_tokens ?? 0)
|
|
798
|
+
};
|
|
799
|
+
}
|
|
800
|
+
break;
|
|
801
|
+
}
|
|
802
|
+
case "error":
|
|
803
|
+
case "response.failed":
|
|
804
|
+
throw new Error(
|
|
805
|
+
event.message ?? event.response?.error?.message ?? "Responses stream failed"
|
|
806
|
+
);
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
yield* flushToolCalls();
|
|
810
|
+
if (citations.length > 0) {
|
|
811
|
+
yield { type: "citation", citations: deduplicateCitations(citations) };
|
|
812
|
+
}
|
|
813
|
+
yield { type: "message:end" };
|
|
814
|
+
yield { type: "done", usage };
|
|
815
|
+
}
|
|
586
816
|
async *stream(request) {
|
|
587
817
|
if (this.shouldUseResponsesApi(request)) {
|
|
588
818
|
const messageId2 = generateMessageId();
|
|
589
819
|
yield { type: "message:start", id: messageId2 };
|
|
590
820
|
try {
|
|
591
|
-
|
|
592
|
-
if (result.content) {
|
|
593
|
-
yield { type: "message:delta", content: result.content };
|
|
594
|
-
}
|
|
595
|
-
for (const toolCall of result.toolCalls) {
|
|
596
|
-
yield {
|
|
597
|
-
type: "action:start",
|
|
598
|
-
id: toolCall.id,
|
|
599
|
-
name: toolCall.name
|
|
600
|
-
};
|
|
601
|
-
yield {
|
|
602
|
-
type: "action:args",
|
|
603
|
-
id: toolCall.id,
|
|
604
|
-
args: JSON.stringify(toolCall.args)
|
|
605
|
-
};
|
|
606
|
-
}
|
|
607
|
-
yield { type: "message:end" };
|
|
608
|
-
yield {
|
|
609
|
-
type: "done",
|
|
610
|
-
usage: result.usage ? {
|
|
611
|
-
prompt_tokens: result.usage.promptTokens,
|
|
612
|
-
completion_tokens: result.usage.completionTokens,
|
|
613
|
-
total_tokens: result.usage.totalTokens
|
|
614
|
-
} : void 0
|
|
615
|
-
};
|
|
821
|
+
yield* this.streamWithResponses(request);
|
|
616
822
|
return;
|
|
617
823
|
} catch (error) {
|
|
618
824
|
yield {
|