@codehz/ai 0.2.4 → 0.4.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/README.md +3 -8
- package/dist/index.d.mts +79 -89
- package/dist/index.mjs +717 -931
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/adapters/chat-completions.ts +153 -266
- package/src/adapters/messages.ts +179 -301
- package/src/adapters/mock.ts +1 -10
- package/src/adapters/ollama.ts +142 -257
- package/src/adapters/responses.ts +141 -251
- package/src/core/validation.ts +19 -0
- package/src/helpers/adapter-auxiliary.ts +1 -23
- package/src/helpers/adapter-base.ts +41 -7
- package/src/helpers/incremental-stream-parser.ts +58 -0
- package/src/helpers/index.ts +20 -8
- package/src/helpers/mapping.ts +1 -10
- package/src/helpers/provider-stream.ts +147 -0
- package/src/helpers/request-mapper.ts +47 -25
- package/src/helpers/usage-mapping.ts +36 -39
- package/src/types/adapter.ts +1 -12
- package/src/types/index.ts +1 -9
- package/src/types/items.ts +0 -1
- package/src/helpers/sse-parser.ts +0 -113
package/dist/index.mjs
CHANGED
|
@@ -156,6 +156,12 @@ function validateInputItem(item, field, issues) {
|
|
|
156
156
|
if (typeof item.id !== "string" || item.id.length === 0) pushIssue(issues, `${field}.id`, "TOOL_CALL_ID_INVALID", `${field}.id must be a non-empty string`);
|
|
157
157
|
if (typeof item.name !== "string" || item.name.length === 0) pushIssue(issues, `${field}.name`, "TOOL_CALL_NAME_INVALID", `${field}.name must be a non-empty string`);
|
|
158
158
|
if (typeof item.argumentsText !== "string") pushIssue(issues, `${field}.argumentsText`, "TOOL_CALL_ARGUMENTS_INVALID", `${field}.argumentsText must be a string`);
|
|
159
|
+
else try {
|
|
160
|
+
const parsed = JSON.parse(item.argumentsText);
|
|
161
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) pushIssue(issues, `${field}.argumentsText`, "TOOL_CALL_ARGUMENTS_INVALID", `${field}.argumentsText must encode a JSON object`);
|
|
162
|
+
} catch {
|
|
163
|
+
pushIssue(issues, `${field}.argumentsText`, "TOOL_CALL_ARGUMENTS_INVALID", `${field}.argumentsText must encode a JSON object`);
|
|
164
|
+
}
|
|
159
165
|
return;
|
|
160
166
|
case "tool_result":
|
|
161
167
|
if (typeof item.callId !== "string" || item.callId.length === 0) pushIssue(issues, `${field}.callId`, "TOOL_RESULT_CALL_ID_INVALID", `${field}.callId must be a non-empty string`);
|
|
@@ -820,13 +826,12 @@ function reasoningItem(content, visibility = "full", id) {
|
|
|
820
826
|
content
|
|
821
827
|
};
|
|
822
828
|
}
|
|
823
|
-
function toolCallItem(id, name, argumentsText
|
|
829
|
+
function toolCallItem(id, name, argumentsText) {
|
|
824
830
|
return {
|
|
825
831
|
type: "tool_call",
|
|
826
832
|
id,
|
|
827
833
|
name,
|
|
828
|
-
argumentsText
|
|
829
|
-
argumentsJson
|
|
834
|
+
argumentsText
|
|
830
835
|
};
|
|
831
836
|
}
|
|
832
837
|
function toolResultItem(callId, toolName, outcome, content) {
|
|
@@ -878,12 +883,6 @@ function contentBlocksToText(blocks) {
|
|
|
878
883
|
return blocks.map(blockToText).join("\n");
|
|
879
884
|
}
|
|
880
885
|
/**
|
|
881
|
-
* 将 instructions(string | InstructionBlock[])归一化为纯文本。
|
|
882
|
-
*/
|
|
883
|
-
function instructionsToText(instructions) {
|
|
884
|
-
return typeof instructions === "string" ? instructions : contentBlocksToText(instructions);
|
|
885
|
-
}
|
|
886
|
-
/**
|
|
887
886
|
* 从 OutputItem 数组中提取所有 message 类型 item 的文本内容。
|
|
888
887
|
*/
|
|
889
888
|
function extractText(output) {
|
|
@@ -1069,14 +1068,6 @@ function emitMalformedStreamWarning(factory, options) {
|
|
|
1069
1068
|
if (options.count < 1) return void 0;
|
|
1070
1069
|
return factory.responseWarning(`Skipped ${options.count} malformed ${options.providerLabel} ${options.transportLabel}`, "STREAM_ERROR");
|
|
1071
1070
|
}
|
|
1072
|
-
function metadataSourceList(...groups) {
|
|
1073
|
-
const sources = /* @__PURE__ */ new Set();
|
|
1074
|
-
for (const group of groups) {
|
|
1075
|
-
if (!group) continue;
|
|
1076
|
-
for (const source of group) sources.add(source);
|
|
1077
|
-
}
|
|
1078
|
-
return sources.size > 0 ? [...sources] : void 0;
|
|
1079
|
-
}
|
|
1080
1071
|
function isEmptyRecord(value) {
|
|
1081
1072
|
return Object.keys(value).length === 0;
|
|
1082
1073
|
}
|
|
@@ -1095,7 +1086,7 @@ var AdapterBase = class {
|
|
|
1095
1086
|
responseId: request.requestId,
|
|
1096
1087
|
backend: {
|
|
1097
1088
|
kind: this.kind,
|
|
1098
|
-
isSynthetic: this.
|
|
1089
|
+
isSynthetic: this.isSyntheticStream
|
|
1099
1090
|
}
|
|
1100
1091
|
});
|
|
1101
1092
|
yield factory.responseStarted(request.model);
|
|
@@ -1129,7 +1120,7 @@ var AdapterBase = class {
|
|
|
1129
1120
|
* 子类可在返回前自定义覆盖。
|
|
1130
1121
|
*/
|
|
1131
1122
|
buildResponse(request, result, _factory) {
|
|
1132
|
-
const text =
|
|
1123
|
+
const text = extractText(result.output);
|
|
1133
1124
|
const warnings = mergeWarnings(result.warnings, _factory.warnings);
|
|
1134
1125
|
const auxiliary = mergeAuxiliary(result.auxiliary, result.providerMetadata ? { providerMetadata: result.providerMetadata } : void 0);
|
|
1135
1126
|
return {
|
|
@@ -1147,15 +1138,36 @@ var AdapterBase = class {
|
|
|
1147
1138
|
requestId: request.requestId,
|
|
1148
1139
|
rawResponseId: result.rawResponseId,
|
|
1149
1140
|
adapter: this.kind,
|
|
1150
|
-
isSyntheticStream: this.
|
|
1141
|
+
isSyntheticStream: this.isSyntheticStream,
|
|
1151
1142
|
metadataSources: result.metadataSources,
|
|
1152
1143
|
warnings
|
|
1153
1144
|
}
|
|
1154
1145
|
};
|
|
1155
1146
|
}
|
|
1156
|
-
/**
|
|
1157
|
-
|
|
1158
|
-
|
|
1147
|
+
/**
|
|
1148
|
+
* 统一 finalize auxiliary → response.completed。
|
|
1149
|
+
* adapter 在调用前组装 output / replay / stopReason 等业务字段。
|
|
1150
|
+
*/
|
|
1151
|
+
async *emitStreamCompleted(factory, request, auxiliary, result) {
|
|
1152
|
+
const auxiliaryResult = await auxiliary.finalize(factory);
|
|
1153
|
+
for (const event of auxiliaryResult.events) yield event;
|
|
1154
|
+
const finalResponse = this.buildResponse(request, {
|
|
1155
|
+
...result,
|
|
1156
|
+
usage: result.usage ?? auxiliaryResult.usage,
|
|
1157
|
+
billing: result.billing ?? auxiliaryResult.billing,
|
|
1158
|
+
auxiliary: mergeAuxiliary(result.auxiliary, auxiliaryResult.auxiliary),
|
|
1159
|
+
warnings: mergeWarnings(result.warnings, auxiliaryResult.warnings),
|
|
1160
|
+
metadataSources: result.metadataSources ?? auxiliaryResult.metadataSources
|
|
1161
|
+
}, factory);
|
|
1162
|
+
yield factory.responseCompleted({
|
|
1163
|
+
replay: finalResponse.replay,
|
|
1164
|
+
stopReason: finalResponse.stopReason,
|
|
1165
|
+
trace: finalResponse.backend,
|
|
1166
|
+
usage: finalResponse.usage,
|
|
1167
|
+
billing: finalResponse.billing,
|
|
1168
|
+
auxiliary: finalResponse.auxiliary,
|
|
1169
|
+
warnings: finalResponse.warnings
|
|
1170
|
+
});
|
|
1159
1171
|
}
|
|
1160
1172
|
createAuxiliaryState(request) {
|
|
1161
1173
|
return new AdapterAuxiliaryState(request);
|
|
@@ -1264,32 +1276,35 @@ function record(obj) {
|
|
|
1264
1276
|
for (const [key, value] of Object.entries(obj)) if (value !== void 0) out[key] = value;
|
|
1265
1277
|
return out;
|
|
1266
1278
|
}
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
const inputTokens = num(raw.prompt_tokens);
|
|
1270
|
-
const outputTokens = num(raw.completion_tokens);
|
|
1271
|
-
const cachedInputTokens = num(raw.prompt_tokens_details?.cached_tokens);
|
|
1272
|
-
const reasoningTokens = num(raw.completion_tokens_details?.reasoning_tokens);
|
|
1279
|
+
function withDerivedTotal(usage) {
|
|
1280
|
+
const { inputTokens, outputTokens, totalTokens, cachedInputTokens, reasoningTokens, cacheWriteInputTokens } = usage;
|
|
1273
1281
|
return record({
|
|
1274
1282
|
inputTokens,
|
|
1275
1283
|
outputTokens,
|
|
1276
|
-
totalTokens:
|
|
1284
|
+
totalTokens: totalTokens ?? (inputTokens !== void 0 && outputTokens !== void 0 ? inputTokens + outputTokens : void 0),
|
|
1277
1285
|
cachedInputTokens,
|
|
1278
|
-
reasoningTokens
|
|
1286
|
+
reasoningTokens,
|
|
1287
|
+
cacheWriteInputTokens
|
|
1288
|
+
});
|
|
1289
|
+
}
|
|
1290
|
+
/** OpenAI Chat Completions `usage` */
|
|
1291
|
+
function usageFromChatCompletions(raw) {
|
|
1292
|
+
return withDerivedTotal({
|
|
1293
|
+
inputTokens: num(raw.prompt_tokens),
|
|
1294
|
+
outputTokens: num(raw.completion_tokens),
|
|
1295
|
+
totalTokens: num(raw.total_tokens),
|
|
1296
|
+
cachedInputTokens: num(raw.prompt_tokens_details?.cached_tokens),
|
|
1297
|
+
reasoningTokens: num(raw.completion_tokens_details?.reasoning_tokens)
|
|
1279
1298
|
});
|
|
1280
1299
|
}
|
|
1281
1300
|
/** OpenAI Responses API `usage` */
|
|
1282
1301
|
function usageFromOpenAIResponses(raw) {
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
outputTokens,
|
|
1290
|
-
totalTokens: num(raw.total_tokens) ?? (inputTokens !== void 0 && outputTokens !== void 0 ? inputTokens + outputTokens : void 0),
|
|
1291
|
-
cachedInputTokens,
|
|
1292
|
-
reasoningTokens
|
|
1302
|
+
return withDerivedTotal({
|
|
1303
|
+
inputTokens: num(raw.input_tokens),
|
|
1304
|
+
outputTokens: num(raw.output_tokens),
|
|
1305
|
+
totalTokens: num(raw.total_tokens),
|
|
1306
|
+
cachedInputTokens: num(raw.input_tokens_details?.cached_tokens),
|
|
1307
|
+
reasoningTokens: num(raw.output_tokens_details?.reasoning_tokens)
|
|
1293
1308
|
});
|
|
1294
1309
|
}
|
|
1295
1310
|
/** Anthropic Messages `usage`(message_start / message_delta) */
|
|
@@ -1303,93 +1318,21 @@ function usageFromAnthropicMessages(raw) {
|
|
|
1303
1318
|
cacheWriteInputTokens,
|
|
1304
1319
|
cachedInputTokens
|
|
1305
1320
|
].filter((n) => n !== void 0);
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
inputTokens,
|
|
1321
|
+
return withDerivedTotal({
|
|
1322
|
+
inputTokens: inputParts.length > 0 ? inputParts.reduce((sum, n) => sum + n, 0) : void 0,
|
|
1309
1323
|
outputTokens,
|
|
1310
|
-
totalTokens: inputTokens !== void 0 && outputTokens !== void 0 ? inputTokens + outputTokens : void 0,
|
|
1311
1324
|
cachedInputTokens,
|
|
1312
1325
|
cacheWriteInputTokens
|
|
1313
1326
|
});
|
|
1314
1327
|
}
|
|
1315
1328
|
/** Ollama 流式 chunk */
|
|
1316
1329
|
function usageFromOllama(raw) {
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
inputTokens,
|
|
1321
|
-
outputTokens,
|
|
1322
|
-
totalTokens: inputTokens !== void 0 && outputTokens !== void 0 ? inputTokens + outputTokens : void 0
|
|
1330
|
+
return withDerivedTotal({
|
|
1331
|
+
inputTokens: num(raw.prompt_eval_count),
|
|
1332
|
+
outputTokens: num(raw.eval_count)
|
|
1323
1333
|
});
|
|
1324
1334
|
}
|
|
1325
1335
|
//#endregion
|
|
1326
|
-
//#region src/helpers/sse-parser.ts
|
|
1327
|
-
/**
|
|
1328
|
-
* 将 SSE 文本块解析为事件数组。
|
|
1329
|
-
* 累积事件行直到遇到空行,支持 [DONE] 标记。
|
|
1330
|
-
* 返回已解析的事件和未处理的剩余 buffer(用于增量解析)。
|
|
1331
|
-
*
|
|
1332
|
-
* 关键行为:
|
|
1333
|
-
* - 只解析完整的 event(以空行结尾)
|
|
1334
|
-
* - 未完成的行保留在 rest 中,等待下次 chunk 补全
|
|
1335
|
-
* - 支持跨 chunk 的 event 分片
|
|
1336
|
-
*/
|
|
1337
|
-
function parseSSEEvents(chunk, options = {}) {
|
|
1338
|
-
const events = [];
|
|
1339
|
-
let eventType = "";
|
|
1340
|
-
let dataLines = [];
|
|
1341
|
-
let consumedUntil = 0;
|
|
1342
|
-
let cursor = 0;
|
|
1343
|
-
let malformedEvents = 0;
|
|
1344
|
-
const emitEvent = (consumedCursor) => {
|
|
1345
|
-
const dataStr = dataLines.join("\n");
|
|
1346
|
-
if (dataStr === "[DONE]") {
|
|
1347
|
-
eventType = "";
|
|
1348
|
-
dataLines = [];
|
|
1349
|
-
consumedUntil = consumedCursor;
|
|
1350
|
-
return;
|
|
1351
|
-
}
|
|
1352
|
-
try {
|
|
1353
|
-
const data = JSON.parse(dataStr);
|
|
1354
|
-
events.push({
|
|
1355
|
-
type: eventType,
|
|
1356
|
-
data
|
|
1357
|
-
});
|
|
1358
|
-
} catch {
|
|
1359
|
-
malformedEvents++;
|
|
1360
|
-
}
|
|
1361
|
-
eventType = "";
|
|
1362
|
-
dataLines = [];
|
|
1363
|
-
consumedUntil = consumedCursor;
|
|
1364
|
-
};
|
|
1365
|
-
const consumeLine = (line, consumedCursor) => {
|
|
1366
|
-
if (line.startsWith("event: ")) eventType = line.slice(7).trim();
|
|
1367
|
-
else if (line.startsWith("data: ")) dataLines.push(line.slice(6));
|
|
1368
|
-
else if (line === "" && eventType && dataLines.length > 0) emitEvent(consumedCursor);
|
|
1369
|
-
else if (line === "" && !eventType && dataLines.length === 0) consumedUntil = consumedCursor;
|
|
1370
|
-
};
|
|
1371
|
-
while (cursor < chunk.length) {
|
|
1372
|
-
const lineEnd = chunk.indexOf("\n", cursor);
|
|
1373
|
-
if (lineEnd === -1) break;
|
|
1374
|
-
let line = chunk.slice(cursor, lineEnd);
|
|
1375
|
-
cursor = lineEnd + 1;
|
|
1376
|
-
if (line.endsWith("\r")) line = line.slice(0, -1);
|
|
1377
|
-
consumeLine(line, cursor);
|
|
1378
|
-
}
|
|
1379
|
-
if (options.allowEOF && cursor < chunk.length) {
|
|
1380
|
-
let line = chunk.slice(cursor);
|
|
1381
|
-
if (line.endsWith("\r")) line = line.slice(0, -1);
|
|
1382
|
-
consumeLine(line, chunk.length);
|
|
1383
|
-
cursor = chunk.length;
|
|
1384
|
-
}
|
|
1385
|
-
if (options.allowEOF && eventType && dataLines.length > 0) emitEvent(chunk.length);
|
|
1386
|
-
return {
|
|
1387
|
-
events,
|
|
1388
|
-
rest: chunk.slice(consumedUntil),
|
|
1389
|
-
malformedEvents
|
|
1390
|
-
};
|
|
1391
|
-
}
|
|
1392
|
-
//#endregion
|
|
1393
1336
|
//#region src/helpers/synthetic-stream.ts
|
|
1394
1337
|
/**
|
|
1395
1338
|
* 模拟流式 (Synthetic Streaming)
|
|
@@ -1563,36 +1506,207 @@ function splitSSEFrames(buffer, allowEOF) {
|
|
|
1563
1506
|
rest: normalized.slice(cursor)
|
|
1564
1507
|
};
|
|
1565
1508
|
}
|
|
1509
|
+
/** 解析标准 SSE frame(event: + data:),用于 Messages / Responses。 */
|
|
1510
|
+
function parseSseJsonFrame(frame) {
|
|
1511
|
+
let eventType = "";
|
|
1512
|
+
let dataStr = "";
|
|
1513
|
+
for (const rawLine of frame.split("\n")) {
|
|
1514
|
+
const line = rawLine.trim();
|
|
1515
|
+
if (line.startsWith("event: ")) eventType = line.slice(7).trim();
|
|
1516
|
+
else if (line.startsWith("data: ")) dataStr += line.slice(6);
|
|
1517
|
+
}
|
|
1518
|
+
if (!eventType) return { status: "ignored" };
|
|
1519
|
+
try {
|
|
1520
|
+
const data = JSON.parse(dataStr);
|
|
1521
|
+
return {
|
|
1522
|
+
status: "parsed",
|
|
1523
|
+
value: {
|
|
1524
|
+
type: eventType,
|
|
1525
|
+
data
|
|
1526
|
+
}
|
|
1527
|
+
};
|
|
1528
|
+
} catch {
|
|
1529
|
+
return { status: "malformed" };
|
|
1530
|
+
}
|
|
1531
|
+
}
|
|
1532
|
+
function createSseJsonParser() {
|
|
1533
|
+
return new IncrementalStreamParser(splitSSEFrames, (frame) => parseSseJsonFrame(frame));
|
|
1534
|
+
}
|
|
1535
|
+
/** OpenAI Chat Completions 简化 SSE:仅 `data: ...` 行,忽略 `[DONE]`。 */
|
|
1536
|
+
function parseChatCompletionsDataLine(item) {
|
|
1537
|
+
const trimmed = item.trim();
|
|
1538
|
+
if (!trimmed.startsWith("data: ")) return { status: "ignored" };
|
|
1539
|
+
const data = trimmed.slice(6).trim();
|
|
1540
|
+
if (data === "[DONE]") return { status: "ignored" };
|
|
1541
|
+
try {
|
|
1542
|
+
return {
|
|
1543
|
+
status: "parsed",
|
|
1544
|
+
value: JSON.parse(data)
|
|
1545
|
+
};
|
|
1546
|
+
} catch {
|
|
1547
|
+
return { status: "malformed" };
|
|
1548
|
+
}
|
|
1549
|
+
}
|
|
1550
|
+
function createChatCompletionsSseParser() {
|
|
1551
|
+
return new IncrementalStreamParser(splitLines, (item) => parseChatCompletionsDataLine(item));
|
|
1552
|
+
}
|
|
1553
|
+
/** NDJSON 行解析(Ollama 等):空行忽略,JSON 失败为 malformed。 */
|
|
1554
|
+
function createNdjsonLineParser(isValid) {
|
|
1555
|
+
return new IncrementalStreamParser(splitLines, (item) => {
|
|
1556
|
+
const trimmed = item.trim();
|
|
1557
|
+
if (!trimmed) return { status: "ignored" };
|
|
1558
|
+
try {
|
|
1559
|
+
const parsed = JSON.parse(trimmed);
|
|
1560
|
+
if (isValid(parsed)) return {
|
|
1561
|
+
status: "parsed",
|
|
1562
|
+
value: parsed
|
|
1563
|
+
};
|
|
1564
|
+
return { status: "malformed" };
|
|
1565
|
+
} catch {
|
|
1566
|
+
return { status: "malformed" };
|
|
1567
|
+
}
|
|
1568
|
+
});
|
|
1569
|
+
}
|
|
1570
|
+
//#endregion
|
|
1571
|
+
//#region src/helpers/provider-stream.ts
|
|
1572
|
+
/**
|
|
1573
|
+
* Provider HTTP 流公共脚手架
|
|
1574
|
+
*
|
|
1575
|
+
* 收敛 adapter 间重复的:
|
|
1576
|
+
* - JSON POST + 错误映射
|
|
1577
|
+
* - ReadableStream reader 生命周期
|
|
1578
|
+
* - IncrementalStreamParser feed/flush + malformed warning
|
|
1579
|
+
* - 不完整尾帧 warning
|
|
1580
|
+
*/
|
|
1581
|
+
/** POST JSON 并返回可读 body reader + response headers;统一网络/HTTP/空 body 错误。 */
|
|
1582
|
+
async function openProviderJsonStream(options) {
|
|
1583
|
+
const { fetchFn, url, headers, body, signal } = options;
|
|
1584
|
+
let response;
|
|
1585
|
+
try {
|
|
1586
|
+
response = await fetchFn(url, {
|
|
1587
|
+
method: "POST",
|
|
1588
|
+
headers,
|
|
1589
|
+
body: JSON.stringify(body),
|
|
1590
|
+
signal
|
|
1591
|
+
});
|
|
1592
|
+
} catch (err) {
|
|
1593
|
+
throw new AIProviderError(err instanceof Error ? err.message : String(err), "PROVIDER_ERROR");
|
|
1594
|
+
}
|
|
1595
|
+
if (!response.ok) {
|
|
1596
|
+
const errorBody = await response.text().catch(() => "");
|
|
1597
|
+
throw providerHttpError(response.status, errorBody);
|
|
1598
|
+
}
|
|
1599
|
+
const bodyStream = response.body;
|
|
1600
|
+
if (!bodyStream) throw new AIStreamError("Response body is not readable", "STREAM_ERROR");
|
|
1601
|
+
return {
|
|
1602
|
+
reader: bodyStream.getReader(),
|
|
1603
|
+
headers: response.headers
|
|
1604
|
+
};
|
|
1605
|
+
}
|
|
1606
|
+
/**
|
|
1607
|
+
* 读取并解析 provider 流。
|
|
1608
|
+
* 每个 batch 携带本轮解析出的 items 与(可选)malformed / incomplete warning。
|
|
1609
|
+
* 调用方应 `for await` 消费完毕;reader 在迭代结束时 cancel/release。
|
|
1610
|
+
*/
|
|
1611
|
+
async function* iterateProviderStreamBatches(options) {
|
|
1612
|
+
const { reader, parser, factory, providerLabel, transportLabel, incompleteMessage } = options;
|
|
1613
|
+
let streamDone = false;
|
|
1614
|
+
try {
|
|
1615
|
+
while (true) {
|
|
1616
|
+
const { done, value } = await reader.read().catch((err) => {
|
|
1617
|
+
throw new AIStreamError(`Failed to read response stream: ${err instanceof Error ? err.message : String(err)}`, "STREAM_ERROR");
|
|
1618
|
+
});
|
|
1619
|
+
const { items, malformed } = done ? parser.flush() : parser.feed(value);
|
|
1620
|
+
const warnings = [];
|
|
1621
|
+
const malformedWarning = emitMalformedStreamWarning(factory, {
|
|
1622
|
+
count: malformed,
|
|
1623
|
+
providerLabel,
|
|
1624
|
+
transportLabel
|
|
1625
|
+
});
|
|
1626
|
+
if (malformedWarning) warnings.push(malformedWarning);
|
|
1627
|
+
yield {
|
|
1628
|
+
items,
|
|
1629
|
+
warnings
|
|
1630
|
+
};
|
|
1631
|
+
if (done) {
|
|
1632
|
+
streamDone = true;
|
|
1633
|
+
break;
|
|
1634
|
+
}
|
|
1635
|
+
}
|
|
1636
|
+
} finally {
|
|
1637
|
+
try {
|
|
1638
|
+
if (!streamDone) await reader.cancel().catch(() => void 0);
|
|
1639
|
+
} finally {
|
|
1640
|
+
reader.releaseLock();
|
|
1641
|
+
}
|
|
1642
|
+
}
|
|
1643
|
+
if (parser.getRemaining().trim().length > 0) yield {
|
|
1644
|
+
items: [],
|
|
1645
|
+
warnings: [factory.responseWarning(incompleteMessage, "STREAM_ERROR")]
|
|
1646
|
+
};
|
|
1647
|
+
}
|
|
1648
|
+
/** 一次性 complete 守卫:首次成功,后续返回 false。 */
|
|
1649
|
+
function createCompletionGate() {
|
|
1650
|
+
let completed = false;
|
|
1651
|
+
return {
|
|
1652
|
+
get completed() {
|
|
1653
|
+
return completed;
|
|
1654
|
+
},
|
|
1655
|
+
tryComplete() {
|
|
1656
|
+
if (completed) return false;
|
|
1657
|
+
completed = true;
|
|
1658
|
+
return true;
|
|
1659
|
+
}
|
|
1660
|
+
};
|
|
1661
|
+
}
|
|
1566
1662
|
//#endregion
|
|
1567
1663
|
//#region src/helpers/request-mapper.ts
|
|
1568
1664
|
var NormalizedRequestMapper = class {
|
|
1569
|
-
|
|
1570
|
-
constructor(
|
|
1571
|
-
this.
|
|
1665
|
+
kind;
|
|
1666
|
+
constructor(kind) {
|
|
1667
|
+
this.kind = kind;
|
|
1572
1668
|
}
|
|
1573
1669
|
mapInstructions(instructions) {
|
|
1574
1670
|
return typeof instructions === "string" ? instructions : contentBlocksToText(this.ensureTextBlocks(instructions, "instructions"));
|
|
1575
1671
|
}
|
|
1576
1672
|
ensureTextBlocks(blocks, field) {
|
|
1577
|
-
return this.ensureBlocks(blocks, field,
|
|
1673
|
+
return this.ensureBlocks(blocks, field, ["text", "json"], "only text/json blocks are supported");
|
|
1578
1674
|
}
|
|
1579
1675
|
ensureReasoningBlocks(blocks, field) {
|
|
1580
|
-
return this.ensureBlocks(blocks, field,
|
|
1676
|
+
return this.ensureBlocks(blocks, field, ["text"], "reasoning only supports text blocks");
|
|
1677
|
+
}
|
|
1678
|
+
/** ensureTextBlocks + contentBlocksToText 的常见组合。 */
|
|
1679
|
+
textFromBlocks(blocks, field) {
|
|
1680
|
+
return contentBlocksToText(this.ensureTextBlocks(blocks, field));
|
|
1581
1681
|
}
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
throw new AIRequestError(`${this.
|
|
1682
|
+
parseToolArguments(item) {
|
|
1683
|
+
try {
|
|
1684
|
+
const parsed = JSON.parse(item.argumentsText);
|
|
1685
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) return parsed;
|
|
1686
|
+
} catch {}
|
|
1687
|
+
throw new AIRequestError(`${this.kind} requires tool_call argumentsText to be a valid JSON object`, "TOOL_CALL_ARGUMENTS_INVALID");
|
|
1588
1688
|
}
|
|
1589
1689
|
rollbackTrailingAssistantMessages(messages) {
|
|
1590
1690
|
while (messages.length > 0 && messages[messages.length - 1]?.role === "assistant") messages.pop();
|
|
1591
1691
|
}
|
|
1692
|
+
mapToolsIfPresent(tools, map) {
|
|
1693
|
+
if (!tools || tools.length === 0) return void 0;
|
|
1694
|
+
return tools.map(map);
|
|
1695
|
+
}
|
|
1696
|
+
/**
|
|
1697
|
+
* 将 canonical toolChoice 映射为 provider 形状。
|
|
1698
|
+
* 返回 undefined 表示调用方无需写入 body 字段。
|
|
1699
|
+
*/
|
|
1700
|
+
mapToolChoice(toolChoice, mappers) {
|
|
1701
|
+
if (!toolChoice) return void 0;
|
|
1702
|
+
if (toolChoice === "auto") return mappers.auto;
|
|
1703
|
+
if (toolChoice === "none") return mappers.none;
|
|
1704
|
+
if (toolChoice.type === "tool") return mappers.tool(toolChoice.name);
|
|
1705
|
+
}
|
|
1592
1706
|
ensureBlocks(blocks, field, supportedTypes, description) {
|
|
1593
1707
|
for (let i = 0; i < blocks.length; i++) {
|
|
1594
1708
|
const block = blocks[i];
|
|
1595
|
-
if (block && !supportedTypes.includes(block.type)) throw new AIRequestError(`${this.
|
|
1709
|
+
if (block && !supportedTypes.includes(block.type)) throw new AIRequestError(`${this.kind} does not support ${field}[${i}] of type "${block.type}"; ${description}`, "UNSUPPORTED_CONTENT_BLOCK");
|
|
1596
1710
|
}
|
|
1597
1711
|
return blocks;
|
|
1598
1712
|
}
|
|
@@ -1609,21 +1723,7 @@ var NormalizedRequestMapper = class {
|
|
|
1609
1723
|
*
|
|
1610
1724
|
* 支持消息流 / reasoning 流 / tool_call 流及高保真 replay。
|
|
1611
1725
|
*/
|
|
1612
|
-
const
|
|
1613
|
-
kind: "responses",
|
|
1614
|
-
instructionsMode: "instructions_field",
|
|
1615
|
-
supportedBlockTypes: ["text", "json"],
|
|
1616
|
-
reasoningBlockTypes: ["text"],
|
|
1617
|
-
capabilities: {
|
|
1618
|
-
textStreaming: "native",
|
|
1619
|
-
reasoningStreaming: "native",
|
|
1620
|
-
toolCallStreaming: "native",
|
|
1621
|
-
replay: "opaque",
|
|
1622
|
-
usage: "final",
|
|
1623
|
-
toolResultOutcomes: ["success"]
|
|
1624
|
-
}
|
|
1625
|
-
};
|
|
1626
|
-
const mapper$3 = new NormalizedRequestMapper(profile$3);
|
|
1726
|
+
const mapper$3 = new NormalizedRequestMapper("responses");
|
|
1627
1727
|
/** 已处理或可安全忽略的 Responses SSE 类型(未知类型会 warning 一次)。 */
|
|
1628
1728
|
const KNOWN_RESPONSES_SSE_TYPES = /* @__PURE__ */ new Set([
|
|
1629
1729
|
"response.output_item.added",
|
|
@@ -1632,8 +1732,6 @@ const KNOWN_RESPONSES_SSE_TYPES = /* @__PURE__ */ new Set([
|
|
|
1632
1732
|
"response.output_text.done",
|
|
1633
1733
|
"response.reasoning.delta",
|
|
1634
1734
|
"response.reasoning.done",
|
|
1635
|
-
"response.tool_call.delta",
|
|
1636
|
-
"response.tool_call.done",
|
|
1637
1735
|
"response.function_call_arguments.delta",
|
|
1638
1736
|
"response.function_call_arguments.done",
|
|
1639
1737
|
"response.content_part.added",
|
|
@@ -1669,7 +1767,7 @@ function canonicalToResponsesBlock(b) {
|
|
|
1669
1767
|
}
|
|
1670
1768
|
var ResponsesAdapter = class extends AdapterBase {
|
|
1671
1769
|
kind = "responses";
|
|
1672
|
-
|
|
1770
|
+
isSyntheticStream = false;
|
|
1673
1771
|
apiKey;
|
|
1674
1772
|
baseUrl;
|
|
1675
1773
|
fetchFn;
|
|
@@ -1693,7 +1791,7 @@ var ResponsesAdapter = class extends AdapterBase {
|
|
|
1693
1791
|
} else input.push({
|
|
1694
1792
|
type: "message",
|
|
1695
1793
|
role: item.role,
|
|
1696
|
-
content:
|
|
1794
|
+
content: mapper$3.textFromBlocks(item.content, `input message (${item.role}) content`)
|
|
1697
1795
|
});
|
|
1698
1796
|
break;
|
|
1699
1797
|
case "reasoning": {
|
|
@@ -1716,8 +1814,7 @@ var ResponsesAdapter = class extends AdapterBase {
|
|
|
1716
1814
|
});
|
|
1717
1815
|
break;
|
|
1718
1816
|
case "tool_result": {
|
|
1719
|
-
mapper$3.
|
|
1720
|
-
const output = mapper$3.ensureTextBlocks(item.content, `tool_result ${item.callId} content`).map(blockToText).join("\n");
|
|
1817
|
+
const output = mapper$3.textFromBlocks(item.content, `tool_result ${item.callId} content`);
|
|
1721
1818
|
input.push({
|
|
1722
1819
|
type: "function_call_output",
|
|
1723
1820
|
call_id: item.callId,
|
|
@@ -1745,20 +1842,20 @@ var ResponsesAdapter = class extends AdapterBase {
|
|
|
1745
1842
|
stream: true
|
|
1746
1843
|
};
|
|
1747
1844
|
if (request.instructions) body.instructions = mapper$3.mapInstructions(request.instructions);
|
|
1748
|
-
|
|
1845
|
+
body.tools = mapper$3.mapToolsIfPresent(request.tools, (t) => ({
|
|
1749
1846
|
type: "function",
|
|
1750
1847
|
name: t.name,
|
|
1751
1848
|
description: t.description,
|
|
1752
1849
|
input_schema: t.inputSchema
|
|
1753
1850
|
}));
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1851
|
+
body.tool_choice = mapper$3.mapToolChoice(request.toolChoice, {
|
|
1852
|
+
auto: "auto",
|
|
1853
|
+
none: "none",
|
|
1854
|
+
tool: (name) => ({
|
|
1758
1855
|
type: "function",
|
|
1759
|
-
name
|
|
1760
|
-
}
|
|
1761
|
-
}
|
|
1856
|
+
name
|
|
1857
|
+
})
|
|
1858
|
+
});
|
|
1762
1859
|
if (request.temperature !== void 0) body.temperature = request.temperature;
|
|
1763
1860
|
if (request.maxOutputTokens !== void 0) body.max_output_tokens = request.maxOutputTokens;
|
|
1764
1861
|
if (request.metadata) body.metadata = request.metadata;
|
|
@@ -1766,151 +1863,108 @@ var ResponsesAdapter = class extends AdapterBase {
|
|
|
1766
1863
|
}
|
|
1767
1864
|
async *runStream(providerRequest, factory, request) {
|
|
1768
1865
|
const auxiliary = this.createAuxiliaryState(request);
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
});
|
|
1780
|
-
} catch (err) {
|
|
1781
|
-
throw new AIProviderError(err instanceof Error ? err.message : String(err), "PROVIDER_ERROR");
|
|
1782
|
-
}
|
|
1783
|
-
if (!response.ok) {
|
|
1784
|
-
const errorBody = await response.text().catch(() => "");
|
|
1785
|
-
throw providerHttpError(response.status, errorBody);
|
|
1786
|
-
}
|
|
1787
|
-
const reader = response.body?.getReader();
|
|
1788
|
-
if (!reader) throw new AIStreamError("Response body is not readable", "STREAM_ERROR");
|
|
1789
|
-
const parser = new IncrementalStreamParser(splitSSEFrames, (frame) => {
|
|
1790
|
-
let eventType = "";
|
|
1791
|
-
let dataStr = "";
|
|
1792
|
-
for (const rawLine of frame.split("\n")) {
|
|
1793
|
-
const line = rawLine.trim();
|
|
1794
|
-
if (line.startsWith("event: ")) eventType = line.slice(7).trim();
|
|
1795
|
-
else if (line.startsWith("data: ")) dataStr += line.slice(6);
|
|
1796
|
-
}
|
|
1797
|
-
if (!eventType) return { status: "ignored" };
|
|
1798
|
-
try {
|
|
1799
|
-
const data = JSON.parse(dataStr);
|
|
1800
|
-
return {
|
|
1801
|
-
status: "parsed",
|
|
1802
|
-
value: {
|
|
1803
|
-
type: eventType,
|
|
1804
|
-
data
|
|
1805
|
-
}
|
|
1806
|
-
};
|
|
1807
|
-
} catch {
|
|
1808
|
-
return { status: "malformed" };
|
|
1809
|
-
}
|
|
1866
|
+
const gate = createCompletionGate();
|
|
1867
|
+
const { reader } = await openProviderJsonStream({
|
|
1868
|
+
fetchFn: this.fetchFn,
|
|
1869
|
+
url: `${this.baseUrl}/responses`,
|
|
1870
|
+
headers: {
|
|
1871
|
+
"Content-Type": "application/json",
|
|
1872
|
+
Authorization: `Bearer ${this.apiKey}`
|
|
1873
|
+
},
|
|
1874
|
+
body: providerRequest,
|
|
1875
|
+
signal: request.signal
|
|
1810
1876
|
});
|
|
1877
|
+
const parser = createSseJsonParser();
|
|
1811
1878
|
const output = [];
|
|
1812
|
-
let streamDone = false;
|
|
1813
1879
|
let completedResponse;
|
|
1814
|
-
let completedEmitted = false;
|
|
1815
1880
|
let unknownEventsWarned = false;
|
|
1816
1881
|
const messageItemsWithDelta = /* @__PURE__ */ new Set();
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
if (
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1882
|
+
const toolCallNames = /* @__PURE__ */ new Map();
|
|
1883
|
+
for await (const batch of iterateProviderStreamBatches({
|
|
1884
|
+
reader,
|
|
1885
|
+
parser,
|
|
1886
|
+
factory,
|
|
1887
|
+
providerLabel: "Responses",
|
|
1888
|
+
transportLabel: "SSE event(s)",
|
|
1889
|
+
incompleteMessage: "Stream ended with an incomplete Responses SSE frame"
|
|
1890
|
+
})) {
|
|
1891
|
+
for (const warning of batch.warnings) yield warning;
|
|
1892
|
+
for (const sseEvent of batch.items) {
|
|
1893
|
+
if (sseEvent.type === "error") {
|
|
1894
|
+
const data = sseEvent.data;
|
|
1895
|
+
yield factory.responseWarning(data.message ?? "Provider error event", data.code);
|
|
1896
|
+
continue;
|
|
1897
|
+
}
|
|
1898
|
+
if (sseEvent.type === "response.output_item.added") {
|
|
1899
|
+
const item = sseEvent.data.item;
|
|
1900
|
+
switch (item.type) {
|
|
1901
|
+
case "message":
|
|
1902
|
+
yield factory.messageStarted(item.id);
|
|
1903
|
+
break;
|
|
1904
|
+
case "reasoning":
|
|
1905
|
+
yield factory.reasoningStarted(item.id, "full");
|
|
1906
|
+
break;
|
|
1907
|
+
case "function_call": {
|
|
1908
|
+
const name = typeof item.name === "string" ? item.name : "unknown";
|
|
1909
|
+
toolCallNames.set(item.id, name);
|
|
1910
|
+
yield factory.toolCallStarted(item.id, name);
|
|
1911
|
+
break;
|
|
1847
1912
|
}
|
|
1848
|
-
continue;
|
|
1849
|
-
}
|
|
1850
|
-
if (sseEvent.type === "response.output_text.delta") {
|
|
1851
|
-
const data = sseEvent.data;
|
|
1852
|
-
yield factory.messageDelta(data.item_id, textBlock(data.delta));
|
|
1853
|
-
messageItemsWithDelta.add(data.item_id);
|
|
1854
|
-
continue;
|
|
1855
|
-
}
|
|
1856
|
-
if (sseEvent.type === "response.output_text.done") {
|
|
1857
|
-
const data = sseEvent.data;
|
|
1858
|
-
if (!messageItemsWithDelta.has(data.item_id) && data.text) yield factory.messageDelta(data.item_id, textBlock(data.text));
|
|
1859
|
-
yield factory.messageCompleted(data.item_id);
|
|
1860
|
-
output.push(messageItem([textBlock(data.text)], { id: data.item_id }));
|
|
1861
|
-
continue;
|
|
1862
1913
|
}
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1914
|
+
continue;
|
|
1915
|
+
}
|
|
1916
|
+
if (sseEvent.type === "response.output_text.delta") {
|
|
1917
|
+
const data = sseEvent.data;
|
|
1918
|
+
yield factory.messageDelta(data.item_id, textBlock(data.delta));
|
|
1919
|
+
messageItemsWithDelta.add(data.item_id);
|
|
1920
|
+
continue;
|
|
1921
|
+
}
|
|
1922
|
+
if (sseEvent.type === "response.output_text.done") {
|
|
1923
|
+
const data = sseEvent.data;
|
|
1924
|
+
if (!messageItemsWithDelta.has(data.item_id) && data.text) yield factory.messageDelta(data.item_id, textBlock(data.text));
|
|
1925
|
+
yield factory.messageCompleted(data.item_id);
|
|
1926
|
+
output.push(messageItem([textBlock(data.text)], { id: data.item_id }));
|
|
1927
|
+
continue;
|
|
1928
|
+
}
|
|
1929
|
+
if (sseEvent.type === "response.reasoning.delta") {
|
|
1930
|
+
const data = sseEvent.data;
|
|
1931
|
+
yield factory.reasoningDelta(data.item_id, textBlock(data.delta));
|
|
1932
|
+
continue;
|
|
1933
|
+
}
|
|
1934
|
+
if (sseEvent.type === "response.reasoning.done") {
|
|
1935
|
+
const data = sseEvent.data;
|
|
1936
|
+
yield factory.reasoningCompleted(data.item_id);
|
|
1937
|
+
output.push(reasoningItem([textBlock(data.text)], "full", data.item_id));
|
|
1938
|
+
continue;
|
|
1939
|
+
}
|
|
1940
|
+
if (sseEvent.type === "response.function_call_arguments.delta") {
|
|
1941
|
+
const data = sseEvent.data;
|
|
1942
|
+
if (data.delta) yield factory.toolCallDelta(data.item_id, { argumentsText: data.delta });
|
|
1943
|
+
continue;
|
|
1944
|
+
}
|
|
1945
|
+
if (sseEvent.type === "response.function_call_arguments.done") {
|
|
1946
|
+
const data = sseEvent.data;
|
|
1947
|
+
const tcItem = toolCallItem(data.item_id, toolCallNames.get(data.item_id) ?? "unknown", data.arguments);
|
|
1948
|
+
yield factory.toolCallCompleted(data.item_id);
|
|
1949
|
+
output.push(tcItem);
|
|
1950
|
+
continue;
|
|
1951
|
+
}
|
|
1952
|
+
if (sseEvent.type === "response.completed" || sseEvent.type === "response.failed" || sseEvent.type === "response.incomplete") {
|
|
1953
|
+
const data = sseEvent.data;
|
|
1954
|
+
if (completedResponse) {
|
|
1955
|
+
yield factory.responseWarning("Duplicate finish signal ignored", "DUPLICATE_FINISH");
|
|
1894
1956
|
continue;
|
|
1895
1957
|
}
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
}
|
|
1958
|
+
completedResponse = data.response;
|
|
1959
|
+
if (sseEvent.type === "response.failed") yield factory.responseWarning(`Response failed: ${extractFailureMessage(data.response)}`, "PROVIDER_FAILURE");
|
|
1960
|
+
continue;
|
|
1900
1961
|
}
|
|
1901
|
-
if (
|
|
1902
|
-
|
|
1903
|
-
|
|
1962
|
+
if (!KNOWN_RESPONSES_SSE_TYPES.has(sseEvent.type) && !unknownEventsWarned) {
|
|
1963
|
+
unknownEventsWarned = true;
|
|
1964
|
+
yield factory.responseWarning(`Responses API sent unknown event type "${sseEvent.type}"; this may indicate an incomplete integration`, "UNKNOWN_PROVIDER_EVENT");
|
|
1904
1965
|
}
|
|
1905
1966
|
}
|
|
1906
|
-
} finally {
|
|
1907
|
-
try {
|
|
1908
|
-
if (!streamDone) await reader.cancel().catch(() => void 0);
|
|
1909
|
-
} finally {
|
|
1910
|
-
reader.releaseLock();
|
|
1911
|
-
}
|
|
1912
1967
|
}
|
|
1913
|
-
if (parser.getRemaining().trim().length > 0) yield factory.responseWarning("Stream ended with an incomplete Responses SSE frame", "STREAM_ERROR");
|
|
1914
1968
|
let rawResponseId;
|
|
1915
1969
|
if (completedResponse) {
|
|
1916
1970
|
rawResponseId = completedResponse.id;
|
|
@@ -1919,31 +1973,12 @@ var ResponsesAdapter = class extends AdapterBase {
|
|
|
1919
1973
|
const replay = [...replayFromOutput(output)];
|
|
1920
1974
|
if (completedResponse?.id) replay.push(opaqueItem("responses", "replay", { id: completedResponse.id }));
|
|
1921
1975
|
const stopReason = completedResponse ? this.inferStopReason(completedResponse) : void 0;
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
replay,
|
|
1929
|
-
stopReason,
|
|
1930
|
-
usage: auxiliaryResult.usage,
|
|
1931
|
-
billing: auxiliaryResult.billing,
|
|
1932
|
-
auxiliary: auxiliaryResult.auxiliary,
|
|
1933
|
-
warnings: auxiliaryResult.warnings,
|
|
1934
|
-
metadataSources: auxiliaryResult.metadataSources,
|
|
1935
|
-
rawResponseId
|
|
1936
|
-
}, factory);
|
|
1937
|
-
yield factory.responseCompleted({
|
|
1938
|
-
replay: finalResponse.replay,
|
|
1939
|
-
stopReason: finalResponse.stopReason,
|
|
1940
|
-
trace: finalResponse.backend,
|
|
1941
|
-
usage: finalResponse.usage,
|
|
1942
|
-
billing: finalResponse.billing,
|
|
1943
|
-
auxiliary: finalResponse.auxiliary,
|
|
1944
|
-
warnings: finalResponse.warnings
|
|
1945
|
-
});
|
|
1946
|
-
}
|
|
1976
|
+
if (gate.tryComplete()) yield* this.emitStreamCompleted(factory, request, auxiliary, {
|
|
1977
|
+
output,
|
|
1978
|
+
replay,
|
|
1979
|
+
stopReason,
|
|
1980
|
+
rawResponseId
|
|
1981
|
+
});
|
|
1947
1982
|
}
|
|
1948
1983
|
inferStopReason(response) {
|
|
1949
1984
|
if (response.status === "failed") return "error";
|
|
@@ -1975,21 +2010,7 @@ var ResponsesAdapter = class extends AdapterBase {
|
|
|
1975
2010
|
* - 高保真 replay(含 opaque continuation)
|
|
1976
2011
|
* - 能力降级 warning
|
|
1977
2012
|
*/
|
|
1978
|
-
const
|
|
1979
|
-
kind: "messages",
|
|
1980
|
-
instructionsMode: "system_message",
|
|
1981
|
-
supportedBlockTypes: ["text", "json"],
|
|
1982
|
-
reasoningBlockTypes: ["text"],
|
|
1983
|
-
capabilities: {
|
|
1984
|
-
textStreaming: "native",
|
|
1985
|
-
reasoningStreaming: "native",
|
|
1986
|
-
toolCallStreaming: "synthetic",
|
|
1987
|
-
replay: "opaque",
|
|
1988
|
-
usage: "stream",
|
|
1989
|
-
toolResultOutcomes: ["success", "error"]
|
|
1990
|
-
}
|
|
1991
|
-
};
|
|
1992
|
-
const mapper$2 = new NormalizedRequestMapper(profile$2);
|
|
2013
|
+
const mapper$2 = new NormalizedRequestMapper("messages");
|
|
1993
2014
|
function isMessagesReplayContentBlock(value) {
|
|
1994
2015
|
if (!value || typeof value !== "object" || !("type" in value)) return false;
|
|
1995
2016
|
const block = value;
|
|
@@ -2015,10 +2036,10 @@ function assertMessagesReplayContent(content) {
|
|
|
2015
2036
|
function synthesizeItemId(kind, blockIndex, responseId) {
|
|
2016
2037
|
return `${kind}-${blockIndex}-${responseId}`;
|
|
2017
2038
|
}
|
|
2018
|
-
function
|
|
2039
|
+
function parseProviderToolUseInput(input) {
|
|
2019
2040
|
try {
|
|
2020
2041
|
const parsed = JSON.parse(input);
|
|
2021
|
-
return parsed && typeof parsed === "object" ? parsed : {};
|
|
2042
|
+
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
|
|
2022
2043
|
} catch {
|
|
2023
2044
|
return {};
|
|
2024
2045
|
}
|
|
@@ -2059,7 +2080,7 @@ function buildStreamMetadata(options) {
|
|
|
2059
2080
|
}
|
|
2060
2081
|
var MessagesAdapter = class extends AdapterBase {
|
|
2061
2082
|
kind = "messages";
|
|
2062
|
-
|
|
2083
|
+
isSyntheticStream = false;
|
|
2063
2084
|
apiKey;
|
|
2064
2085
|
apiVersion;
|
|
2065
2086
|
baseUrl;
|
|
@@ -2098,7 +2119,7 @@ var MessagesAdapter = class extends AdapterBase {
|
|
|
2098
2119
|
type: "tool_use",
|
|
2099
2120
|
id: item.id,
|
|
2100
2121
|
name: item.name,
|
|
2101
|
-
input:
|
|
2122
|
+
input: mapper$2.parseToolArguments(item)
|
|
2102
2123
|
};
|
|
2103
2124
|
if (lastMsg && lastMsg.role === "assistant" && typeof lastMsg.content !== "string") lastMsg.content.push(toolBlock);
|
|
2104
2125
|
else messages.push({
|
|
@@ -2108,13 +2129,12 @@ var MessagesAdapter = class extends AdapterBase {
|
|
|
2108
2129
|
break;
|
|
2109
2130
|
}
|
|
2110
2131
|
case "tool_result": {
|
|
2111
|
-
mapper$2.
|
|
2112
|
-
const content = mapper$2.ensureTextBlocks(item.content, `tool_result ${item.callId} content`).map(blockToText).join("\n");
|
|
2132
|
+
const content = mapper$2.textFromBlocks(item.content, `tool_result ${item.callId} content`);
|
|
2113
2133
|
const block = {
|
|
2114
2134
|
type: "tool_result",
|
|
2115
2135
|
tool_use_id: item.callId,
|
|
2116
2136
|
content,
|
|
2117
|
-
is_error: item.outcome
|
|
2137
|
+
is_error: item.outcome !== "success"
|
|
2118
2138
|
};
|
|
2119
2139
|
if (pendingToolResultMessage && typeof pendingToolResultMessage.content !== "string") pendingToolResultMessage.content.push(block);
|
|
2120
2140
|
else {
|
|
@@ -2140,7 +2160,7 @@ var MessagesAdapter = class extends AdapterBase {
|
|
|
2140
2160
|
break;
|
|
2141
2161
|
}
|
|
2142
2162
|
case "opaque": {
|
|
2143
|
-
if (item.purpose !== "replay") break;
|
|
2163
|
+
if (item.source !== "messages" || item.purpose !== "replay") break;
|
|
2144
2164
|
assertOpaqueReplayEnvelope(item.payload);
|
|
2145
2165
|
const payload = item.payload;
|
|
2146
2166
|
if (payload.role === "assistant" && "content" in payload) {
|
|
@@ -2162,71 +2182,39 @@ var MessagesAdapter = class extends AdapterBase {
|
|
|
2162
2182
|
stream: true
|
|
2163
2183
|
};
|
|
2164
2184
|
if (systemPrompt) body.system = systemPrompt;
|
|
2165
|
-
|
|
2185
|
+
body.tools = mapper$2.mapToolsIfPresent(request.tools, (t) => ({
|
|
2166
2186
|
name: t.name,
|
|
2167
2187
|
description: t.description,
|
|
2168
2188
|
input_schema: t.inputSchema
|
|
2169
2189
|
}));
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2190
|
+
body.tool_choice = mapper$2.mapToolChoice(request.toolChoice, {
|
|
2191
|
+
auto: { type: "auto" },
|
|
2192
|
+
none: { type: "none" },
|
|
2193
|
+
tool: (name) => ({
|
|
2174
2194
|
type: "tool",
|
|
2175
|
-
name
|
|
2176
|
-
}
|
|
2177
|
-
}
|
|
2195
|
+
name
|
|
2196
|
+
})
|
|
2197
|
+
});
|
|
2178
2198
|
if (request.temperature !== void 0) body.temperature = request.temperature;
|
|
2179
2199
|
return body;
|
|
2180
2200
|
}
|
|
2181
2201
|
async *runStream(providerRequest, factory, request) {
|
|
2182
2202
|
const auxiliary = this.createAuxiliaryState(request);
|
|
2183
|
-
|
|
2203
|
+
const gate = createCompletionGate();
|
|
2184
2204
|
if (request.metadata) yield factory.responseWarning("Request metadata is not supported by the Messages adapter", "UNSUPPORTED_METADATA");
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
signal: request.signal
|
|
2196
|
-
});
|
|
2197
|
-
} catch (err) {
|
|
2198
|
-
throw new AIProviderError(err instanceof Error ? err.message : String(err), "PROVIDER_ERROR");
|
|
2199
|
-
}
|
|
2200
|
-
if (!response.ok) {
|
|
2201
|
-
const errorBody = await response.text().catch(() => "");
|
|
2202
|
-
throw providerHttpError(response.status, errorBody);
|
|
2203
|
-
}
|
|
2204
|
-
const reader = response.body?.getReader();
|
|
2205
|
-
if (!reader) throw new AIStreamError("Response body is not readable", "STREAM_ERROR");
|
|
2206
|
-
const parser = new IncrementalStreamParser(splitSSEFrames, (frame) => {
|
|
2207
|
-
let eventType = "";
|
|
2208
|
-
let dataStr = "";
|
|
2209
|
-
for (const rawLine of frame.split("\n")) {
|
|
2210
|
-
const line = rawLine.trim();
|
|
2211
|
-
if (line.startsWith("event: ")) eventType = line.slice(7).trim();
|
|
2212
|
-
else if (line.startsWith("data: ")) dataStr += line.slice(6);
|
|
2213
|
-
}
|
|
2214
|
-
if (!eventType) return { status: "ignored" };
|
|
2215
|
-
try {
|
|
2216
|
-
const data = JSON.parse(dataStr);
|
|
2217
|
-
return {
|
|
2218
|
-
status: "parsed",
|
|
2219
|
-
value: {
|
|
2220
|
-
type: eventType,
|
|
2221
|
-
data
|
|
2222
|
-
}
|
|
2223
|
-
};
|
|
2224
|
-
} catch {
|
|
2225
|
-
return { status: "malformed" };
|
|
2226
|
-
}
|
|
2205
|
+
const { reader, headers } = await openProviderJsonStream({
|
|
2206
|
+
fetchFn: this.fetchFn,
|
|
2207
|
+
url: `${this.baseUrl}/messages`,
|
|
2208
|
+
headers: {
|
|
2209
|
+
"Content-Type": "application/json",
|
|
2210
|
+
"x-api-key": this.apiKey,
|
|
2211
|
+
"anthropic-version": this.apiVersion
|
|
2212
|
+
},
|
|
2213
|
+
body: providerRequest,
|
|
2214
|
+
signal: request.signal
|
|
2227
2215
|
});
|
|
2216
|
+
const parser = createSseJsonParser();
|
|
2228
2217
|
const output = [];
|
|
2229
|
-
let streamDone = false;
|
|
2230
2218
|
let messageResponse;
|
|
2231
2219
|
let currentContentBlockIndex = -1;
|
|
2232
2220
|
let currentItemType = null;
|
|
@@ -2234,7 +2222,6 @@ var MessagesAdapter = class extends AdapterBase {
|
|
|
2234
2222
|
let currentToolName = "";
|
|
2235
2223
|
let currentArgsText = "";
|
|
2236
2224
|
let currentThinkingVisibility = "full";
|
|
2237
|
-
let hasStreamedReasoning = false;
|
|
2238
2225
|
const rawReplayContent = [];
|
|
2239
2226
|
let textBuffer = "";
|
|
2240
2227
|
let thinkingBuffer = "";
|
|
@@ -2243,160 +2230,142 @@ var MessagesAdapter = class extends AdapterBase {
|
|
|
2243
2230
|
let stopSequence;
|
|
2244
2231
|
let rawResponseId = "";
|
|
2245
2232
|
if (request.include?.providerMetadata !== "off") {
|
|
2246
|
-
const headerMetadata = pickProviderHeaders(
|
|
2233
|
+
const headerMetadata = pickProviderHeaders(headers);
|
|
2247
2234
|
auxiliary.recordProviderMetadata("header", Object.keys(headerMetadata).length > 0 ? { headers: headerMetadata } : void 0);
|
|
2248
2235
|
}
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
currentItemId = synthesizeItemId("reason-redacted", currentContentBlockIndex, rawResponseId);
|
|
2295
|
-
currentThinkingVisibility = "redacted";
|
|
2296
|
-
const data = block.data;
|
|
2297
|
-
yield factory.reasoningStarted(currentItemId, "redacted");
|
|
2298
|
-
yield factory.reasoningDelta(currentItemId, textBlock(data));
|
|
2299
|
-
const redactedItem = reasoningItem([textBlock(data)], "redacted", currentItemId);
|
|
2300
|
-
yield factory.reasoningCompleted(currentItemId);
|
|
2301
|
-
output.push(redactedItem);
|
|
2302
|
-
rawReplayContent.push({
|
|
2303
|
-
type: "redacted_thinking",
|
|
2304
|
-
data
|
|
2305
|
-
});
|
|
2306
|
-
currentItemType = null;
|
|
2307
|
-
break;
|
|
2308
|
-
}
|
|
2309
|
-
case "tool_use": {
|
|
2310
|
-
const tuBlock = block;
|
|
2311
|
-
currentItemType = "tool_call";
|
|
2312
|
-
currentItemId = tuBlock.id;
|
|
2313
|
-
currentToolName = tuBlock.name;
|
|
2314
|
-
currentArgsText = "";
|
|
2315
|
-
argsBuffer = "";
|
|
2316
|
-
yield factory.toolCallStarted(currentItemId, currentToolName);
|
|
2317
|
-
break;
|
|
2318
|
-
}
|
|
2319
|
-
}
|
|
2320
|
-
continue;
|
|
2321
|
-
}
|
|
2322
|
-
case "content_block_delta": {
|
|
2323
|
-
const delta = sseEvent.data.delta;
|
|
2324
|
-
switch (delta.type) {
|
|
2325
|
-
case "text_delta":
|
|
2326
|
-
if (currentItemType === "message" && currentItemId) {
|
|
2327
|
-
const txt = delta.text;
|
|
2328
|
-
textBuffer += txt;
|
|
2329
|
-
yield factory.messageDelta(currentItemId, textBlock(txt));
|
|
2330
|
-
}
|
|
2331
|
-
break;
|
|
2332
|
-
case "thinking_delta":
|
|
2333
|
-
if (currentItemType === "reasoning" && currentItemId) {
|
|
2334
|
-
const txt = delta.thinking;
|
|
2335
|
-
thinkingBuffer += txt;
|
|
2336
|
-
yield factory.reasoningDelta(currentItemId, textBlock(txt));
|
|
2337
|
-
}
|
|
2338
|
-
break;
|
|
2339
|
-
case "input_json_delta":
|
|
2340
|
-
if (currentItemType === "tool_call" && currentItemId) {
|
|
2341
|
-
const partial = delta.partial_json;
|
|
2342
|
-
argsBuffer += partial;
|
|
2343
|
-
yield factory.toolCallDelta(currentItemId, { argumentsText: partial });
|
|
2344
|
-
}
|
|
2345
|
-
break;
|
|
2346
|
-
}
|
|
2347
|
-
continue;
|
|
2348
|
-
}
|
|
2349
|
-
case "content_block_stop":
|
|
2350
|
-
if (currentItemType === "message" && currentItemId) {
|
|
2351
|
-
yield factory.messageCompleted(currentItemId);
|
|
2352
|
-
output.push(messageItem([textBlock(textBuffer)], { id: currentItemId }));
|
|
2353
|
-
rawReplayContent.push({
|
|
2354
|
-
type: "text",
|
|
2355
|
-
text: textBuffer
|
|
2356
|
-
});
|
|
2357
|
-
} else if (currentItemType === "reasoning" && currentItemId && currentThinkingVisibility !== "redacted") {
|
|
2236
|
+
for await (const batch of iterateProviderStreamBatches({
|
|
2237
|
+
reader,
|
|
2238
|
+
parser,
|
|
2239
|
+
factory,
|
|
2240
|
+
providerLabel: "Messages",
|
|
2241
|
+
transportLabel: "SSE event(s)",
|
|
2242
|
+
incompleteMessage: "Stream ended with an incomplete Messages SSE frame"
|
|
2243
|
+
})) {
|
|
2244
|
+
for (const warning of batch.warnings) yield warning;
|
|
2245
|
+
for (const sseEvent of batch.items) switch (sseEvent.type) {
|
|
2246
|
+
case "ping": continue;
|
|
2247
|
+
case "error": {
|
|
2248
|
+
const err = sseEvent.data.error;
|
|
2249
|
+
yield factory.responseWarning(err.message, err.type);
|
|
2250
|
+
continue;
|
|
2251
|
+
}
|
|
2252
|
+
case "message_start":
|
|
2253
|
+
messageResponse = sseEvent.data.message;
|
|
2254
|
+
rawResponseId = messageResponse.id;
|
|
2255
|
+
continue;
|
|
2256
|
+
case "content_block_start": {
|
|
2257
|
+
const block = sseEvent.data.content_block;
|
|
2258
|
+
currentContentBlockIndex = sseEvent.data.index;
|
|
2259
|
+
switch (block.type) {
|
|
2260
|
+
case "text":
|
|
2261
|
+
currentItemType = "message";
|
|
2262
|
+
currentItemId = synthesizeItemId("msg", currentContentBlockIndex, rawResponseId);
|
|
2263
|
+
textBuffer = "";
|
|
2264
|
+
yield factory.messageStarted(currentItemId);
|
|
2265
|
+
break;
|
|
2266
|
+
case "thinking":
|
|
2267
|
+
currentItemType = "reasoning";
|
|
2268
|
+
currentItemId = synthesizeItemId("reason", currentContentBlockIndex, rawResponseId);
|
|
2269
|
+
currentThinkingVisibility = "full";
|
|
2270
|
+
thinkingBuffer = "";
|
|
2271
|
+
yield factory.reasoningStarted(currentItemId, "full");
|
|
2272
|
+
break;
|
|
2273
|
+
case "redacted_thinking": {
|
|
2274
|
+
currentItemType = "reasoning";
|
|
2275
|
+
currentItemId = synthesizeItemId("reason-redacted", currentContentBlockIndex, rawResponseId);
|
|
2276
|
+
currentThinkingVisibility = "redacted";
|
|
2277
|
+
const data = block.data;
|
|
2278
|
+
yield factory.reasoningStarted(currentItemId, "redacted");
|
|
2279
|
+
yield factory.reasoningDelta(currentItemId, textBlock(data));
|
|
2280
|
+
const redactedItem = reasoningItem([textBlock(data)], "redacted", currentItemId);
|
|
2358
2281
|
yield factory.reasoningCompleted(currentItemId);
|
|
2359
|
-
output.push(
|
|
2360
|
-
rawReplayContent.push({
|
|
2361
|
-
type: "thinking",
|
|
2362
|
-
thinking: thinkingBuffer
|
|
2363
|
-
});
|
|
2364
|
-
} else if (currentItemType === "tool_call" && currentItemId) {
|
|
2365
|
-
const tcItem = toolCallItem(currentItemId, currentToolName, currentArgsText || argsBuffer);
|
|
2366
|
-
yield factory.toolCallCompleted(currentItemId);
|
|
2367
|
-
output.push(tcItem);
|
|
2282
|
+
output.push(redactedItem);
|
|
2368
2283
|
rawReplayContent.push({
|
|
2369
|
-
type: "
|
|
2370
|
-
|
|
2371
|
-
name: currentToolName,
|
|
2372
|
-
input: parseToolUseInput(currentArgsText || argsBuffer)
|
|
2284
|
+
type: "redacted_thinking",
|
|
2285
|
+
data
|
|
2373
2286
|
});
|
|
2287
|
+
currentItemType = null;
|
|
2288
|
+
break;
|
|
2289
|
+
}
|
|
2290
|
+
case "tool_use": {
|
|
2291
|
+
const tuBlock = block;
|
|
2292
|
+
currentItemType = "tool_call";
|
|
2293
|
+
currentItemId = tuBlock.id;
|
|
2294
|
+
currentToolName = tuBlock.name;
|
|
2295
|
+
currentArgsText = "";
|
|
2296
|
+
argsBuffer = "";
|
|
2297
|
+
yield factory.toolCallStarted(currentItemId, currentToolName);
|
|
2298
|
+
break;
|
|
2374
2299
|
}
|
|
2375
|
-
currentItemType = null;
|
|
2376
|
-
currentItemId = "";
|
|
2377
|
-
continue;
|
|
2378
|
-
case "message_delta": {
|
|
2379
|
-
stopReason = sseEvent.data.delta.stop_reason;
|
|
2380
|
-
stopSequence = sseEvent.data.delta.stop_sequence;
|
|
2381
|
-
const u = sseEvent.data.usage;
|
|
2382
|
-
if (u) auxiliary.recordUsage(usageFromAnthropicMessages(u), "stream", u);
|
|
2383
|
-
continue;
|
|
2384
2300
|
}
|
|
2385
|
-
|
|
2301
|
+
continue;
|
|
2386
2302
|
}
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
2303
|
+
case "content_block_delta": {
|
|
2304
|
+
const delta = sseEvent.data.delta;
|
|
2305
|
+
switch (delta.type) {
|
|
2306
|
+
case "text_delta":
|
|
2307
|
+
if (currentItemType === "message" && currentItemId) {
|
|
2308
|
+
const txt = delta.text;
|
|
2309
|
+
textBuffer += txt;
|
|
2310
|
+
yield factory.messageDelta(currentItemId, textBlock(txt));
|
|
2311
|
+
}
|
|
2312
|
+
break;
|
|
2313
|
+
case "thinking_delta":
|
|
2314
|
+
if (currentItemType === "reasoning" && currentItemId) {
|
|
2315
|
+
const txt = delta.thinking;
|
|
2316
|
+
thinkingBuffer += txt;
|
|
2317
|
+
yield factory.reasoningDelta(currentItemId, textBlock(txt));
|
|
2318
|
+
}
|
|
2319
|
+
break;
|
|
2320
|
+
case "input_json_delta":
|
|
2321
|
+
if (currentItemType === "tool_call" && currentItemId) {
|
|
2322
|
+
const partial = delta.partial_json;
|
|
2323
|
+
argsBuffer += partial;
|
|
2324
|
+
yield factory.toolCallDelta(currentItemId, { argumentsText: partial });
|
|
2325
|
+
}
|
|
2326
|
+
break;
|
|
2327
|
+
}
|
|
2328
|
+
continue;
|
|
2390
2329
|
}
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
|
|
2396
|
-
|
|
2330
|
+
case "content_block_stop":
|
|
2331
|
+
if (currentItemType === "message" && currentItemId) {
|
|
2332
|
+
yield factory.messageCompleted(currentItemId);
|
|
2333
|
+
output.push(messageItem([textBlock(textBuffer)], { id: currentItemId }));
|
|
2334
|
+
rawReplayContent.push({
|
|
2335
|
+
type: "text",
|
|
2336
|
+
text: textBuffer
|
|
2337
|
+
});
|
|
2338
|
+
} else if (currentItemType === "reasoning" && currentItemId && currentThinkingVisibility !== "redacted") {
|
|
2339
|
+
yield factory.reasoningCompleted(currentItemId);
|
|
2340
|
+
output.push(reasoningItem([textBlock(thinkingBuffer)], currentThinkingVisibility, currentItemId));
|
|
2341
|
+
rawReplayContent.push({
|
|
2342
|
+
type: "thinking",
|
|
2343
|
+
thinking: thinkingBuffer
|
|
2344
|
+
});
|
|
2345
|
+
} else if (currentItemType === "tool_call" && currentItemId) {
|
|
2346
|
+
const tcItem = toolCallItem(currentItemId, currentToolName, currentArgsText || argsBuffer);
|
|
2347
|
+
yield factory.toolCallCompleted(currentItemId);
|
|
2348
|
+
output.push(tcItem);
|
|
2349
|
+
rawReplayContent.push({
|
|
2350
|
+
type: "tool_use",
|
|
2351
|
+
id: currentItemId,
|
|
2352
|
+
name: currentToolName,
|
|
2353
|
+
input: parseProviderToolUseInput(currentArgsText || argsBuffer)
|
|
2354
|
+
});
|
|
2355
|
+
}
|
|
2356
|
+
currentItemType = null;
|
|
2357
|
+
currentItemId = "";
|
|
2358
|
+
continue;
|
|
2359
|
+
case "message_delta": {
|
|
2360
|
+
stopReason = sseEvent.data.delta.stop_reason;
|
|
2361
|
+
stopSequence = sseEvent.data.delta.stop_sequence;
|
|
2362
|
+
const u = sseEvent.data.usage;
|
|
2363
|
+
if (u) auxiliary.recordUsage(usageFromAnthropicMessages(u), "stream", u);
|
|
2364
|
+
continue;
|
|
2365
|
+
}
|
|
2366
|
+
case "message_stop": break;
|
|
2397
2367
|
}
|
|
2398
2368
|
}
|
|
2399
|
-
if (parser.getRemaining().trim().length > 0) yield factory.responseWarning("Stream ended with an incomplete Messages SSE frame", "STREAM_ERROR");
|
|
2400
2369
|
const replay = [...replayFromOutput(output)];
|
|
2401
2370
|
if (messageResponse) {
|
|
2402
2371
|
const replayContent = rawReplayContent.length > 0 ? rawReplayContent : messageResponse.content;
|
|
@@ -2414,32 +2383,12 @@ var MessagesAdapter = class extends AdapterBase {
|
|
|
2414
2383
|
stopReason,
|
|
2415
2384
|
stopSequence
|
|
2416
2385
|
}));
|
|
2417
|
-
if (
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
output,
|
|
2424
|
-
replay,
|
|
2425
|
-
stopReason: stopReason ? mapStopReason(stopReason) : void 0,
|
|
2426
|
-
usage: auxiliaryResult.usage,
|
|
2427
|
-
billing: auxiliaryResult.billing,
|
|
2428
|
-
auxiliary: auxiliaryResult.auxiliary,
|
|
2429
|
-
warnings: auxiliaryResult.warnings,
|
|
2430
|
-
metadataSources: auxiliaryResult.metadataSources,
|
|
2431
|
-
rawResponseId
|
|
2432
|
-
}, factory);
|
|
2433
|
-
yield factory.responseCompleted({
|
|
2434
|
-
replay: finalResponse.replay,
|
|
2435
|
-
stopReason: finalResponse.stopReason,
|
|
2436
|
-
trace: finalResponse.backend,
|
|
2437
|
-
usage: finalResponse.usage,
|
|
2438
|
-
billing: finalResponse.billing,
|
|
2439
|
-
auxiliary: finalResponse.auxiliary,
|
|
2440
|
-
warnings: finalResponse.warnings
|
|
2441
|
-
});
|
|
2442
|
-
}
|
|
2386
|
+
if (gate.tryComplete()) yield* this.emitStreamCompleted(factory, request, auxiliary, {
|
|
2387
|
+
output,
|
|
2388
|
+
replay,
|
|
2389
|
+
stopReason: stopReason ? mapStopReason(stopReason) : void 0,
|
|
2390
|
+
rawResponseId
|
|
2391
|
+
});
|
|
2443
2392
|
}
|
|
2444
2393
|
};
|
|
2445
2394
|
//#endregion
|
|
@@ -2454,21 +2403,7 @@ var MessagesAdapter = class extends AdapterBase {
|
|
|
2454
2403
|
* - replay fidelity 依赖 provider 是否暴露可回放的 assistant turn 字段
|
|
2455
2404
|
*/
|
|
2456
2405
|
const REASONING_FIELDS = ["reasoning_content", "reasoning"];
|
|
2457
|
-
const
|
|
2458
|
-
kind: "chat-completions",
|
|
2459
|
-
instructionsMode: "system_message",
|
|
2460
|
-
supportedBlockTypes: ["text", "json"],
|
|
2461
|
-
reasoningBlockTypes: ["text"],
|
|
2462
|
-
capabilities: {
|
|
2463
|
-
textStreaming: "native",
|
|
2464
|
-
reasoningStreaming: "native",
|
|
2465
|
-
toolCallStreaming: "native",
|
|
2466
|
-
replay: "opaque",
|
|
2467
|
-
usage: "final",
|
|
2468
|
-
toolResultOutcomes: ["success"]
|
|
2469
|
-
}
|
|
2470
|
-
};
|
|
2471
|
-
const mapper$1 = new NormalizedRequestMapper(profile$1);
|
|
2406
|
+
const mapper$1 = new NormalizedRequestMapper("chat-completions");
|
|
2472
2407
|
function extractReasoningText(value) {
|
|
2473
2408
|
if (typeof value === "string") return value;
|
|
2474
2409
|
if (Array.isArray(value)) return value.map(extractReasoningText).join("");
|
|
@@ -2545,7 +2480,7 @@ function buildAssistantReplayMessage(params) {
|
|
|
2545
2480
|
}
|
|
2546
2481
|
var ChatCompletionsAdapter = class extends AdapterBase {
|
|
2547
2482
|
kind = "chat-completions";
|
|
2548
|
-
|
|
2483
|
+
isSyntheticStream = false;
|
|
2549
2484
|
apiKey;
|
|
2550
2485
|
baseUrl;
|
|
2551
2486
|
fetchFn;
|
|
@@ -2564,7 +2499,7 @@ var ChatCompletionsAdapter = class extends AdapterBase {
|
|
|
2564
2499
|
for (const item of request.input) switch (item.type) {
|
|
2565
2500
|
case "message": {
|
|
2566
2501
|
const role = item.role;
|
|
2567
|
-
const text =
|
|
2502
|
+
const text = mapper$1.textFromBlocks(item.content, `input message (${item.role}) content`);
|
|
2568
2503
|
messages.push({
|
|
2569
2504
|
role,
|
|
2570
2505
|
content: text || null
|
|
@@ -2590,22 +2525,21 @@ var ChatCompletionsAdapter = class extends AdapterBase {
|
|
|
2590
2525
|
break;
|
|
2591
2526
|
}
|
|
2592
2527
|
case "tool_result":
|
|
2593
|
-
mapper$1.assertToolResultOutcome(item.outcome);
|
|
2594
2528
|
messages.push({
|
|
2595
2529
|
role: "tool",
|
|
2596
2530
|
tool_call_id: item.callId,
|
|
2597
2531
|
name: item.toolName,
|
|
2598
|
-
content:
|
|
2532
|
+
content: mapper$1.textFromBlocks(item.content, `tool_result ${item.callId} content`)
|
|
2599
2533
|
});
|
|
2600
2534
|
break;
|
|
2601
2535
|
case "reasoning":
|
|
2602
2536
|
messages.push({
|
|
2603
2537
|
role: "assistant",
|
|
2604
|
-
content:
|
|
2538
|
+
content: mapper$1.textFromBlocks(item.content, "reasoning content")
|
|
2605
2539
|
});
|
|
2606
2540
|
break;
|
|
2607
2541
|
case "opaque": {
|
|
2608
|
-
if (item.purpose !== "replay") break;
|
|
2542
|
+
if (item.source !== "chat.completions" || item.purpose !== "replay") break;
|
|
2609
2543
|
assertOpaqueReplayEnvelope(item.payload);
|
|
2610
2544
|
const payload = item.payload;
|
|
2611
2545
|
if (payload.role === "assistant" && typeof payload.content === "string") messages.push({
|
|
@@ -2629,7 +2563,7 @@ var ChatCompletionsAdapter = class extends AdapterBase {
|
|
|
2629
2563
|
stream: true,
|
|
2630
2564
|
n: 1
|
|
2631
2565
|
};
|
|
2632
|
-
|
|
2566
|
+
body.tools = mapper$1.mapToolsIfPresent(request.tools, (t) => ({
|
|
2633
2567
|
type: "function",
|
|
2634
2568
|
function: {
|
|
2635
2569
|
name: t.name,
|
|
@@ -2637,14 +2571,14 @@ var ChatCompletionsAdapter = class extends AdapterBase {
|
|
|
2637
2571
|
parameters: t.inputSchema
|
|
2638
2572
|
}
|
|
2639
2573
|
}));
|
|
2640
|
-
|
|
2641
|
-
|
|
2642
|
-
|
|
2643
|
-
|
|
2574
|
+
body.tool_choice = mapper$1.mapToolChoice(request.toolChoice, {
|
|
2575
|
+
auto: "auto",
|
|
2576
|
+
none: "none",
|
|
2577
|
+
tool: (name) => ({
|
|
2644
2578
|
type: "function",
|
|
2645
|
-
function: { name
|
|
2646
|
-
}
|
|
2647
|
-
}
|
|
2579
|
+
function: { name }
|
|
2580
|
+
})
|
|
2581
|
+
});
|
|
2648
2582
|
if (request.temperature !== void 0) body.temperature = request.temperature;
|
|
2649
2583
|
if (request.maxOutputTokens !== void 0) body.max_tokens = request.maxOutputTokens;
|
|
2650
2584
|
if (request.metadata) body.metadata = request.metadata;
|
|
@@ -2652,42 +2586,19 @@ var ChatCompletionsAdapter = class extends AdapterBase {
|
|
|
2652
2586
|
}
|
|
2653
2587
|
async *runStream(providerRequest, factory, request) {
|
|
2654
2588
|
const auxiliary = this.createAuxiliaryState(request);
|
|
2655
|
-
|
|
2656
|
-
|
|
2657
|
-
|
|
2658
|
-
|
|
2659
|
-
|
|
2660
|
-
|
|
2661
|
-
|
|
2662
|
-
|
|
2663
|
-
|
|
2664
|
-
|
|
2665
|
-
});
|
|
2666
|
-
} catch (err) {
|
|
2667
|
-
throw new AIProviderError(err instanceof Error ? err.message : String(err), "PROVIDER_ERROR");
|
|
2668
|
-
}
|
|
2669
|
-
if (!response.ok) {
|
|
2670
|
-
const errorBody = await response.text().catch(() => "");
|
|
2671
|
-
throw providerHttpError(response.status, errorBody);
|
|
2672
|
-
}
|
|
2673
|
-
const reader = response.body?.getReader();
|
|
2674
|
-
if (!reader) throw new AIStreamError("Response body is not readable", "STREAM_ERROR");
|
|
2675
|
-
const parser = new IncrementalStreamParser(splitLines, (item) => {
|
|
2676
|
-
const trimmed = item.trim();
|
|
2677
|
-
if (!trimmed.startsWith("data: ")) return { status: "ignored" };
|
|
2678
|
-
const data = trimmed.slice(6).trim();
|
|
2679
|
-
if (data === "[DONE]") return { status: "ignored" };
|
|
2680
|
-
try {
|
|
2681
|
-
return {
|
|
2682
|
-
status: "parsed",
|
|
2683
|
-
value: JSON.parse(data)
|
|
2684
|
-
};
|
|
2685
|
-
} catch {
|
|
2686
|
-
return { status: "malformed" };
|
|
2687
|
-
}
|
|
2589
|
+
const gate = createCompletionGate();
|
|
2590
|
+
const { reader } = await openProviderJsonStream({
|
|
2591
|
+
fetchFn: this.fetchFn,
|
|
2592
|
+
url: `${this.baseUrl}/chat/completions`,
|
|
2593
|
+
headers: {
|
|
2594
|
+
"Content-Type": "application/json",
|
|
2595
|
+
Authorization: `Bearer ${this.apiKey}`
|
|
2596
|
+
},
|
|
2597
|
+
body: providerRequest,
|
|
2598
|
+
signal: request.signal
|
|
2688
2599
|
});
|
|
2600
|
+
const parser = createChatCompletionsSseParser();
|
|
2689
2601
|
const output = [];
|
|
2690
|
-
let streamDone = false;
|
|
2691
2602
|
let responseId;
|
|
2692
2603
|
let accumulatedContent = "";
|
|
2693
2604
|
let accumulatedReasoning = "";
|
|
@@ -2695,9 +2606,7 @@ var ChatCompletionsAdapter = class extends AdapterBase {
|
|
|
2695
2606
|
let currentReasoningId = "";
|
|
2696
2607
|
let hasMessageStarted = false;
|
|
2697
2608
|
let hasReasoningStarted = false;
|
|
2698
|
-
let completedEmitted = false;
|
|
2699
2609
|
let warnedNonZeroChoice = false;
|
|
2700
|
-
const buildResponse = this.buildResponse.bind(this);
|
|
2701
2610
|
const pendingToolCalls = /* @__PURE__ */ new Map();
|
|
2702
2611
|
const reasoningByField = /* @__PURE__ */ new Map();
|
|
2703
2612
|
const finalizePendingTurn = () => {
|
|
@@ -2738,163 +2647,131 @@ var ChatCompletionsAdapter = class extends AdapterBase {
|
|
|
2738
2647
|
};
|
|
2739
2648
|
};
|
|
2740
2649
|
const emitCompleted = async function* (stopReason, assistantReplayMessage, rawResponseId) {
|
|
2741
|
-
if (
|
|
2650
|
+
if (!gate.tryComplete()) {
|
|
2742
2651
|
yield factory.responseWarning("Duplicate finish signal ignored", "DUPLICATE_FINISH");
|
|
2743
2652
|
return;
|
|
2744
2653
|
}
|
|
2745
|
-
completedEmitted = true;
|
|
2746
2654
|
const replay = [...replayFromOutput(output)];
|
|
2747
2655
|
if (assistantReplayMessage) replay.push(opaqueItem("chat.completions", "replay", {
|
|
2748
2656
|
replaceCanonical: true,
|
|
2749
2657
|
messages: [assistantReplayMessage]
|
|
2750
2658
|
}));
|
|
2751
|
-
|
|
2752
|
-
for (const event of auxiliaryResult.events) yield event;
|
|
2753
|
-
const finalResponse = buildResponse(request, {
|
|
2659
|
+
yield* this.emitStreamCompleted(factory, request, auxiliary, {
|
|
2754
2660
|
output,
|
|
2755
2661
|
replay,
|
|
2756
2662
|
stopReason,
|
|
2757
|
-
usage: auxiliaryResult.usage,
|
|
2758
|
-
billing: auxiliaryResult.billing,
|
|
2759
|
-
auxiliary: auxiliaryResult.auxiliary,
|
|
2760
|
-
warnings: auxiliaryResult.warnings,
|
|
2761
|
-
metadataSources: auxiliaryResult.metadataSources,
|
|
2762
2663
|
rawResponseId
|
|
2763
|
-
}, factory);
|
|
2764
|
-
yield factory.responseCompleted({
|
|
2765
|
-
replay: finalResponse.replay,
|
|
2766
|
-
stopReason: finalResponse.stopReason,
|
|
2767
|
-
trace: finalResponse.backend,
|
|
2768
|
-
usage: finalResponse.usage,
|
|
2769
|
-
billing: finalResponse.billing,
|
|
2770
|
-
auxiliary: finalResponse.auxiliary,
|
|
2771
|
-
warnings: finalResponse.warnings
|
|
2772
2664
|
});
|
|
2773
|
-
};
|
|
2774
|
-
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
|
|
2779
|
-
|
|
2780
|
-
|
|
2781
|
-
|
|
2782
|
-
|
|
2783
|
-
|
|
2784
|
-
|
|
2785
|
-
if (
|
|
2786
|
-
for (const
|
|
2787
|
-
|
|
2788
|
-
|
|
2789
|
-
|
|
2790
|
-
|
|
2791
|
-
if (!warnedNonZeroChoice) {
|
|
2792
|
-
yield factory.responseWarning(`Chat Completions returned choice index ${choice.index}; only the first choice (index 0) is supported. This choice is ignored.`, "MULTIPLE_CHOICES_IGNORED");
|
|
2793
|
-
warnedNonZeroChoice = true;
|
|
2794
|
-
}
|
|
2795
|
-
continue;
|
|
2665
|
+
}.bind(this);
|
|
2666
|
+
for await (const batch of iterateProviderStreamBatches({
|
|
2667
|
+
reader,
|
|
2668
|
+
parser,
|
|
2669
|
+
factory,
|
|
2670
|
+
providerLabel: "Chat Completions",
|
|
2671
|
+
transportLabel: "SSE event(s)",
|
|
2672
|
+
incompleteMessage: "Stream ended with an incomplete Chat Completions SSE frame"
|
|
2673
|
+
})) {
|
|
2674
|
+
for (const warning of batch.warnings) yield warning;
|
|
2675
|
+
for (const chunk of batch.items) {
|
|
2676
|
+
responseId = chunk.id;
|
|
2677
|
+
if (chunk.usage) auxiliary.recordUsage(usageFromChatCompletions(chunk.usage), "final", chunk.usage);
|
|
2678
|
+
for (const choice of chunk.choices) {
|
|
2679
|
+
if (choice.index !== 0) {
|
|
2680
|
+
if (!warnedNonZeroChoice) {
|
|
2681
|
+
yield factory.responseWarning(`Chat Completions returned choice index ${choice.index}; only the first choice (index 0) is supported. This choice is ignored.`, "MULTIPLE_CHOICES_IGNORED");
|
|
2682
|
+
warnedNonZeroChoice = true;
|
|
2796
2683
|
}
|
|
2797
|
-
|
|
2798
|
-
|
|
2799
|
-
|
|
2684
|
+
continue;
|
|
2685
|
+
}
|
|
2686
|
+
if (gate.completed) {
|
|
2687
|
+
if (choice.finish_reason) yield factory.responseWarning("Duplicate finish signal ignored", "DUPLICATE_FINISH");
|
|
2688
|
+
continue;
|
|
2689
|
+
}
|
|
2690
|
+
const delta = choice.delta;
|
|
2691
|
+
const finishReason = choice.finish_reason;
|
|
2692
|
+
const reasoningDeltas = extractReasoningDeltas(delta);
|
|
2693
|
+
const ensureMessageStarted = () => {
|
|
2694
|
+
if (hasMessageStarted) return;
|
|
2695
|
+
currentMessageId = `msg-${chunk.id}`;
|
|
2696
|
+
hasMessageStarted = true;
|
|
2697
|
+
accumulatedContent = "";
|
|
2698
|
+
};
|
|
2699
|
+
if (reasoningDeltas.length > 0) {
|
|
2700
|
+
if (!hasReasoningStarted) {
|
|
2701
|
+
currentReasoningId = `reason-${chunk.id}`;
|
|
2702
|
+
hasReasoningStarted = true;
|
|
2703
|
+
accumulatedReasoning = "";
|
|
2704
|
+
yield factory.reasoningStarted(currentReasoningId, "full");
|
|
2800
2705
|
}
|
|
2801
|
-
const
|
|
2802
|
-
|
|
2803
|
-
|
|
2804
|
-
|
|
2805
|
-
if (hasMessageStarted) return;
|
|
2806
|
-
currentMessageId = `msg-${chunk.id}`;
|
|
2807
|
-
hasMessageStarted = true;
|
|
2808
|
-
accumulatedContent = "";
|
|
2809
|
-
};
|
|
2810
|
-
if (reasoningDeltas.length > 0) {
|
|
2811
|
-
if (!hasReasoningStarted) {
|
|
2812
|
-
currentReasoningId = `reason-${chunk.id}`;
|
|
2813
|
-
hasReasoningStarted = true;
|
|
2814
|
-
accumulatedReasoning = "";
|
|
2815
|
-
yield factory.reasoningStarted(currentReasoningId, "full");
|
|
2816
|
-
}
|
|
2817
|
-
for (const reasoningDelta of reasoningDeltas) {
|
|
2818
|
-
accumulatedReasoning += reasoningDelta.text;
|
|
2819
|
-
reasoningByField.set(reasoningDelta.field, (reasoningByField.get(reasoningDelta.field) ?? "") + reasoningDelta.text);
|
|
2820
|
-
yield factory.reasoningDelta(currentReasoningId, textBlock(reasoningDelta.text));
|
|
2821
|
-
}
|
|
2706
|
+
for (const reasoningDelta of reasoningDeltas) {
|
|
2707
|
+
accumulatedReasoning += reasoningDelta.text;
|
|
2708
|
+
reasoningByField.set(reasoningDelta.field, (reasoningByField.get(reasoningDelta.field) ?? "") + reasoningDelta.text);
|
|
2709
|
+
yield factory.reasoningDelta(currentReasoningId, textBlock(reasoningDelta.text));
|
|
2822
2710
|
}
|
|
2823
|
-
|
|
2824
|
-
|
|
2825
|
-
|
|
2826
|
-
|
|
2827
|
-
|
|
2828
|
-
accumulatedContent += delta.content;
|
|
2829
|
-
yield factory.messageDelta(currentMessageId, textBlock(delta.content));
|
|
2711
|
+
}
|
|
2712
|
+
if (delta.content) {
|
|
2713
|
+
if (!hasMessageStarted) {
|
|
2714
|
+
ensureMessageStarted();
|
|
2715
|
+
yield factory.messageStarted(currentMessageId);
|
|
2830
2716
|
}
|
|
2831
|
-
|
|
2832
|
-
|
|
2833
|
-
|
|
2834
|
-
|
|
2835
|
-
|
|
2836
|
-
|
|
2837
|
-
|
|
2838
|
-
if (tc.id) {
|
|
2839
|
-
pendingToolCalls.set(idx, {
|
|
2840
|
-
id: tc.id,
|
|
2841
|
-
name: tc.function?.name ?? "",
|
|
2842
|
-
args: ""
|
|
2843
|
-
});
|
|
2844
|
-
yield factory.toolCallStarted(tc.id, tc.function?.name ?? "");
|
|
2845
|
-
}
|
|
2846
|
-
if (tc.function?.arguments) {
|
|
2847
|
-
const pending = pendingToolCalls.get(idx);
|
|
2848
|
-
if (pending) {
|
|
2849
|
-
pending.args += tc.function.arguments;
|
|
2850
|
-
yield factory.toolCallDelta(pending.id, { argumentsText: tc.function.arguments });
|
|
2851
|
-
}
|
|
2852
|
-
}
|
|
2853
|
-
}
|
|
2717
|
+
accumulatedContent += delta.content;
|
|
2718
|
+
yield factory.messageDelta(currentMessageId, textBlock(delta.content));
|
|
2719
|
+
}
|
|
2720
|
+
if (delta.tool_calls) {
|
|
2721
|
+
if (!hasMessageStarted) {
|
|
2722
|
+
ensureMessageStarted();
|
|
2723
|
+
yield factory.messageStarted(currentMessageId);
|
|
2854
2724
|
}
|
|
2855
|
-
|
|
2856
|
-
|
|
2857
|
-
|
|
2858
|
-
|
|
2859
|
-
|
|
2860
|
-
|
|
2861
|
-
const fcId = `fc-${chunk.id}-0`;
|
|
2862
|
-
pendingToolCalls.set(0, {
|
|
2863
|
-
id: fcId,
|
|
2864
|
-
name: delta.function_call.name,
|
|
2725
|
+
for (const tc of delta.tool_calls) {
|
|
2726
|
+
const idx = tc.index;
|
|
2727
|
+
if (tc.id) {
|
|
2728
|
+
pendingToolCalls.set(idx, {
|
|
2729
|
+
id: tc.id,
|
|
2730
|
+
name: tc.function?.name ?? "",
|
|
2865
2731
|
args: ""
|
|
2866
2732
|
});
|
|
2867
|
-
yield factory.toolCallStarted(
|
|
2733
|
+
yield factory.toolCallStarted(tc.id, tc.function?.name ?? "");
|
|
2868
2734
|
}
|
|
2869
|
-
if (
|
|
2870
|
-
const pending = pendingToolCalls.get(
|
|
2735
|
+
if (tc.function?.arguments) {
|
|
2736
|
+
const pending = pendingToolCalls.get(idx);
|
|
2871
2737
|
if (pending) {
|
|
2872
|
-
pending.args +=
|
|
2873
|
-
yield factory.toolCallDelta(pending.id, { argumentsText:
|
|
2738
|
+
pending.args += tc.function.arguments;
|
|
2739
|
+
yield factory.toolCallDelta(pending.id, { argumentsText: tc.function.arguments });
|
|
2874
2740
|
}
|
|
2875
2741
|
}
|
|
2876
2742
|
}
|
|
2877
|
-
|
|
2878
|
-
|
|
2879
|
-
|
|
2880
|
-
|
|
2743
|
+
}
|
|
2744
|
+
if (delta.function_call) {
|
|
2745
|
+
if (!hasMessageStarted) {
|
|
2746
|
+
ensureMessageStarted();
|
|
2747
|
+
yield factory.messageStarted(currentMessageId);
|
|
2748
|
+
}
|
|
2749
|
+
if (delta.function_call.name) {
|
|
2750
|
+
const fcId = `fc-${chunk.id}-0`;
|
|
2751
|
+
pendingToolCalls.set(0, {
|
|
2752
|
+
id: fcId,
|
|
2753
|
+
name: delta.function_call.name,
|
|
2754
|
+
args: ""
|
|
2755
|
+
});
|
|
2756
|
+
yield factory.toolCallStarted(fcId, delta.function_call.name);
|
|
2757
|
+
}
|
|
2758
|
+
if (delta.function_call.arguments) {
|
|
2759
|
+
const pending = pendingToolCalls.get(0);
|
|
2760
|
+
if (pending) {
|
|
2761
|
+
pending.args += delta.function_call.arguments;
|
|
2762
|
+
yield factory.toolCallDelta(pending.id, { argumentsText: delta.function_call.arguments });
|
|
2763
|
+
}
|
|
2881
2764
|
}
|
|
2882
2765
|
}
|
|
2766
|
+
if (finishReason && finishReason !== null) {
|
|
2767
|
+
const { events, assistantReplayMessage } = finalizePendingTurn();
|
|
2768
|
+
for (const event of events) yield event;
|
|
2769
|
+
yield* emitCompleted(mapStopReason(finishReason), assistantReplayMessage, chunk.id);
|
|
2770
|
+
}
|
|
2883
2771
|
}
|
|
2884
|
-
if (done) {
|
|
2885
|
-
streamDone = true;
|
|
2886
|
-
break;
|
|
2887
|
-
}
|
|
2888
|
-
}
|
|
2889
|
-
} finally {
|
|
2890
|
-
try {
|
|
2891
|
-
if (!streamDone) await reader.cancel().catch(() => void 0);
|
|
2892
|
-
} finally {
|
|
2893
|
-
reader.releaseLock();
|
|
2894
2772
|
}
|
|
2895
2773
|
}
|
|
2896
|
-
if (
|
|
2897
|
-
if (!completedEmitted && (hasMessageStarted || hasReasoningStarted || pendingToolCalls.size > 0)) {
|
|
2774
|
+
if (!gate.completed && (hasMessageStarted || hasReasoningStarted || pendingToolCalls.size > 0)) {
|
|
2898
2775
|
yield factory.responseWarning("Stream ended without a finish_reason", "INCOMPLETE_STREAM");
|
|
2899
2776
|
const { events, assistantReplayMessage } = finalizePendingTurn();
|
|
2900
2777
|
for (const event of events) yield event;
|
|
@@ -2920,29 +2797,7 @@ var ChatCompletionsAdapter = class extends AdapterBase {
|
|
|
2920
2797
|
* - tool_call 不支持逐 token 流式
|
|
2921
2798
|
* - replay 保真度低(无 opaque continuation 机制)
|
|
2922
2799
|
*/
|
|
2923
|
-
const
|
|
2924
|
-
kind: "ollama",
|
|
2925
|
-
instructionsMode: "system_message",
|
|
2926
|
-
supportedBlockTypes: ["text", "json"],
|
|
2927
|
-
reasoningBlockTypes: ["text"],
|
|
2928
|
-
capabilities: {
|
|
2929
|
-
textStreaming: "native",
|
|
2930
|
-
reasoningStreaming: "none",
|
|
2931
|
-
toolCallStreaming: "synthetic",
|
|
2932
|
-
replay: "opaque",
|
|
2933
|
-
usage: "final",
|
|
2934
|
-
toolResultOutcomes: ["success"]
|
|
2935
|
-
}
|
|
2936
|
-
};
|
|
2937
|
-
const mapper = new NormalizedRequestMapper(profile);
|
|
2938
|
-
function parseOllamaToolArguments(item) {
|
|
2939
|
-
if (item.argumentsJson && typeof item.argumentsJson === "object" && item.argumentsJson !== null) return item.argumentsJson;
|
|
2940
|
-
try {
|
|
2941
|
-
const parsed = JSON.parse(item.argumentsText);
|
|
2942
|
-
if (parsed && typeof parsed === "object") return parsed;
|
|
2943
|
-
} catch {}
|
|
2944
|
-
throw new AIRequestError("ollama tool_call argumentsText must be valid JSON object when argumentsJson is absent", "TOOL_CALL_ARGUMENTS_INVALID");
|
|
2945
|
-
}
|
|
2800
|
+
const mapper = new NormalizedRequestMapper("ollama");
|
|
2946
2801
|
function isOllamaReplayToolCalls(value) {
|
|
2947
2802
|
return Array.isArray(value) && value.every((entry) => {
|
|
2948
2803
|
if (!entry || typeof entry !== "object" || !("function" in entry)) return false;
|
|
@@ -2960,7 +2815,7 @@ function toWireOllamaToolCalls(toolCalls) {
|
|
|
2960
2815
|
}
|
|
2961
2816
|
var OllamaAdapter = class extends AdapterBase {
|
|
2962
2817
|
kind = "ollama";
|
|
2963
|
-
|
|
2818
|
+
isSyntheticStream = false;
|
|
2964
2819
|
baseUrl;
|
|
2965
2820
|
apiKey;
|
|
2966
2821
|
fetchFn;
|
|
@@ -2971,7 +2826,6 @@ var OllamaAdapter = class extends AdapterBase {
|
|
|
2971
2826
|
this.fetchFn = options.fetch ?? globalThis.fetch;
|
|
2972
2827
|
}
|
|
2973
2828
|
buildRequest(request) {
|
|
2974
|
-
if (request.toolChoice && request.toolChoice !== "auto") throw new AIRequestError("ollama does not support explicit toolChoice", "UNSUPPORTED_TOOL_CHOICE");
|
|
2975
2829
|
const messages = [];
|
|
2976
2830
|
/** Local-only name → call id queue for best-effort tool_result association (not sent to Ollama). */
|
|
2977
2831
|
const callIdsByName = /* @__PURE__ */ new Map();
|
|
@@ -2984,7 +2838,7 @@ var OllamaAdapter = class extends AdapterBase {
|
|
|
2984
2838
|
const role = item.role;
|
|
2985
2839
|
messages.push({
|
|
2986
2840
|
role,
|
|
2987
|
-
content:
|
|
2841
|
+
content: mapper.textFromBlocks(item.content, `input message (${item.role}) content`)
|
|
2988
2842
|
});
|
|
2989
2843
|
break;
|
|
2990
2844
|
}
|
|
@@ -2992,7 +2846,7 @@ var OllamaAdapter = class extends AdapterBase {
|
|
|
2992
2846
|
const lastAssistant = messages.findLast((m) => m.role === "assistant");
|
|
2993
2847
|
const tc = { function: {
|
|
2994
2848
|
name: item.name,
|
|
2995
|
-
arguments:
|
|
2849
|
+
arguments: mapper.parseToolArguments(item)
|
|
2996
2850
|
} };
|
|
2997
2851
|
const queue = callIdsByName.get(item.name) ?? [];
|
|
2998
2852
|
queue.push(item.id);
|
|
@@ -3006,12 +2860,11 @@ var OllamaAdapter = class extends AdapterBase {
|
|
|
3006
2860
|
break;
|
|
3007
2861
|
}
|
|
3008
2862
|
case "tool_result": {
|
|
3009
|
-
mapper.assertToolResultOutcome(item.outcome);
|
|
3010
2863
|
const queue = callIdsByName.get(item.toolName);
|
|
3011
2864
|
if (queue && queue.length > 0) queue.shift();
|
|
3012
2865
|
messages.push({
|
|
3013
2866
|
role: "tool",
|
|
3014
|
-
content:
|
|
2867
|
+
content: mapper.textFromBlocks(item.content, `tool_result ${item.callId} content`)
|
|
3015
2868
|
});
|
|
3016
2869
|
break;
|
|
3017
2870
|
}
|
|
@@ -3053,7 +2906,9 @@ var OllamaAdapter = class extends AdapterBase {
|
|
|
3053
2906
|
messages,
|
|
3054
2907
|
stream: true
|
|
3055
2908
|
};
|
|
3056
|
-
|
|
2909
|
+
const toolChoice = request.toolChoice;
|
|
2910
|
+
const selectedTools = toolChoice === "none" ? [] : toolChoice && typeof toolChoice === "object" ? request.tools?.filter((tool) => tool.name === toolChoice.name) : request.tools;
|
|
2911
|
+
if (selectedTools && selectedTools.length > 0) body.tools = selectedTools.map((t) => ({
|
|
3057
2912
|
type: "function",
|
|
3058
2913
|
function: {
|
|
3059
2914
|
name: t.name,
|
|
@@ -3070,56 +2925,31 @@ var OllamaAdapter = class extends AdapterBase {
|
|
|
3070
2925
|
}
|
|
3071
2926
|
async *runStream(providerRequest, factory, request) {
|
|
3072
2927
|
const auxiliary = this.createAuxiliaryState(request);
|
|
3073
|
-
|
|
2928
|
+
const gate = createCompletionGate();
|
|
2929
|
+
if (request.toolChoice && request.toolChoice !== "auto") yield factory.responseWarning(request.toolChoice === "none" ? "Ollama toolChoice none was mapped by omitting tools" : `Ollama cannot force tool choice; only tool "${request.toolChoice.name}" was provided as a best-effort constraint`, WarningCode.CAPABILITY_DOWNGRADE);
|
|
3074
2930
|
if (request.metadata) yield factory.responseWarning("Request metadata is not supported by the Ollama adapter", "UNSUPPORTED_METADATA");
|
|
3075
2931
|
const headers = { "Content-Type": "application/json" };
|
|
3076
2932
|
if (this.apiKey) headers.Authorization = `Bearer ${this.apiKey}`;
|
|
3077
|
-
|
|
3078
|
-
|
|
3079
|
-
|
|
3080
|
-
|
|
3081
|
-
|
|
3082
|
-
|
|
3083
|
-
signal: request.signal
|
|
3084
|
-
});
|
|
3085
|
-
} catch (err) {
|
|
3086
|
-
throw new AIProviderError(err instanceof Error ? err.message : String(err), "PROVIDER_ERROR");
|
|
3087
|
-
}
|
|
3088
|
-
if (!response.ok) {
|
|
3089
|
-
const errorBody = await response.text().catch(() => "");
|
|
3090
|
-
throw providerHttpError(response.status, errorBody);
|
|
3091
|
-
}
|
|
3092
|
-
const reader = response.body?.getReader();
|
|
3093
|
-
if (!reader) throw new AIStreamError("Response body is not readable", "STREAM_ERROR");
|
|
3094
|
-
const parser = new IncrementalStreamParser(splitLines, (item) => {
|
|
3095
|
-
const trimmed = item.trim();
|
|
3096
|
-
if (!trimmed) return { status: "ignored" };
|
|
3097
|
-
try {
|
|
3098
|
-
const parsed = JSON.parse(trimmed);
|
|
3099
|
-
if (parsed && typeof parsed === "object" && "message" in parsed) return {
|
|
3100
|
-
status: "parsed",
|
|
3101
|
-
value: parsed
|
|
3102
|
-
};
|
|
3103
|
-
return { status: "malformed" };
|
|
3104
|
-
} catch {
|
|
3105
|
-
return { status: "malformed" };
|
|
3106
|
-
}
|
|
2933
|
+
const { reader } = await openProviderJsonStream({
|
|
2934
|
+
fetchFn: this.fetchFn,
|
|
2935
|
+
url: `${this.baseUrl}/api/chat`,
|
|
2936
|
+
headers,
|
|
2937
|
+
body: providerRequest,
|
|
2938
|
+
signal: request.signal
|
|
3107
2939
|
});
|
|
2940
|
+
const parser = createNdjsonLineParser((value) => !!value && typeof value === "object" && "message" in value);
|
|
3108
2941
|
const output = [];
|
|
3109
|
-
let streamDone = false;
|
|
3110
2942
|
let responseId;
|
|
3111
2943
|
let accumulatedContent = "";
|
|
3112
2944
|
let currentMessageId = "";
|
|
3113
2945
|
let hasMessageStarted = false;
|
|
3114
2946
|
let pendingToolCalls = [];
|
|
3115
2947
|
let toolCallIndex = 0;
|
|
3116
|
-
const buildResponse = this.buildResponse.bind(this);
|
|
3117
2948
|
const emitCompleted = async function* (stopReason, rawResponseId) {
|
|
3118
|
-
if (
|
|
2949
|
+
if (!gate.tryComplete()) {
|
|
3119
2950
|
yield factory.responseWarning("Duplicate finish signal ignored", "DUPLICATE_FINISH");
|
|
3120
2951
|
return;
|
|
3121
2952
|
}
|
|
3122
|
-
completedEmitted = true;
|
|
3123
2953
|
const replay = replayFromOutput(output);
|
|
3124
2954
|
if (accumulatedContent || pendingToolCalls.length > 0) replay.push(opaqueItem("ollama", "replay", {
|
|
3125
2955
|
role: "assistant",
|
|
@@ -3128,118 +2958,86 @@ var OllamaAdapter = class extends AdapterBase {
|
|
|
3128
2958
|
id: tc.id,
|
|
3129
2959
|
function: {
|
|
3130
2960
|
name: tc.name,
|
|
3131
|
-
arguments: tc.
|
|
2961
|
+
arguments: JSON.parse(tc.argumentsText)
|
|
3132
2962
|
}
|
|
3133
2963
|
}))
|
|
3134
2964
|
}));
|
|
3135
|
-
|
|
3136
|
-
for (const event of auxiliaryResult.events) yield event;
|
|
3137
|
-
const finalResponse = buildResponse(request, {
|
|
2965
|
+
yield* this.emitStreamCompleted(factory, request, auxiliary, {
|
|
3138
2966
|
output,
|
|
3139
2967
|
replay,
|
|
3140
2968
|
stopReason,
|
|
3141
|
-
usage: auxiliaryResult.usage,
|
|
3142
|
-
billing: auxiliaryResult.billing,
|
|
3143
|
-
auxiliary: auxiliaryResult.auxiliary,
|
|
3144
|
-
warnings: auxiliaryResult.warnings,
|
|
3145
|
-
metadataSources: auxiliaryResult.metadataSources,
|
|
3146
2969
|
rawResponseId
|
|
3147
|
-
}, factory);
|
|
3148
|
-
yield factory.responseCompleted({
|
|
3149
|
-
replay: finalResponse.replay,
|
|
3150
|
-
stopReason: finalResponse.stopReason,
|
|
3151
|
-
trace: finalResponse.backend,
|
|
3152
|
-
usage: finalResponse.usage,
|
|
3153
|
-
billing: finalResponse.billing,
|
|
3154
|
-
auxiliary: finalResponse.auxiliary,
|
|
3155
|
-
warnings: finalResponse.warnings
|
|
3156
2970
|
});
|
|
3157
|
-
};
|
|
3158
|
-
|
|
3159
|
-
|
|
3160
|
-
|
|
3161
|
-
|
|
3162
|
-
|
|
3163
|
-
|
|
3164
|
-
|
|
3165
|
-
|
|
3166
|
-
|
|
3167
|
-
|
|
3168
|
-
|
|
3169
|
-
if (
|
|
3170
|
-
|
|
3171
|
-
|
|
3172
|
-
|
|
3173
|
-
|
|
3174
|
-
|
|
2971
|
+
}.bind(this);
|
|
2972
|
+
for await (const batch of iterateProviderStreamBatches({
|
|
2973
|
+
reader,
|
|
2974
|
+
parser,
|
|
2975
|
+
factory,
|
|
2976
|
+
providerLabel: "Ollama",
|
|
2977
|
+
transportLabel: "NDJSON line(s)",
|
|
2978
|
+
incompleteMessage: "Stream ended with an incomplete Ollama NDJSON line"
|
|
2979
|
+
})) {
|
|
2980
|
+
for (const warning of batch.warnings) yield warning;
|
|
2981
|
+
for (const chunk of batch.items) {
|
|
2982
|
+
responseId = chunk.created_at;
|
|
2983
|
+
if (gate.completed) {
|
|
2984
|
+
if (chunk.done) yield factory.responseWarning("Duplicate finish signal ignored", "DUPLICATE_FINISH");
|
|
2985
|
+
continue;
|
|
2986
|
+
}
|
|
2987
|
+
const msg = chunk.message;
|
|
2988
|
+
if (msg.content) {
|
|
2989
|
+
if (!hasMessageStarted) {
|
|
2990
|
+
currentMessageId = `msg-${chunk.created_at}`;
|
|
2991
|
+
hasMessageStarted = true;
|
|
2992
|
+
yield factory.messageStarted(currentMessageId);
|
|
3175
2993
|
}
|
|
3176
|
-
|
|
3177
|
-
|
|
3178
|
-
|
|
3179
|
-
|
|
3180
|
-
|
|
3181
|
-
|
|
3182
|
-
|
|
3183
|
-
|
|
3184
|
-
|
|
2994
|
+
accumulatedContent += msg.content;
|
|
2995
|
+
yield factory.messageDelta(currentMessageId, textBlock(msg.content));
|
|
2996
|
+
}
|
|
2997
|
+
if (msg.tool_calls && msg.tool_calls.length > 0) for (const tc of msg.tool_calls) {
|
|
2998
|
+
const tcId = `ollama-tc-${request.requestId}-${toolCallIndex++}`;
|
|
2999
|
+
const argsText = JSON.stringify(tc.function.arguments);
|
|
3000
|
+
pendingToolCalls.push({
|
|
3001
|
+
id: tcId,
|
|
3002
|
+
name: tc.function.name,
|
|
3003
|
+
argumentsText: argsText
|
|
3004
|
+
});
|
|
3005
|
+
}
|
|
3006
|
+
if (chunk.done) {
|
|
3007
|
+
if (accumulatedContent === "" && pendingToolCalls.length > 0 && !hasMessageStarted) {
|
|
3008
|
+
currentMessageId = `msg-${chunk.created_at}`;
|
|
3009
|
+
hasMessageStarted = true;
|
|
3010
|
+
yield factory.messageStarted(currentMessageId);
|
|
3185
3011
|
}
|
|
3186
|
-
if (
|
|
3187
|
-
const
|
|
3188
|
-
|
|
3189
|
-
|
|
3190
|
-
id: tcId,
|
|
3191
|
-
name: tc.function.name,
|
|
3192
|
-
argumentsText: argsText,
|
|
3193
|
-
argumentsJson: tc.function.arguments
|
|
3194
|
-
});
|
|
3012
|
+
if (hasMessageStarted) {
|
|
3013
|
+
const message = messageItem([textBlock(accumulatedContent)], { id: currentMessageId });
|
|
3014
|
+
yield factory.messageCompleted(currentMessageId);
|
|
3015
|
+
if (accumulatedContent) output.push(message);
|
|
3195
3016
|
}
|
|
3196
|
-
if (
|
|
3197
|
-
|
|
3198
|
-
|
|
3199
|
-
|
|
3200
|
-
|
|
3201
|
-
|
|
3202
|
-
|
|
3203
|
-
const message = messageItem([textBlock(accumulatedContent)], { id: currentMessageId });
|
|
3204
|
-
yield factory.messageCompleted(currentMessageId);
|
|
3205
|
-
if (accumulatedContent) output.push(message);
|
|
3206
|
-
}
|
|
3207
|
-
if (pendingToolCalls.length > 0) yield factory.responseWarning(`Ollama delivered ${pendingToolCalls.length} tool call(s) as a batch; tool_call streaming is not supported`, WarningCode.TOOL_CALL_BATCHED);
|
|
3208
|
-
for (const pending of pendingToolCalls) {
|
|
3209
|
-
const toolCall = toolCallItem(pending.id, pending.name, pending.argumentsText, pending.argumentsJson);
|
|
3210
|
-
yield factory.toolCallStarted(pending.id, pending.name);
|
|
3211
|
-
yield factory.toolCallDelta(pending.id, { argumentsText: pending.argumentsText });
|
|
3212
|
-
yield factory.toolCallCompleted(pending.id);
|
|
3213
|
-
output.push(toolCall);
|
|
3214
|
-
}
|
|
3215
|
-
if (request.include?.usage !== "off" && (chunk.prompt_eval_count !== void 0 || chunk.eval_count !== void 0)) auxiliary.recordUsage(usageFromOllama({
|
|
3216
|
-
prompt_eval_count: chunk.prompt_eval_count,
|
|
3217
|
-
eval_count: chunk.eval_count
|
|
3218
|
-
}), "final", {
|
|
3219
|
-
prompt_eval_count: chunk.prompt_eval_count,
|
|
3220
|
-
eval_count: chunk.eval_count
|
|
3221
|
-
});
|
|
3222
|
-
yield* emitCompleted(chunk.done_reason ? mapStopReason(chunk.done_reason) : void 0, chunk.created_at);
|
|
3223
|
-
accumulatedContent = "";
|
|
3224
|
-
currentMessageId = "";
|
|
3225
|
-
hasMessageStarted = false;
|
|
3226
|
-
pendingToolCalls = [];
|
|
3017
|
+
if (pendingToolCalls.length > 0) yield factory.responseWarning(`Ollama delivered ${pendingToolCalls.length} tool call(s) as a batch; tool_call streaming is not supported`, WarningCode.TOOL_CALL_BATCHED);
|
|
3018
|
+
for (const pending of pendingToolCalls) {
|
|
3019
|
+
const toolCall = toolCallItem(pending.id, pending.name, pending.argumentsText);
|
|
3020
|
+
yield factory.toolCallStarted(pending.id, pending.name);
|
|
3021
|
+
yield factory.toolCallDelta(pending.id, { argumentsText: pending.argumentsText });
|
|
3022
|
+
yield factory.toolCallCompleted(pending.id);
|
|
3023
|
+
output.push(toolCall);
|
|
3227
3024
|
}
|
|
3228
|
-
|
|
3229
|
-
|
|
3230
|
-
|
|
3231
|
-
|
|
3025
|
+
if (request.include?.usage !== "off" && (chunk.prompt_eval_count !== void 0 || chunk.eval_count !== void 0)) auxiliary.recordUsage(usageFromOllama({
|
|
3026
|
+
prompt_eval_count: chunk.prompt_eval_count,
|
|
3027
|
+
eval_count: chunk.eval_count
|
|
3028
|
+
}), "final", {
|
|
3029
|
+
prompt_eval_count: chunk.prompt_eval_count,
|
|
3030
|
+
eval_count: chunk.eval_count
|
|
3031
|
+
});
|
|
3032
|
+
yield* emitCompleted(chunk.done_reason ? mapStopReason(chunk.done_reason) : void 0, chunk.created_at);
|
|
3033
|
+
accumulatedContent = "";
|
|
3034
|
+
currentMessageId = "";
|
|
3035
|
+
hasMessageStarted = false;
|
|
3036
|
+
pendingToolCalls = [];
|
|
3232
3037
|
}
|
|
3233
3038
|
}
|
|
3234
|
-
} finally {
|
|
3235
|
-
try {
|
|
3236
|
-
if (!streamDone) await reader.cancel().catch(() => void 0);
|
|
3237
|
-
} finally {
|
|
3238
|
-
reader.releaseLock();
|
|
3239
|
-
}
|
|
3240
3039
|
}
|
|
3241
|
-
if (
|
|
3242
|
-
if (!completedEmitted && (hasMessageStarted || pendingToolCalls.length > 0)) {
|
|
3040
|
+
if (!gate.completed && (hasMessageStarted || pendingToolCalls.length > 0)) {
|
|
3243
3041
|
yield factory.responseWarning("Stream ended without a done signal", "INCOMPLETE_STREAM");
|
|
3244
3042
|
if (hasMessageStarted) {
|
|
3245
3043
|
const message = messageItem([textBlock(accumulatedContent)], { id: currentMessageId });
|
|
@@ -3248,7 +3046,7 @@ var OllamaAdapter = class extends AdapterBase {
|
|
|
3248
3046
|
}
|
|
3249
3047
|
if (pendingToolCalls.length > 0) yield factory.responseWarning(`Ollama delivered ${pendingToolCalls.length} tool call(s) as a batch; tool_call streaming is not supported`, WarningCode.TOOL_CALL_BATCHED);
|
|
3250
3048
|
for (const pending of pendingToolCalls) {
|
|
3251
|
-
const toolCall = toolCallItem(pending.id, pending.name, pending.argumentsText
|
|
3049
|
+
const toolCall = toolCallItem(pending.id, pending.name, pending.argumentsText);
|
|
3252
3050
|
yield factory.toolCallStarted(pending.id, pending.name);
|
|
3253
3051
|
yield factory.toolCallDelta(pending.id, { argumentsText: pending.argumentsText });
|
|
3254
3052
|
yield factory.toolCallCompleted(pending.id);
|
|
@@ -3289,18 +3087,7 @@ function assertMockRequest(request, expectation, context) {
|
|
|
3289
3087
|
}
|
|
3290
3088
|
var MockAdapter = class extends AdapterBase {
|
|
3291
3089
|
kind = "mock";
|
|
3292
|
-
|
|
3293
|
-
textStreaming: "synthetic",
|
|
3294
|
-
reasoningStreaming: "synthetic",
|
|
3295
|
-
toolCallStreaming: "synthetic",
|
|
3296
|
-
replay: "canonical",
|
|
3297
|
-
usage: "final",
|
|
3298
|
-
toolResultOutcomes: [
|
|
3299
|
-
"success",
|
|
3300
|
-
"error",
|
|
3301
|
-
"rejected"
|
|
3302
|
-
]
|
|
3303
|
-
};
|
|
3090
|
+
isSyntheticStream = true;
|
|
3304
3091
|
handler;
|
|
3305
3092
|
providerMetadata;
|
|
3306
3093
|
cursor = 0;
|
|
@@ -3508,8 +3295,7 @@ function createToolCallFromStep(step) {
|
|
|
3508
3295
|
type: "tool_call",
|
|
3509
3296
|
id: step.id,
|
|
3510
3297
|
name: step.name,
|
|
3511
|
-
argumentsText: step.argumentsText
|
|
3512
|
-
argumentsJson: step.argumentsJson
|
|
3298
|
+
argumentsText: step.argumentsText
|
|
3513
3299
|
};
|
|
3514
3300
|
}
|
|
3515
3301
|
function normalizeBlocks(content) {
|
|
@@ -3691,6 +3477,6 @@ function cloneItem(item) {
|
|
|
3691
3477
|
return structuredClone(item);
|
|
3692
3478
|
}
|
|
3693
3479
|
//#endregion
|
|
3694
|
-
export { AIError, AIMappingError, AIProviderError, AIRequestError, AIStreamError, AdapterAuxiliaryState, AdapterBase, AuxiliaryCollector, ChatCompletionsAdapter, IncrementalStreamParser, MAX_OPAQUE_JSON_DEPTH, MAX_OPAQUE_PAYLOAD_BYTES, MessagesAdapter, MockAdapter, NormalizedRequestMapper, OllamaAdapter, PROVIDER_ERROR_MESSAGE_MAX_LEN, PROVIDER_ERROR_RAW_BODY_THRESHOLD, ResponsesAdapter, WarningCode, aggregateEvents, assertMockRequest, assertOpaqueReplayEnvelope, assertValidRequest, blockToText, collectStream, contentBlocksToText, createAIClient, createEventFactory, emitMalformedStreamWarning, extractProviderErrorMessage, extractText, imageBlock,
|
|
3480
|
+
export { AIError, AIMappingError, AIProviderError, AIRequestError, AIStreamError, AdapterAuxiliaryState, AdapterBase, AuxiliaryCollector, ChatCompletionsAdapter, IncrementalStreamParser, MAX_OPAQUE_JSON_DEPTH, MAX_OPAQUE_PAYLOAD_BYTES, MessagesAdapter, MockAdapter, NormalizedRequestMapper, OllamaAdapter, PROVIDER_ERROR_MESSAGE_MAX_LEN, PROVIDER_ERROR_RAW_BODY_THRESHOLD, ResponsesAdapter, WarningCode, aggregateEvents, assertMockRequest, assertOpaqueReplayEnvelope, assertValidRequest, blockToText, collectStream, contentBlocksToText, createAIClient, createChatCompletionsSseParser, createCompletionGate, createEventFactory, createNdjsonLineParser, createSseJsonParser, emitMalformedStreamWarning, extractProviderErrorMessage, extractText, imageBlock, iterateProviderStreamBatches, jsonBlock, mapReasoningVisibility, mapStopReason, measureJsonDepth, messageItem, normalizeRequest, opaqueBlock, opaqueItem, openProviderJsonStream, parseChatCompletionsDataLine, parseSseJsonFrame, providerHttpError, reasoningItem, replayFromOutput, splitLines, splitSSEFrames, syntheticStream, textBlock, toolCallItem, toolResultItem, usageFromAnthropicMessages, usageFromChatCompletions, usageFromOllama, usageFromOpenAIResponses, validateOpaqueReplayEnvelope, validateRequest, withMockStreaming };
|
|
3695
3481
|
|
|
3696
3482
|
//# sourceMappingURL=index.mjs.map
|