@tanstack/openai-base 0.9.9 → 0.9.11
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/esm/adapters/chat-completions-text.js +844 -962
- package/dist/esm/adapters/chat-completions-text.js.map +1 -1
- package/dist/esm/adapters/chat-completions-tool-converter.js +54 -38
- package/dist/esm/adapters/chat-completions-tool-converter.js.map +1 -1
- package/dist/esm/adapters/responses-text.d.ts +22 -8
- package/dist/esm/adapters/responses-text.js +1193 -1319
- package/dist/esm/adapters/responses-text.js.map +1 -1
- package/dist/esm/adapters/responses-tool-converter.js +50 -34
- package/dist/esm/adapters/responses-tool-converter.js.map +1 -1
- package/dist/esm/index.d.ts +1 -1
- package/dist/esm/index.js +4 -43
- package/dist/esm/tools/apply-patch-tool.js +20 -13
- package/dist/esm/tools/apply-patch-tool.js.map +1 -1
- package/dist/esm/tools/code-interpreter-tool.js +26 -18
- package/dist/esm/tools/code-interpreter-tool.js.map +1 -1
- package/dist/esm/tools/computer-use-tool.js +26 -19
- package/dist/esm/tools/computer-use-tool.js.map +1 -1
- package/dist/esm/tools/custom-tool.js +23 -21
- package/dist/esm/tools/custom-tool.js.map +1 -1
- package/dist/esm/tools/file-search-tool.js +30 -30
- package/dist/esm/tools/file-search-tool.js.map +1 -1
- package/dist/esm/tools/function-tool.js +44 -31
- package/dist/esm/tools/function-tool.js.map +1 -1
- package/dist/esm/tools/image-generation-tool.js +30 -23
- package/dist/esm/tools/image-generation-tool.js.map +1 -1
- package/dist/esm/tools/local-shell-tool.js +20 -13
- package/dist/esm/tools/local-shell-tool.js.map +1 -1
- package/dist/esm/tools/mcp-tool.js +31 -25
- package/dist/esm/tools/mcp-tool.js.map +1 -1
- package/dist/esm/tools/shell-tool.js +25 -21
- package/dist/esm/tools/shell-tool.js.map +1 -1
- package/dist/esm/tools/tool-converter.js +37 -46
- package/dist/esm/tools/tool-converter.js.map +1 -1
- package/dist/esm/tools/web-search-preview-tool.js +26 -15
- package/dist/esm/tools/web-search-preview-tool.js.map +1 -1
- package/dist/esm/tools/web-search-tool.js +27 -15
- package/dist/esm/tools/web-search-tool.js.map +1 -1
- package/dist/esm/usage.js +88 -76
- package/dist/esm/usage.js.map +1 -1
- package/dist/esm/utils/request-options.js +20 -9
- package/dist/esm/utils/request-options.js.map +1 -1
- package/dist/esm/utils/schema-converter.js +193 -146
- package/dist/esm/utils/schema-converter.js.map +1 -1
- package/package.json +7 -7
- package/src/adapters/responses-text.ts +84 -57
- package/src/index.ts +4 -1
- package/dist/esm/index.js.map +0 -1
|
@@ -31,6 +31,27 @@ import type {
|
|
|
31
31
|
TextOptions,
|
|
32
32
|
} from '@tanstack/ai'
|
|
33
33
|
|
|
34
|
+
/**
|
|
35
|
+
* Provider-specific metadata that preserves the Responses API output item ID.
|
|
36
|
+
*
|
|
37
|
+
* Responses function calls have two identifiers: `call_id` correlates the
|
|
38
|
+
* function output with the call, while `id` identifies the output item itself.
|
|
39
|
+
* TanStack AI uses `call_id` as the canonical tool-call ID and carries the
|
|
40
|
+
* item ID here so stateless follow-up requests can replay both values.
|
|
41
|
+
*/
|
|
42
|
+
export interface OpenAIResponsesToolCallMetadata {
|
|
43
|
+
itemId: string
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
interface StreamedFunctionCallMetadata {
|
|
47
|
+
callId: string
|
|
48
|
+
index: number
|
|
49
|
+
name: string
|
|
50
|
+
started: boolean
|
|
51
|
+
ended?: boolean
|
|
52
|
+
pendingArguments?: string | undefined
|
|
53
|
+
}
|
|
54
|
+
|
|
34
55
|
/**
|
|
35
56
|
* Shared implementation of the OpenAI Responses API. Holds the stream-event
|
|
36
57
|
* accumulator + AG-UI lifecycle and calls the OpenAI SDK directly. Subclasses
|
|
@@ -49,7 +70,8 @@ export abstract class OpenAIBaseResponsesTextAdapter<
|
|
|
49
70
|
TProviderOptions,
|
|
50
71
|
TInputModalities,
|
|
51
72
|
TMessageMetadata,
|
|
52
|
-
TToolCapabilities
|
|
73
|
+
TToolCapabilities,
|
|
74
|
+
OpenAIResponsesToolCallMetadata
|
|
53
75
|
> {
|
|
54
76
|
override readonly kind = 'text' as const
|
|
55
77
|
readonly name: string
|
|
@@ -64,30 +86,17 @@ export abstract class OpenAIBaseResponsesTextAdapter<
|
|
|
64
86
|
async *chatStream(
|
|
65
87
|
options: TextOptions<TProviderOptions>,
|
|
66
88
|
): AsyncIterable<StreamChunk> {
|
|
67
|
-
//
|
|
68
|
-
//
|
|
69
|
-
//
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
started: boolean
|
|
77
|
-
// Set once TOOL_CALL_END has been emitted (via args.done or the
|
|
78
|
-
// output_item.done backfill) so the two paths don't double-emit.
|
|
79
|
-
ended?: boolean
|
|
80
|
-
// Set when args.done arrives before TOOL_CALL_START could fire
|
|
81
|
-
// (output_item.added lacked a name). output_item.done picks these
|
|
82
|
-
// up to emit the missing END. Allow explicit `undefined` so the
|
|
83
|
-
// emission paths can re-clear the slot after handing it off.
|
|
84
|
-
pendingArguments?: string
|
|
85
|
-
}
|
|
86
|
-
>()
|
|
87
|
-
|
|
88
|
-
// AG-UI lifecycle tracking
|
|
89
|
+
// Key streamed state by output item ID because argument deltas reference
|
|
90
|
+
// `item_id`. The state separately retains `call_id`, which is the public
|
|
91
|
+
// tool-call ID and the correlation key for function_call_output.
|
|
92
|
+
const toolCallMetadata = new Map<string, StreamedFunctionCallMetadata>()
|
|
93
|
+
|
|
94
|
+
// AG-UI lifecycle tracking. Honor a caller-supplied `runId` (as `threadId`
|
|
95
|
+
// already does) so the emitted RUN_STARTED matches the id the caller keys
|
|
96
|
+
// durability by — e.g. a summarize run threading the client's runId through
|
|
97
|
+
// for mid-run reload resumability. Falls back to a generated id.
|
|
89
98
|
const aguiState = {
|
|
90
|
-
runId: generateId(this.name),
|
|
99
|
+
runId: options.runId ?? generateId(this.name),
|
|
91
100
|
threadId: options.threadId ?? generateId(this.name),
|
|
92
101
|
messageId: generateId(this.name),
|
|
93
102
|
hasEmittedRunStarted: false,
|
|
@@ -759,16 +768,7 @@ export abstract class OpenAIBaseResponsesTextAdapter<
|
|
|
759
768
|
*/
|
|
760
769
|
protected async *processStreamChunks(
|
|
761
770
|
stream: AsyncIterable<ResponseStreamEvent>,
|
|
762
|
-
toolCallMetadata: Map<
|
|
763
|
-
string,
|
|
764
|
-
{
|
|
765
|
-
index: number
|
|
766
|
-
name: string
|
|
767
|
-
started: boolean
|
|
768
|
-
ended?: boolean
|
|
769
|
-
pendingArguments?: string | undefined
|
|
770
|
-
}
|
|
771
|
-
>,
|
|
771
|
+
toolCallMetadata: Map<string, StreamedFunctionCallMetadata>,
|
|
772
772
|
options: TextOptions<TProviderOptions>,
|
|
773
773
|
aguiState: {
|
|
774
774
|
runId: string
|
|
@@ -1184,26 +1184,33 @@ export abstract class OpenAIBaseResponsesTextAdapter<
|
|
|
1184
1184
|
let metadata = toolCallMetadata.get(item.id)
|
|
1185
1185
|
if (!metadata) {
|
|
1186
1186
|
metadata = {
|
|
1187
|
+
callId: item.call_id || item.id,
|
|
1187
1188
|
index: chunk.output_index,
|
|
1188
1189
|
name: item.name || '',
|
|
1189
1190
|
started: false,
|
|
1190
1191
|
}
|
|
1191
1192
|
toolCallMetadata.set(item.id, metadata)
|
|
1192
|
-
} else
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1193
|
+
} else {
|
|
1194
|
+
if (item.call_id) metadata.callId = item.call_id
|
|
1195
|
+
if (!metadata.name && item.name) {
|
|
1196
|
+
// A later output_item.added for the same id finally carries
|
|
1197
|
+
// the name. Update so the gated emission below can fire.
|
|
1198
|
+
metadata.name = item.name
|
|
1199
|
+
}
|
|
1196
1200
|
}
|
|
1197
1201
|
if (!metadata.started && metadata.name) {
|
|
1198
1202
|
yield {
|
|
1199
1203
|
type: EventType.TOOL_CALL_START,
|
|
1200
|
-
toolCallId:
|
|
1204
|
+
toolCallId: metadata.callId,
|
|
1201
1205
|
toolCallName: metadata.name,
|
|
1202
1206
|
toolName: metadata.name,
|
|
1203
1207
|
parentMessageId: aguiState.messageId,
|
|
1204
1208
|
model: model || options.model,
|
|
1205
1209
|
timestamp: Date.now(),
|
|
1206
1210
|
index: chunk.output_index,
|
|
1211
|
+
metadata: {
|
|
1212
|
+
itemId: item.id,
|
|
1213
|
+
} satisfies OpenAIResponsesToolCallMetadata,
|
|
1207
1214
|
}
|
|
1208
1215
|
metadata.started = true
|
|
1209
1216
|
}
|
|
@@ -1232,7 +1239,9 @@ export abstract class OpenAIBaseResponsesTextAdapter<
|
|
|
1232
1239
|
`${this.name}.processStreamChunks orphan function_call_arguments.delta`,
|
|
1233
1240
|
{
|
|
1234
1241
|
source: `${this.name}.processStreamChunks`,
|
|
1235
|
-
|
|
1242
|
+
// No metadata yet, so the `call_id` is unknown here — only the
|
|
1243
|
+
// output item id the delta referenced.
|
|
1244
|
+
itemId: chunk.item_id,
|
|
1236
1245
|
rawDelta: chunk.delta,
|
|
1237
1246
|
},
|
|
1238
1247
|
)
|
|
@@ -1240,7 +1249,7 @@ export abstract class OpenAIBaseResponsesTextAdapter<
|
|
|
1240
1249
|
}
|
|
1241
1250
|
yield {
|
|
1242
1251
|
type: EventType.TOOL_CALL_ARGS,
|
|
1243
|
-
toolCallId:
|
|
1252
|
+
toolCallId: metadata.callId,
|
|
1244
1253
|
model: model || options.model,
|
|
1245
1254
|
timestamp: Date.now(),
|
|
1246
1255
|
delta: chunk.delta,
|
|
@@ -1267,7 +1276,8 @@ export abstract class OpenAIBaseResponsesTextAdapter<
|
|
|
1267
1276
|
`${this.name}.processStreamChunks deferring function_call_arguments.done — TOOL_CALL_START not yet emitted (waiting for name)`,
|
|
1268
1277
|
{
|
|
1269
1278
|
source: `${this.name}.processStreamChunks`,
|
|
1270
|
-
toolCallId:
|
|
1279
|
+
...(metadata && { toolCallId: metadata.callId }),
|
|
1280
|
+
itemId: item_id,
|
|
1271
1281
|
rawArguments: chunk.arguments,
|
|
1272
1282
|
},
|
|
1273
1283
|
)
|
|
@@ -1294,10 +1304,11 @@ export abstract class OpenAIBaseResponsesTextAdapter<
|
|
|
1294
1304
|
{
|
|
1295
1305
|
error: toRunErrorPayload(
|
|
1296
1306
|
parseError,
|
|
1297
|
-
`tool ${name} (${
|
|
1307
|
+
`tool ${name} (${metadata.callId}) returned malformed JSON arguments`,
|
|
1298
1308
|
),
|
|
1299
1309
|
source: `${this.name}.processStreamChunks`,
|
|
1300
|
-
toolCallId:
|
|
1310
|
+
toolCallId: metadata.callId,
|
|
1311
|
+
itemId: item_id,
|
|
1301
1312
|
toolName: name,
|
|
1302
1313
|
rawArguments: chunk.arguments,
|
|
1303
1314
|
},
|
|
@@ -1308,7 +1319,7 @@ export abstract class OpenAIBaseResponsesTextAdapter<
|
|
|
1308
1319
|
|
|
1309
1320
|
yield {
|
|
1310
1321
|
type: EventType.TOOL_CALL_END,
|
|
1311
|
-
toolCallId:
|
|
1322
|
+
toolCallId: metadata.callId,
|
|
1312
1323
|
toolCallName: name,
|
|
1313
1324
|
toolName: name,
|
|
1314
1325
|
model: model || options.model,
|
|
@@ -1326,26 +1337,31 @@ export abstract class OpenAIBaseResponsesTextAdapter<
|
|
|
1326
1337
|
const item = chunk.item
|
|
1327
1338
|
if (item.type === 'function_call' && item.id) {
|
|
1328
1339
|
const metadata = toolCallMetadata.get(item.id) ?? {
|
|
1340
|
+
callId: item.call_id || item.id,
|
|
1329
1341
|
index: chunk.output_index,
|
|
1330
1342
|
name: item.name || '',
|
|
1331
1343
|
started: false,
|
|
1332
1344
|
}
|
|
1333
1345
|
if (!toolCallMetadata.has(item.id)) {
|
|
1334
1346
|
toolCallMetadata.set(item.id, metadata)
|
|
1335
|
-
} else
|
|
1336
|
-
metadata.
|
|
1347
|
+
} else {
|
|
1348
|
+
if (item.call_id) metadata.callId = item.call_id
|
|
1349
|
+
if (!metadata.name && item.name) metadata.name = item.name
|
|
1337
1350
|
}
|
|
1338
1351
|
// Emit gated START if we now have a name and never started.
|
|
1339
1352
|
if (!metadata.started && metadata.name) {
|
|
1340
1353
|
yield {
|
|
1341
1354
|
type: EventType.TOOL_CALL_START,
|
|
1342
|
-
toolCallId:
|
|
1355
|
+
toolCallId: metadata.callId,
|
|
1343
1356
|
toolCallName: metadata.name,
|
|
1344
1357
|
toolName: metadata.name,
|
|
1345
1358
|
parentMessageId: aguiState.messageId,
|
|
1346
1359
|
model: model || options.model,
|
|
1347
1360
|
timestamp: Date.now(),
|
|
1348
1361
|
index: metadata.index,
|
|
1362
|
+
metadata: {
|
|
1363
|
+
itemId: item.id,
|
|
1364
|
+
} satisfies OpenAIResponsesToolCallMetadata,
|
|
1349
1365
|
}
|
|
1350
1366
|
metadata.started = true
|
|
1351
1367
|
}
|
|
@@ -1369,10 +1385,11 @@ export abstract class OpenAIBaseResponsesTextAdapter<
|
|
|
1369
1385
|
{
|
|
1370
1386
|
error: toRunErrorPayload(
|
|
1371
1387
|
parseError,
|
|
1372
|
-
`tool ${name} (${
|
|
1388
|
+
`tool ${name} (${metadata.callId}) returned malformed JSON arguments`,
|
|
1373
1389
|
),
|
|
1374
1390
|
source: `${this.name}.processStreamChunks`,
|
|
1375
|
-
toolCallId:
|
|
1391
|
+
toolCallId: metadata.callId,
|
|
1392
|
+
itemId: item.id,
|
|
1376
1393
|
toolName: name,
|
|
1377
1394
|
rawArguments: rawArgs,
|
|
1378
1395
|
},
|
|
@@ -1382,7 +1399,7 @@ export abstract class OpenAIBaseResponsesTextAdapter<
|
|
|
1382
1399
|
}
|
|
1383
1400
|
yield {
|
|
1384
1401
|
type: EventType.TOOL_CALL_END,
|
|
1385
|
-
toolCallId:
|
|
1402
|
+
toolCallId: metadata.callId,
|
|
1386
1403
|
toolCallName: name,
|
|
1387
1404
|
toolName: name,
|
|
1388
1405
|
model: model || options.model,
|
|
@@ -1406,25 +1423,30 @@ export abstract class OpenAIBaseResponsesTextAdapter<
|
|
|
1406
1423
|
for (const item of chunk.response.output) {
|
|
1407
1424
|
if (item.type !== 'function_call' || !item.id) continue
|
|
1408
1425
|
const metadata = toolCallMetadata.get(item.id) ?? {
|
|
1426
|
+
callId: item.call_id || item.id,
|
|
1409
1427
|
index: 0,
|
|
1410
1428
|
name: item.name || '',
|
|
1411
1429
|
started: false,
|
|
1412
1430
|
}
|
|
1413
1431
|
if (!toolCallMetadata.has(item.id)) {
|
|
1414
1432
|
toolCallMetadata.set(item.id, metadata)
|
|
1415
|
-
} else
|
|
1416
|
-
metadata.
|
|
1433
|
+
} else {
|
|
1434
|
+
if (item.call_id) metadata.callId = item.call_id
|
|
1435
|
+
if (!metadata.name && item.name) metadata.name = item.name
|
|
1417
1436
|
}
|
|
1418
1437
|
if (!metadata.started && metadata.name) {
|
|
1419
1438
|
yield {
|
|
1420
1439
|
type: EventType.TOOL_CALL_START,
|
|
1421
|
-
toolCallId:
|
|
1440
|
+
toolCallId: metadata.callId,
|
|
1422
1441
|
toolCallName: metadata.name,
|
|
1423
1442
|
toolName: metadata.name,
|
|
1424
1443
|
parentMessageId: aguiState.messageId,
|
|
1425
1444
|
model: model || options.model,
|
|
1426
1445
|
timestamp: Date.now(),
|
|
1427
1446
|
index: metadata.index,
|
|
1447
|
+
metadata: {
|
|
1448
|
+
itemId: item.id,
|
|
1449
|
+
} satisfies OpenAIResponsesToolCallMetadata,
|
|
1428
1450
|
}
|
|
1429
1451
|
metadata.started = true
|
|
1430
1452
|
}
|
|
@@ -1446,10 +1468,11 @@ export abstract class OpenAIBaseResponsesTextAdapter<
|
|
|
1446
1468
|
{
|
|
1447
1469
|
error: toRunErrorPayload(
|
|
1448
1470
|
parseError,
|
|
1449
|
-
`tool ${name} (${
|
|
1471
|
+
`tool ${name} (${metadata.callId}) returned malformed JSON arguments`,
|
|
1450
1472
|
),
|
|
1451
1473
|
source: `${this.name}.processStreamChunks`,
|
|
1452
|
-
toolCallId:
|
|
1474
|
+
toolCallId: metadata.callId,
|
|
1475
|
+
itemId: item.id,
|
|
1453
1476
|
toolName: name,
|
|
1454
1477
|
rawArguments: rawArgs,
|
|
1455
1478
|
},
|
|
@@ -1459,7 +1482,7 @@ export abstract class OpenAIBaseResponsesTextAdapter<
|
|
|
1459
1482
|
}
|
|
1460
1483
|
yield {
|
|
1461
1484
|
type: EventType.TOOL_CALL_END,
|
|
1462
|
-
toolCallId:
|
|
1485
|
+
toolCallId: metadata.callId,
|
|
1463
1486
|
toolCallName: name,
|
|
1464
1487
|
toolName: name,
|
|
1465
1488
|
model: model || options.model,
|
|
@@ -1725,10 +1748,14 @@ export abstract class OpenAIBaseResponsesTextAdapter<
|
|
|
1725
1748
|
typeof toolCall.function.arguments === 'string'
|
|
1726
1749
|
? toolCall.function.arguments
|
|
1727
1750
|
: JSON.stringify(toolCall.function.arguments)
|
|
1751
|
+
const itemId = (
|
|
1752
|
+
toolCall.metadata as OpenAIResponsesToolCallMetadata | undefined
|
|
1753
|
+
)?.itemId
|
|
1728
1754
|
|
|
1729
1755
|
result.push({
|
|
1730
1756
|
type: 'function_call',
|
|
1731
1757
|
call_id: toolCall.id,
|
|
1758
|
+
...(itemId && { id: itemId }),
|
|
1732
1759
|
name: toolCall.function.name,
|
|
1733
1760
|
arguments: argumentsString,
|
|
1734
1761
|
})
|
package/src/index.ts
CHANGED
|
@@ -11,7 +11,10 @@ export {
|
|
|
11
11
|
convertToolsToChatCompletionsFormat,
|
|
12
12
|
type ChatCompletionFunctionTool,
|
|
13
13
|
} from './adapters/chat-completions-tool-converter'
|
|
14
|
-
export {
|
|
14
|
+
export {
|
|
15
|
+
OpenAIBaseResponsesTextAdapter,
|
|
16
|
+
type OpenAIResponsesToolCallMetadata,
|
|
17
|
+
} from './adapters/responses-text'
|
|
15
18
|
export {
|
|
16
19
|
convertFunctionToolToResponsesFormat,
|
|
17
20
|
convertToolsToResponsesFormat,
|
package/dist/esm/index.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;"}
|