@openai/agents-extensions 0.3.6 → 0.3.8
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/aiSdk.d.ts +17 -16
- package/dist/aiSdk.js +273 -74
- package/dist/aiSdk.js.map +1 -1
- package/dist/aiSdk.mjs +273 -74
- package/dist/aiSdk.mjs.map +1 -1
- package/dist/metadata.js +2 -2
- package/dist/metadata.mjs +2 -2
- package/package.json +4 -3
package/dist/aiSdk.mjs
CHANGED
|
@@ -1,6 +1,26 @@
|
|
|
1
1
|
import { createGenerationSpan, resetCurrentSpan, setCurrentSpan, Usage, UserError, withGenerationSpan, getLogger, } from '@openai/agents';
|
|
2
2
|
import { isZodObject } from '@openai/agents/utils';
|
|
3
3
|
import { encodeUint8ArrayToBase64 } from '@openai/agents/utils';
|
|
4
|
+
function getSpecVersion(model) {
|
|
5
|
+
const spec = model?.specificationVersion;
|
|
6
|
+
if (!spec) {
|
|
7
|
+
// Default to v2 for backward compatibility with older AI SDK model wrappers.
|
|
8
|
+
return 'v2';
|
|
9
|
+
}
|
|
10
|
+
if (spec === 'v2') {
|
|
11
|
+
return 'v2';
|
|
12
|
+
}
|
|
13
|
+
if (typeof spec === 'string' && spec.toLowerCase().startsWith('v3')) {
|
|
14
|
+
return 'v3';
|
|
15
|
+
}
|
|
16
|
+
return 'unknown';
|
|
17
|
+
}
|
|
18
|
+
function ensureSupportedModel(model) {
|
|
19
|
+
const spec = getSpecVersion(model);
|
|
20
|
+
if (spec === 'unknown') {
|
|
21
|
+
throw new UserError(`Unsupported AI SDK specificationVersion: ${String(model?.specificationVersion)}. Only v2 and v3 are supported.`);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
4
24
|
/**
|
|
5
25
|
* @internal
|
|
6
26
|
* Converts a list of model items to a list of language model V2 messages.
|
|
@@ -9,23 +29,52 @@ import { encodeUint8ArrayToBase64 } from '@openai/agents/utils';
|
|
|
9
29
|
* @param items - The items to convert.
|
|
10
30
|
* @returns The list of language model V2 messages.
|
|
11
31
|
*/
|
|
12
|
-
export function itemsToLanguageV2Messages(model, items) {
|
|
32
|
+
export function itemsToLanguageV2Messages(model, items, modelSettings) {
|
|
13
33
|
const messages = [];
|
|
14
34
|
let currentAssistantMessage;
|
|
35
|
+
let pendingReasonerReasoning;
|
|
36
|
+
const flushPendingReasonerReasoningToMessages = () => {
|
|
37
|
+
if (!(shouldIncludeReasoningContent(model, modelSettings) &&
|
|
38
|
+
pendingReasonerReasoning)) {
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
const reasoningPart = {
|
|
42
|
+
type: 'reasoning',
|
|
43
|
+
text: pendingReasonerReasoning.text,
|
|
44
|
+
providerOptions: pendingReasonerReasoning.providerOptions,
|
|
45
|
+
};
|
|
46
|
+
if (currentAssistantMessage &&
|
|
47
|
+
Array.isArray(currentAssistantMessage.content) &&
|
|
48
|
+
currentAssistantMessage.role === 'assistant') {
|
|
49
|
+
currentAssistantMessage.content.unshift(reasoningPart);
|
|
50
|
+
currentAssistantMessage.providerOptions = {
|
|
51
|
+
...pendingReasonerReasoning.providerOptions,
|
|
52
|
+
...currentAssistantMessage.providerOptions,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
messages.push({
|
|
57
|
+
role: 'assistant',
|
|
58
|
+
content: [reasoningPart],
|
|
59
|
+
providerOptions: pendingReasonerReasoning.providerOptions,
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
pendingReasonerReasoning = undefined;
|
|
63
|
+
};
|
|
15
64
|
for (const item of items) {
|
|
16
65
|
if (item.type === 'message' || typeof item.type === 'undefined') {
|
|
17
66
|
const { role, content, providerData } = item;
|
|
18
67
|
if (role === 'system') {
|
|
68
|
+
flushPendingReasonerReasoningToMessages();
|
|
19
69
|
messages.push({
|
|
20
70
|
role: 'system',
|
|
21
71
|
content: content,
|
|
22
|
-
providerOptions:
|
|
23
|
-
...(providerData ?? {}),
|
|
24
|
-
},
|
|
72
|
+
providerOptions: toProviderOptions(providerData, model),
|
|
25
73
|
});
|
|
26
74
|
continue;
|
|
27
75
|
}
|
|
28
76
|
if (role === 'user') {
|
|
77
|
+
flushPendingReasonerReasoningToMessages();
|
|
29
78
|
messages.push({
|
|
30
79
|
role,
|
|
31
80
|
content: typeof content === 'string'
|
|
@@ -36,9 +85,7 @@ export function itemsToLanguageV2Messages(model, items) {
|
|
|
36
85
|
return {
|
|
37
86
|
type: 'text',
|
|
38
87
|
text: c.text,
|
|
39
|
-
providerOptions:
|
|
40
|
-
...(contentProviderData ?? {}),
|
|
41
|
-
},
|
|
88
|
+
providerOptions: toProviderOptions(contentProviderData, model),
|
|
42
89
|
};
|
|
43
90
|
}
|
|
44
91
|
if (c.type === 'input_image') {
|
|
@@ -55,9 +102,7 @@ export function itemsToLanguageV2Messages(model, items) {
|
|
|
55
102
|
type: 'file',
|
|
56
103
|
data: url,
|
|
57
104
|
mediaType: 'image/*',
|
|
58
|
-
providerOptions:
|
|
59
|
-
...(contentProviderData ?? {}),
|
|
60
|
-
},
|
|
105
|
+
providerOptions: toProviderOptions(contentProviderData, model),
|
|
61
106
|
};
|
|
62
107
|
}
|
|
63
108
|
if (c.type === 'input_file') {
|
|
@@ -65,9 +110,7 @@ export function itemsToLanguageV2Messages(model, items) {
|
|
|
65
110
|
}
|
|
66
111
|
throw new UserError(`Unknown content type: ${c.type}`);
|
|
67
112
|
}),
|
|
68
|
-
providerOptions:
|
|
69
|
-
...(providerData ?? {}),
|
|
70
|
-
},
|
|
113
|
+
providerOptions: toProviderOptions(providerData, model),
|
|
71
114
|
});
|
|
72
115
|
continue;
|
|
73
116
|
}
|
|
@@ -76,23 +119,39 @@ export function itemsToLanguageV2Messages(model, items) {
|
|
|
76
119
|
messages.push(currentAssistantMessage);
|
|
77
120
|
currentAssistantMessage = undefined;
|
|
78
121
|
}
|
|
122
|
+
const assistantProviderOptions = toProviderOptions(providerData, model);
|
|
123
|
+
const assistantContent = content
|
|
124
|
+
.filter((c) => c.type === 'output_text')
|
|
125
|
+
.map((c) => {
|
|
126
|
+
const { providerData: contentProviderData } = c;
|
|
127
|
+
return {
|
|
128
|
+
type: 'text',
|
|
129
|
+
text: c.text,
|
|
130
|
+
providerOptions: toProviderOptions(contentProviderData, model),
|
|
131
|
+
};
|
|
132
|
+
});
|
|
133
|
+
if (shouldIncludeReasoningContent(model, modelSettings) &&
|
|
134
|
+
pendingReasonerReasoning) {
|
|
135
|
+
assistantContent.unshift({
|
|
136
|
+
type: 'reasoning',
|
|
137
|
+
text: pendingReasonerReasoning.text,
|
|
138
|
+
providerOptions: pendingReasonerReasoning.providerOptions,
|
|
139
|
+
});
|
|
140
|
+
messages.push({
|
|
141
|
+
role,
|
|
142
|
+
content: assistantContent,
|
|
143
|
+
providerOptions: {
|
|
144
|
+
...pendingReasonerReasoning.providerOptions,
|
|
145
|
+
...assistantProviderOptions,
|
|
146
|
+
},
|
|
147
|
+
});
|
|
148
|
+
pendingReasonerReasoning = undefined;
|
|
149
|
+
continue;
|
|
150
|
+
}
|
|
79
151
|
messages.push({
|
|
80
152
|
role,
|
|
81
|
-
content:
|
|
82
|
-
|
|
83
|
-
.map((c) => {
|
|
84
|
-
const { providerData: contentProviderData } = c;
|
|
85
|
-
return {
|
|
86
|
-
type: 'text',
|
|
87
|
-
text: c.text,
|
|
88
|
-
providerOptions: {
|
|
89
|
-
...(contentProviderData ?? {}),
|
|
90
|
-
},
|
|
91
|
-
};
|
|
92
|
-
}),
|
|
93
|
-
providerOptions: {
|
|
94
|
-
...(providerData ?? {}),
|
|
95
|
-
},
|
|
153
|
+
content: assistantContent,
|
|
154
|
+
providerOptions: assistantProviderOptions,
|
|
96
155
|
});
|
|
97
156
|
continue;
|
|
98
157
|
}
|
|
@@ -104,27 +163,38 @@ export function itemsToLanguageV2Messages(model, items) {
|
|
|
104
163
|
currentAssistantMessage = {
|
|
105
164
|
role: 'assistant',
|
|
106
165
|
content: [],
|
|
107
|
-
providerOptions:
|
|
108
|
-
...(item.providerData ?? {}),
|
|
109
|
-
},
|
|
166
|
+
providerOptions: toProviderOptions(item.providerData, model),
|
|
110
167
|
};
|
|
111
168
|
}
|
|
112
169
|
if (Array.isArray(currentAssistantMessage.content) &&
|
|
113
170
|
currentAssistantMessage.role === 'assistant') {
|
|
171
|
+
// Reasoner models (e.g., DeepSeek Reasoner) require reasoning_content on tool-call messages.
|
|
172
|
+
if (shouldIncludeReasoningContent(model, modelSettings) &&
|
|
173
|
+
pendingReasonerReasoning) {
|
|
174
|
+
currentAssistantMessage.content.push({
|
|
175
|
+
type: 'reasoning',
|
|
176
|
+
text: pendingReasonerReasoning.text,
|
|
177
|
+
providerOptions: pendingReasonerReasoning.providerOptions,
|
|
178
|
+
});
|
|
179
|
+
currentAssistantMessage.providerOptions = {
|
|
180
|
+
...pendingReasonerReasoning.providerOptions,
|
|
181
|
+
...currentAssistantMessage.providerOptions,
|
|
182
|
+
};
|
|
183
|
+
pendingReasonerReasoning = undefined;
|
|
184
|
+
}
|
|
114
185
|
const content = {
|
|
115
186
|
type: 'tool-call',
|
|
116
187
|
toolCallId: item.callId,
|
|
117
188
|
toolName: item.name,
|
|
118
189
|
input: parseArguments(item.arguments),
|
|
119
|
-
providerOptions:
|
|
120
|
-
...(item.providerData ?? {}),
|
|
121
|
-
},
|
|
190
|
+
providerOptions: toProviderOptions(item.providerData, model),
|
|
122
191
|
};
|
|
123
192
|
currentAssistantMessage.content.push(content);
|
|
124
193
|
}
|
|
125
194
|
continue;
|
|
126
195
|
}
|
|
127
196
|
else if (item.type === 'function_call_result') {
|
|
197
|
+
flushPendingReasonerReasoningToMessages();
|
|
128
198
|
if (currentAssistantMessage) {
|
|
129
199
|
messages.push(currentAssistantMessage);
|
|
130
200
|
currentAssistantMessage = undefined;
|
|
@@ -134,16 +204,12 @@ export function itemsToLanguageV2Messages(model, items) {
|
|
|
134
204
|
toolCallId: item.callId,
|
|
135
205
|
toolName: item.name,
|
|
136
206
|
output: convertToAiSdkOutput(item.output),
|
|
137
|
-
providerOptions:
|
|
138
|
-
...(item.providerData ?? {}),
|
|
139
|
-
},
|
|
207
|
+
providerOptions: toProviderOptions(item.providerData, model),
|
|
140
208
|
};
|
|
141
209
|
messages.push({
|
|
142
210
|
role: 'tool',
|
|
143
211
|
content: [toolResult],
|
|
144
|
-
providerOptions:
|
|
145
|
-
...(item.providerData ?? {}),
|
|
146
|
-
},
|
|
212
|
+
providerOptions: toProviderOptions(item.providerData, model),
|
|
147
213
|
});
|
|
148
214
|
continue;
|
|
149
215
|
}
|
|
@@ -171,22 +237,29 @@ export function itemsToLanguageV2Messages(model, items) {
|
|
|
171
237
|
if (item.type === 'reasoning' &&
|
|
172
238
|
item.content.length > 0 &&
|
|
173
239
|
typeof item.content[0].text === 'string') {
|
|
240
|
+
// Only forward provider data when it targets this model so signatures stay scoped correctly.
|
|
241
|
+
if (shouldIncludeReasoningContent(model, modelSettings)) {
|
|
242
|
+
pendingReasonerReasoning = {
|
|
243
|
+
text: item.content[0].text,
|
|
244
|
+
providerOptions: toProviderOptions(item.providerData, model),
|
|
245
|
+
};
|
|
246
|
+
continue;
|
|
247
|
+
}
|
|
174
248
|
messages.push({
|
|
175
249
|
role: 'assistant',
|
|
176
250
|
content: [
|
|
177
251
|
{
|
|
178
252
|
type: 'reasoning',
|
|
179
253
|
text: item.content[0].text,
|
|
180
|
-
providerOptions:
|
|
254
|
+
providerOptions: toProviderOptions(item.providerData, model),
|
|
181
255
|
},
|
|
182
256
|
],
|
|
183
|
-
providerOptions:
|
|
184
|
-
...(item.providerData ?? {}),
|
|
185
|
-
},
|
|
257
|
+
providerOptions: toProviderOptions(item.providerData, model),
|
|
186
258
|
});
|
|
187
259
|
continue;
|
|
188
260
|
}
|
|
189
261
|
if (item.type === 'unknown') {
|
|
262
|
+
flushPendingReasonerReasoningToMessages();
|
|
190
263
|
messages.push({ ...(item.providerData ?? {}) });
|
|
191
264
|
continue;
|
|
192
265
|
}
|
|
@@ -196,6 +269,7 @@ export function itemsToLanguageV2Messages(model, items) {
|
|
|
196
269
|
const itemType = item;
|
|
197
270
|
throw new UserError(`Unknown item type: ${itemType}`);
|
|
198
271
|
}
|
|
272
|
+
flushPendingReasonerReasoningToMessages();
|
|
199
273
|
if (currentAssistantMessage) {
|
|
200
274
|
messages.push(currentAssistantMessage);
|
|
201
275
|
}
|
|
@@ -381,6 +455,122 @@ function convertStructuredOutputsToAiSdkOutput(outputs) {
|
|
|
381
455
|
function isRecord(value) {
|
|
382
456
|
return typeof value === 'object' && value !== null;
|
|
383
457
|
}
|
|
458
|
+
function getModelIdentifier(model) {
|
|
459
|
+
return `${model.provider}:${model.modelId}`;
|
|
460
|
+
}
|
|
461
|
+
function isProviderDataForModel(providerData, model) {
|
|
462
|
+
const providerDataModel = providerData.model;
|
|
463
|
+
if (typeof providerDataModel !== 'string') {
|
|
464
|
+
return true;
|
|
465
|
+
}
|
|
466
|
+
const target = getModelIdentifier(model).toLowerCase();
|
|
467
|
+
const pdLower = providerDataModel.toLowerCase();
|
|
468
|
+
return (pdLower === target ||
|
|
469
|
+
pdLower === model.modelId.toLowerCase() ||
|
|
470
|
+
pdLower === model.provider.toLowerCase());
|
|
471
|
+
}
|
|
472
|
+
function isGeminiModel(model) {
|
|
473
|
+
const target = getModelIdentifier(model).toLowerCase();
|
|
474
|
+
return (target.includes('gemini') || model.modelId.toLowerCase().includes('gemini'));
|
|
475
|
+
}
|
|
476
|
+
function isDeepSeekModel(model) {
|
|
477
|
+
const target = getModelIdentifier(model).toLowerCase();
|
|
478
|
+
return (target.includes('deepseek') ||
|
|
479
|
+
model.modelId.toLowerCase().includes('deepseek') ||
|
|
480
|
+
model.provider.toLowerCase().includes('deepseek'));
|
|
481
|
+
}
|
|
482
|
+
function shouldIncludeReasoningContent(model, modelSettings) {
|
|
483
|
+
const target = getModelIdentifier(model).toLowerCase();
|
|
484
|
+
const modelIdLower = model.modelId.toLowerCase();
|
|
485
|
+
// DeepSeek models require reasoning_content to be sent alongside tool calls when
|
|
486
|
+
// either the dedicated reasoner model is used or thinking mode is explicitly enabled.
|
|
487
|
+
const isDeepSeekReasoner = target.includes('deepseek-reasoner') ||
|
|
488
|
+
modelIdLower.includes('deepseek-reasoner');
|
|
489
|
+
if (isDeepSeekReasoner) {
|
|
490
|
+
return true;
|
|
491
|
+
}
|
|
492
|
+
if (!isDeepSeekModel(model)) {
|
|
493
|
+
return false;
|
|
494
|
+
}
|
|
495
|
+
return hasEnabledDeepSeekThinking(modelSettings?.providerData);
|
|
496
|
+
}
|
|
497
|
+
function hasEnabledDeepSeekThinking(providerData) {
|
|
498
|
+
if (!isRecord(providerData)) {
|
|
499
|
+
return false;
|
|
500
|
+
}
|
|
501
|
+
const thinkingOption = [
|
|
502
|
+
providerData.thinking,
|
|
503
|
+
providerData.deepseek?.thinking,
|
|
504
|
+
providerData.providerOptions?.thinking,
|
|
505
|
+
providerData.providerOptions?.deepseek?.thinking,
|
|
506
|
+
].find((value) => value !== undefined);
|
|
507
|
+
return isThinkingEnabled(thinkingOption);
|
|
508
|
+
}
|
|
509
|
+
function isThinkingEnabled(option) {
|
|
510
|
+
if (option === undefined || option === null) {
|
|
511
|
+
return false;
|
|
512
|
+
}
|
|
513
|
+
if (option === true) {
|
|
514
|
+
return true;
|
|
515
|
+
}
|
|
516
|
+
if (typeof option === 'string') {
|
|
517
|
+
return option.toLowerCase() === 'enabled';
|
|
518
|
+
}
|
|
519
|
+
if (isRecord(option)) {
|
|
520
|
+
const type = option.type ?? option.mode ?? option.status;
|
|
521
|
+
if (typeof type === 'string') {
|
|
522
|
+
return type.toLowerCase() === 'enabled';
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
return false;
|
|
526
|
+
}
|
|
527
|
+
function toProviderOptions(providerData, model) {
|
|
528
|
+
if (!isRecord(providerData)) {
|
|
529
|
+
return {};
|
|
530
|
+
}
|
|
531
|
+
if (!isProviderDataForModel(providerData, model)) {
|
|
532
|
+
return {};
|
|
533
|
+
}
|
|
534
|
+
const options = { ...providerData };
|
|
535
|
+
delete options.model;
|
|
536
|
+
delete options.responseId;
|
|
537
|
+
delete options.response_id;
|
|
538
|
+
if (isGeminiModel(model)) {
|
|
539
|
+
const googleFields = isRecord(options.google) ? { ...options.google } : {};
|
|
540
|
+
const thoughtSignature = googleFields.thoughtSignature ??
|
|
541
|
+
googleFields.thought_signature ??
|
|
542
|
+
options.thoughtSignature ??
|
|
543
|
+
options.thought_signature;
|
|
544
|
+
if (thoughtSignature) {
|
|
545
|
+
googleFields.thoughtSignature = thoughtSignature;
|
|
546
|
+
}
|
|
547
|
+
if (Object.keys(googleFields).length > 0) {
|
|
548
|
+
options.google = googleFields;
|
|
549
|
+
}
|
|
550
|
+
delete options.thoughtSignature;
|
|
551
|
+
delete options.thought_signature;
|
|
552
|
+
}
|
|
553
|
+
return options;
|
|
554
|
+
}
|
|
555
|
+
function buildBaseProviderData(model, responseId) {
|
|
556
|
+
const base = { model: getModelIdentifier(model) };
|
|
557
|
+
if (responseId) {
|
|
558
|
+
base.responseId = responseId;
|
|
559
|
+
}
|
|
560
|
+
return base;
|
|
561
|
+
}
|
|
562
|
+
function mergeProviderData(base, ...sources) {
|
|
563
|
+
const merged = {};
|
|
564
|
+
if (isRecord(base)) {
|
|
565
|
+
Object.assign(merged, base);
|
|
566
|
+
}
|
|
567
|
+
for (const src of sources) {
|
|
568
|
+
if (isRecord(src)) {
|
|
569
|
+
Object.assign(merged, src);
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
return Object.keys(merged).length > 0 ? merged : undefined;
|
|
573
|
+
}
|
|
384
574
|
function getImageInlineMediaType(source) {
|
|
385
575
|
if (typeof source.mediaType === 'string' && source.mediaType.length > 0) {
|
|
386
576
|
return source.mediaType;
|
|
@@ -476,6 +666,7 @@ export class AiSdkModel {
|
|
|
476
666
|
#model;
|
|
477
667
|
#logger = getLogger('openai-agents:extensions:ai-sdk');
|
|
478
668
|
constructor(model) {
|
|
669
|
+
ensureSupportedModel(model);
|
|
479
670
|
this.#model = model;
|
|
480
671
|
}
|
|
481
672
|
async getResponse(request) {
|
|
@@ -493,7 +684,7 @@ export class AiSdkModel {
|
|
|
493
684
|
content: [{ type: 'text', text: request.input }],
|
|
494
685
|
},
|
|
495
686
|
]
|
|
496
|
-
: itemsToLanguageV2Messages(this.#model, request.input);
|
|
687
|
+
: itemsToLanguageV2Messages(this.#model, request.input, request.modelSettings);
|
|
497
688
|
if (request.systemInstructions) {
|
|
498
689
|
input = [
|
|
499
690
|
{
|
|
@@ -534,8 +725,10 @@ export class AiSdkModel {
|
|
|
534
725
|
this.#logger.debug('Request:', JSON.stringify(aiSdkRequest, null, 2));
|
|
535
726
|
}
|
|
536
727
|
const result = await this.#model.doGenerate(aiSdkRequest);
|
|
728
|
+
const baseProviderData = buildBaseProviderData(this.#model, result.response?.id);
|
|
537
729
|
const output = [];
|
|
538
730
|
const resultContent = result.content ?? [];
|
|
731
|
+
// Emit reasoning before tool calls so Anthropic thinking signatures propagate into the next turn.
|
|
539
732
|
// Extract and add reasoning items FIRST (required by Anthropic: thinking blocks must precede tool_use blocks)
|
|
540
733
|
const reasoningParts = resultContent.filter((c) => c && c.type === 'reasoning');
|
|
541
734
|
for (const reasoningPart of reasoningParts) {
|
|
@@ -545,7 +738,7 @@ export class AiSdkModel {
|
|
|
545
738
|
content: [{ type: 'input_text', text: reasoningText }],
|
|
546
739
|
rawContent: [{ type: 'reasoning_text', text: reasoningText }],
|
|
547
740
|
// Preserve provider-specific metadata (including signature for Anthropic extended thinking)
|
|
548
|
-
providerData: reasoningPart.providerMetadata
|
|
741
|
+
providerData: mergeProviderData(baseProviderData, reasoningPart.providerMetadata),
|
|
549
742
|
});
|
|
550
743
|
}
|
|
551
744
|
const toolCalls = resultContent.filter((c) => c && c.type === 'tool-call');
|
|
@@ -577,8 +770,8 @@ export class AiSdkModel {
|
|
|
577
770
|
name: toolCall.toolName,
|
|
578
771
|
arguments: toolCallArguments,
|
|
579
772
|
status: 'completed',
|
|
580
|
-
providerData: toolCall.providerMetadata ??
|
|
581
|
-
(hasToolCalls ? result.providerMetadata : undefined),
|
|
773
|
+
providerData: mergeProviderData(baseProviderData, toolCall.providerMetadata ??
|
|
774
|
+
(hasToolCalls ? result.providerMetadata : undefined)),
|
|
582
775
|
});
|
|
583
776
|
}
|
|
584
777
|
// Some of other platforms may return both tool calls and text.
|
|
@@ -593,7 +786,7 @@ export class AiSdkModel {
|
|
|
593
786
|
content: [{ type: 'output_text', text: textItem.text }],
|
|
594
787
|
role: 'assistant',
|
|
595
788
|
status: 'completed',
|
|
596
|
-
providerData: result.providerMetadata,
|
|
789
|
+
providerData: mergeProviderData(baseProviderData, result.providerMetadata),
|
|
597
790
|
});
|
|
598
791
|
}
|
|
599
792
|
}
|
|
@@ -603,18 +796,10 @@ export class AiSdkModel {
|
|
|
603
796
|
const response = {
|
|
604
797
|
responseId: result.response?.id ?? 'FAKE_ID',
|
|
605
798
|
usage: new Usage({
|
|
606
|
-
inputTokens:
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
? 0
|
|
611
|
-
: (result.usage?.outputTokens ?? 0),
|
|
612
|
-
totalTokens: (Number.isNaN(result.usage?.inputTokens)
|
|
613
|
-
? 0
|
|
614
|
-
: (result.usage?.inputTokens ?? 0)) +
|
|
615
|
-
(Number.isNaN(result.usage?.outputTokens)
|
|
616
|
-
? 0
|
|
617
|
-
: (result.usage?.outputTokens ?? 0)) || 0,
|
|
799
|
+
inputTokens: extractTokenCount(result.usage, 'inputTokens'),
|
|
800
|
+
outputTokens: extractTokenCount(result.usage, 'outputTokens'),
|
|
801
|
+
totalTokens: extractTokenCount(result.usage, 'inputTokens') +
|
|
802
|
+
extractTokenCount(result.usage, 'outputTokens') || 0,
|
|
618
803
|
}),
|
|
619
804
|
output,
|
|
620
805
|
providerData: result,
|
|
@@ -702,7 +887,7 @@ export class AiSdkModel {
|
|
|
702
887
|
content: [{ type: 'text', text: request.input }],
|
|
703
888
|
},
|
|
704
889
|
]
|
|
705
|
-
: itemsToLanguageV2Messages(this.#model, request.input);
|
|
890
|
+
: itemsToLanguageV2Messages(this.#model, request.input, request.modelSettings);
|
|
706
891
|
if (request.systemInstructions) {
|
|
707
892
|
input = [
|
|
708
893
|
{
|
|
@@ -722,6 +907,7 @@ export class AiSdkModel {
|
|
|
722
907
|
const responseFormat = getResponseFormat(request.outputType);
|
|
723
908
|
const aiSdkRequest = {
|
|
724
909
|
tools,
|
|
910
|
+
toolChoice: toolChoiceToLanguageV2Format(request.modelSettings.toolChoice),
|
|
725
911
|
prompt: input,
|
|
726
912
|
temperature: request.modelSettings.temperature,
|
|
727
913
|
topP: request.modelSettings.topP,
|
|
@@ -739,13 +925,15 @@ export class AiSdkModel {
|
|
|
739
925
|
this.#logger.debug('Request (streamed):', JSON.stringify(aiSdkRequest, null, 2));
|
|
740
926
|
}
|
|
741
927
|
const { stream } = await this.#model.doStream(aiSdkRequest);
|
|
928
|
+
const baseProviderData = buildBaseProviderData(this.#model);
|
|
742
929
|
let started = false;
|
|
743
930
|
let responseId;
|
|
744
931
|
let usagePromptTokens = 0;
|
|
745
932
|
let usageCompletionTokens = 0;
|
|
746
933
|
const functionCalls = {};
|
|
747
934
|
let textOutput;
|
|
748
|
-
// State for tracking reasoning blocks (for Anthropic extended thinking)
|
|
935
|
+
// State for tracking reasoning blocks (for Anthropic extended thinking):
|
|
936
|
+
// Track reasoning deltas so we can preserve Anthropic signatures even when text is redacted.
|
|
749
937
|
const reasoningBlocks = {};
|
|
750
938
|
for await (const part of stream) {
|
|
751
939
|
if (!started) {
|
|
@@ -801,9 +989,7 @@ export class AiSdkModel {
|
|
|
801
989
|
name: part.toolName,
|
|
802
990
|
arguments: part.input ?? '',
|
|
803
991
|
status: 'completed',
|
|
804
|
-
|
|
805
|
-
? { providerData: part.providerMetadata }
|
|
806
|
-
: {}),
|
|
992
|
+
providerData: mergeProviderData(baseProviderData, part.providerMetadata),
|
|
807
993
|
};
|
|
808
994
|
}
|
|
809
995
|
break;
|
|
@@ -815,12 +1001,8 @@ export class AiSdkModel {
|
|
|
815
1001
|
break;
|
|
816
1002
|
}
|
|
817
1003
|
case 'finish': {
|
|
818
|
-
usagePromptTokens =
|
|
819
|
-
|
|
820
|
-
: (part.usage?.inputTokens ?? 0);
|
|
821
|
-
usageCompletionTokens = Number.isNaN(part.usage?.outputTokens)
|
|
822
|
-
? 0
|
|
823
|
-
: (part.usage?.outputTokens ?? 0);
|
|
1004
|
+
usagePromptTokens = extractTokenCount(part.usage, 'inputTokens');
|
|
1005
|
+
usageCompletionTokens = extractTokenCount(part.usage, 'outputTokens');
|
|
824
1006
|
break;
|
|
825
1007
|
}
|
|
826
1008
|
case 'error': {
|
|
@@ -841,7 +1023,7 @@ export class AiSdkModel {
|
|
|
841
1023
|
content: [{ type: 'input_text', text: reasoningBlock.text }],
|
|
842
1024
|
rawContent: [{ type: 'reasoning_text', text: reasoningBlock.text }],
|
|
843
1025
|
// Preserve provider-specific metadata (including signature for Anthropic extended thinking)
|
|
844
|
-
providerData: reasoningBlock.providerMetadata
|
|
1026
|
+
providerData: mergeProviderData(baseProviderData, reasoningBlock.providerMetadata, responseId ? { responseId } : undefined),
|
|
845
1027
|
});
|
|
846
1028
|
}
|
|
847
1029
|
}
|
|
@@ -851,10 +1033,14 @@ export class AiSdkModel {
|
|
|
851
1033
|
role: 'assistant',
|
|
852
1034
|
content: [textOutput],
|
|
853
1035
|
status: 'completed',
|
|
1036
|
+
providerData: mergeProviderData(baseProviderData, responseId ? { responseId } : undefined),
|
|
854
1037
|
});
|
|
855
1038
|
}
|
|
856
1039
|
for (const fc of Object.values(functionCalls)) {
|
|
857
|
-
outputs.push(
|
|
1040
|
+
outputs.push({
|
|
1041
|
+
...fc,
|
|
1042
|
+
providerData: mergeProviderData(baseProviderData, fc.providerData, responseId ? { responseId } : undefined),
|
|
1043
|
+
});
|
|
858
1044
|
}
|
|
859
1045
|
const finalEvent = {
|
|
860
1046
|
type: 'response_done',
|
|
@@ -960,6 +1146,19 @@ export class AiSdkModel {
|
|
|
960
1146
|
export function aisdk(model) {
|
|
961
1147
|
return new AiSdkModel(model);
|
|
962
1148
|
}
|
|
1149
|
+
function extractTokenCount(usage, key) {
|
|
1150
|
+
const val = usage?.[key];
|
|
1151
|
+
if (typeof val === 'number') {
|
|
1152
|
+
return Number.isNaN(val) ? 0 : val;
|
|
1153
|
+
}
|
|
1154
|
+
// Handle Google AI SDK object format ({ total: number, ... })
|
|
1155
|
+
if (typeof val === 'object' &&
|
|
1156
|
+
val !== null &&
|
|
1157
|
+
typeof val.total === 'number') {
|
|
1158
|
+
return val.total;
|
|
1159
|
+
}
|
|
1160
|
+
return 0;
|
|
1161
|
+
}
|
|
963
1162
|
export function parseArguments(args) {
|
|
964
1163
|
if (!args) {
|
|
965
1164
|
return {};
|