@codehz/ai 0.3.0 → 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/dist/index.d.mts +68 -53
- package/dist/index.mjs +671 -813
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/adapters/chat-completions.ts +150 -244
- package/src/adapters/messages.ts +171 -274
- package/src/adapters/ollama.ts +118 -205
- package/src/adapters/responses.ts +135 -226
- package/src/helpers/adapter-auxiliary.ts +1 -23
- package/src/helpers/adapter-base.ts +38 -4
- package/src/helpers/incremental-stream-parser.ts +58 -0
- package/src/helpers/index.ts +20 -7
- package/src/helpers/mapping.ts +0 -8
- package/src/helpers/provider-stream.ts +147 -0
- package/src/helpers/request-mapper.ts +30 -1
- package/src/helpers/usage-mapping.ts +36 -39
- package/src/helpers/sse-parser.ts +0 -113
package/dist/index.mjs
CHANGED
|
@@ -883,12 +883,6 @@ function contentBlocksToText(blocks) {
|
|
|
883
883
|
return blocks.map(blockToText).join("\n");
|
|
884
884
|
}
|
|
885
885
|
/**
|
|
886
|
-
* 将 instructions(string | InstructionBlock[])归一化为纯文本。
|
|
887
|
-
*/
|
|
888
|
-
function instructionsToText(instructions) {
|
|
889
|
-
return typeof instructions === "string" ? instructions : contentBlocksToText(instructions);
|
|
890
|
-
}
|
|
891
|
-
/**
|
|
892
886
|
* 从 OutputItem 数组中提取所有 message 类型 item 的文本内容。
|
|
893
887
|
*/
|
|
894
888
|
function extractText(output) {
|
|
@@ -1074,14 +1068,6 @@ function emitMalformedStreamWarning(factory, options) {
|
|
|
1074
1068
|
if (options.count < 1) return void 0;
|
|
1075
1069
|
return factory.responseWarning(`Skipped ${options.count} malformed ${options.providerLabel} ${options.transportLabel}`, "STREAM_ERROR");
|
|
1076
1070
|
}
|
|
1077
|
-
function metadataSourceList(...groups) {
|
|
1078
|
-
const sources = /* @__PURE__ */ new Set();
|
|
1079
|
-
for (const group of groups) {
|
|
1080
|
-
if (!group) continue;
|
|
1081
|
-
for (const source of group) sources.add(source);
|
|
1082
|
-
}
|
|
1083
|
-
return sources.size > 0 ? [...sources] : void 0;
|
|
1084
|
-
}
|
|
1085
1071
|
function isEmptyRecord(value) {
|
|
1086
1072
|
return Object.keys(value).length === 0;
|
|
1087
1073
|
}
|
|
@@ -1134,7 +1120,7 @@ var AdapterBase = class {
|
|
|
1134
1120
|
* 子类可在返回前自定义覆盖。
|
|
1135
1121
|
*/
|
|
1136
1122
|
buildResponse(request, result, _factory) {
|
|
1137
|
-
const text =
|
|
1123
|
+
const text = extractText(result.output);
|
|
1138
1124
|
const warnings = mergeWarnings(result.warnings, _factory.warnings);
|
|
1139
1125
|
const auxiliary = mergeAuxiliary(result.auxiliary, result.providerMetadata ? { providerMetadata: result.providerMetadata } : void 0);
|
|
1140
1126
|
return {
|
|
@@ -1158,9 +1144,30 @@ var AdapterBase = class {
|
|
|
1158
1144
|
}
|
|
1159
1145
|
};
|
|
1160
1146
|
}
|
|
1161
|
-
/**
|
|
1162
|
-
|
|
1163
|
-
|
|
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
|
+
});
|
|
1164
1171
|
}
|
|
1165
1172
|
createAuxiliaryState(request) {
|
|
1166
1173
|
return new AdapterAuxiliaryState(request);
|
|
@@ -1269,32 +1276,35 @@ function record(obj) {
|
|
|
1269
1276
|
for (const [key, value] of Object.entries(obj)) if (value !== void 0) out[key] = value;
|
|
1270
1277
|
return out;
|
|
1271
1278
|
}
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
const inputTokens = num(raw.prompt_tokens);
|
|
1275
|
-
const outputTokens = num(raw.completion_tokens);
|
|
1276
|
-
const cachedInputTokens = num(raw.prompt_tokens_details?.cached_tokens);
|
|
1277
|
-
const reasoningTokens = num(raw.completion_tokens_details?.reasoning_tokens);
|
|
1279
|
+
function withDerivedTotal(usage) {
|
|
1280
|
+
const { inputTokens, outputTokens, totalTokens, cachedInputTokens, reasoningTokens, cacheWriteInputTokens } = usage;
|
|
1278
1281
|
return record({
|
|
1279
1282
|
inputTokens,
|
|
1280
1283
|
outputTokens,
|
|
1281
|
-
totalTokens:
|
|
1284
|
+
totalTokens: totalTokens ?? (inputTokens !== void 0 && outputTokens !== void 0 ? inputTokens + outputTokens : void 0),
|
|
1282
1285
|
cachedInputTokens,
|
|
1283
|
-
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)
|
|
1284
1298
|
});
|
|
1285
1299
|
}
|
|
1286
1300
|
/** OpenAI Responses API `usage` */
|
|
1287
1301
|
function usageFromOpenAIResponses(raw) {
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
outputTokens,
|
|
1295
|
-
totalTokens: num(raw.total_tokens) ?? (inputTokens !== void 0 && outputTokens !== void 0 ? inputTokens + outputTokens : void 0),
|
|
1296
|
-
cachedInputTokens,
|
|
1297
|
-
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)
|
|
1298
1308
|
});
|
|
1299
1309
|
}
|
|
1300
1310
|
/** Anthropic Messages `usage`(message_start / message_delta) */
|
|
@@ -1308,93 +1318,21 @@ function usageFromAnthropicMessages(raw) {
|
|
|
1308
1318
|
cacheWriteInputTokens,
|
|
1309
1319
|
cachedInputTokens
|
|
1310
1320
|
].filter((n) => n !== void 0);
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
inputTokens,
|
|
1321
|
+
return withDerivedTotal({
|
|
1322
|
+
inputTokens: inputParts.length > 0 ? inputParts.reduce((sum, n) => sum + n, 0) : void 0,
|
|
1314
1323
|
outputTokens,
|
|
1315
|
-
totalTokens: inputTokens !== void 0 && outputTokens !== void 0 ? inputTokens + outputTokens : void 0,
|
|
1316
1324
|
cachedInputTokens,
|
|
1317
1325
|
cacheWriteInputTokens
|
|
1318
1326
|
});
|
|
1319
1327
|
}
|
|
1320
1328
|
/** Ollama 流式 chunk */
|
|
1321
1329
|
function usageFromOllama(raw) {
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
inputTokens,
|
|
1326
|
-
outputTokens,
|
|
1327
|
-
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)
|
|
1328
1333
|
});
|
|
1329
1334
|
}
|
|
1330
1335
|
//#endregion
|
|
1331
|
-
//#region src/helpers/sse-parser.ts
|
|
1332
|
-
/**
|
|
1333
|
-
* 将 SSE 文本块解析为事件数组。
|
|
1334
|
-
* 累积事件行直到遇到空行,支持 [DONE] 标记。
|
|
1335
|
-
* 返回已解析的事件和未处理的剩余 buffer(用于增量解析)。
|
|
1336
|
-
*
|
|
1337
|
-
* 关键行为:
|
|
1338
|
-
* - 只解析完整的 event(以空行结尾)
|
|
1339
|
-
* - 未完成的行保留在 rest 中,等待下次 chunk 补全
|
|
1340
|
-
* - 支持跨 chunk 的 event 分片
|
|
1341
|
-
*/
|
|
1342
|
-
function parseSSEEvents(chunk, options = {}) {
|
|
1343
|
-
const events = [];
|
|
1344
|
-
let eventType = "";
|
|
1345
|
-
let dataLines = [];
|
|
1346
|
-
let consumedUntil = 0;
|
|
1347
|
-
let cursor = 0;
|
|
1348
|
-
let malformedEvents = 0;
|
|
1349
|
-
const emitEvent = (consumedCursor) => {
|
|
1350
|
-
const dataStr = dataLines.join("\n");
|
|
1351
|
-
if (dataStr === "[DONE]") {
|
|
1352
|
-
eventType = "";
|
|
1353
|
-
dataLines = [];
|
|
1354
|
-
consumedUntil = consumedCursor;
|
|
1355
|
-
return;
|
|
1356
|
-
}
|
|
1357
|
-
try {
|
|
1358
|
-
const data = JSON.parse(dataStr);
|
|
1359
|
-
events.push({
|
|
1360
|
-
type: eventType,
|
|
1361
|
-
data
|
|
1362
|
-
});
|
|
1363
|
-
} catch {
|
|
1364
|
-
malformedEvents++;
|
|
1365
|
-
}
|
|
1366
|
-
eventType = "";
|
|
1367
|
-
dataLines = [];
|
|
1368
|
-
consumedUntil = consumedCursor;
|
|
1369
|
-
};
|
|
1370
|
-
const consumeLine = (line, consumedCursor) => {
|
|
1371
|
-
if (line.startsWith("event: ")) eventType = line.slice(7).trim();
|
|
1372
|
-
else if (line.startsWith("data: ")) dataLines.push(line.slice(6));
|
|
1373
|
-
else if (line === "" && eventType && dataLines.length > 0) emitEvent(consumedCursor);
|
|
1374
|
-
else if (line === "" && !eventType && dataLines.length === 0) consumedUntil = consumedCursor;
|
|
1375
|
-
};
|
|
1376
|
-
while (cursor < chunk.length) {
|
|
1377
|
-
const lineEnd = chunk.indexOf("\n", cursor);
|
|
1378
|
-
if (lineEnd === -1) break;
|
|
1379
|
-
let line = chunk.slice(cursor, lineEnd);
|
|
1380
|
-
cursor = lineEnd + 1;
|
|
1381
|
-
if (line.endsWith("\r")) line = line.slice(0, -1);
|
|
1382
|
-
consumeLine(line, cursor);
|
|
1383
|
-
}
|
|
1384
|
-
if (options.allowEOF && cursor < chunk.length) {
|
|
1385
|
-
let line = chunk.slice(cursor);
|
|
1386
|
-
if (line.endsWith("\r")) line = line.slice(0, -1);
|
|
1387
|
-
consumeLine(line, chunk.length);
|
|
1388
|
-
cursor = chunk.length;
|
|
1389
|
-
}
|
|
1390
|
-
if (options.allowEOF && eventType && dataLines.length > 0) emitEvent(chunk.length);
|
|
1391
|
-
return {
|
|
1392
|
-
events,
|
|
1393
|
-
rest: chunk.slice(consumedUntil),
|
|
1394
|
-
malformedEvents
|
|
1395
|
-
};
|
|
1396
|
-
}
|
|
1397
|
-
//#endregion
|
|
1398
1336
|
//#region src/helpers/synthetic-stream.ts
|
|
1399
1337
|
/**
|
|
1400
1338
|
* 模拟流式 (Synthetic Streaming)
|
|
@@ -1568,6 +1506,159 @@ function splitSSEFrames(buffer, allowEOF) {
|
|
|
1568
1506
|
rest: normalized.slice(cursor)
|
|
1569
1507
|
};
|
|
1570
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
|
+
}
|
|
1571
1662
|
//#endregion
|
|
1572
1663
|
//#region src/helpers/request-mapper.ts
|
|
1573
1664
|
var NormalizedRequestMapper = class {
|
|
@@ -1584,6 +1675,10 @@ var NormalizedRequestMapper = class {
|
|
|
1584
1675
|
ensureReasoningBlocks(blocks, field) {
|
|
1585
1676
|
return this.ensureBlocks(blocks, field, ["text"], "reasoning only supports text blocks");
|
|
1586
1677
|
}
|
|
1678
|
+
/** ensureTextBlocks + contentBlocksToText 的常见组合。 */
|
|
1679
|
+
textFromBlocks(blocks, field) {
|
|
1680
|
+
return contentBlocksToText(this.ensureTextBlocks(blocks, field));
|
|
1681
|
+
}
|
|
1587
1682
|
parseToolArguments(item) {
|
|
1588
1683
|
try {
|
|
1589
1684
|
const parsed = JSON.parse(item.argumentsText);
|
|
@@ -1594,6 +1689,20 @@ var NormalizedRequestMapper = class {
|
|
|
1594
1689
|
rollbackTrailingAssistantMessages(messages) {
|
|
1595
1690
|
while (messages.length > 0 && messages[messages.length - 1]?.role === "assistant") messages.pop();
|
|
1596
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
|
+
}
|
|
1597
1706
|
ensureBlocks(blocks, field, supportedTypes, description) {
|
|
1598
1707
|
for (let i = 0; i < blocks.length; i++) {
|
|
1599
1708
|
const block = blocks[i];
|
|
@@ -1682,7 +1791,7 @@ var ResponsesAdapter = class extends AdapterBase {
|
|
|
1682
1791
|
} else input.push({
|
|
1683
1792
|
type: "message",
|
|
1684
1793
|
role: item.role,
|
|
1685
|
-
content:
|
|
1794
|
+
content: mapper$3.textFromBlocks(item.content, `input message (${item.role}) content`)
|
|
1686
1795
|
});
|
|
1687
1796
|
break;
|
|
1688
1797
|
case "reasoning": {
|
|
@@ -1705,7 +1814,7 @@ var ResponsesAdapter = class extends AdapterBase {
|
|
|
1705
1814
|
});
|
|
1706
1815
|
break;
|
|
1707
1816
|
case "tool_result": {
|
|
1708
|
-
const output = mapper$3.
|
|
1817
|
+
const output = mapper$3.textFromBlocks(item.content, `tool_result ${item.callId} content`);
|
|
1709
1818
|
input.push({
|
|
1710
1819
|
type: "function_call_output",
|
|
1711
1820
|
call_id: item.callId,
|
|
@@ -1733,20 +1842,20 @@ var ResponsesAdapter = class extends AdapterBase {
|
|
|
1733
1842
|
stream: true
|
|
1734
1843
|
};
|
|
1735
1844
|
if (request.instructions) body.instructions = mapper$3.mapInstructions(request.instructions);
|
|
1736
|
-
|
|
1845
|
+
body.tools = mapper$3.mapToolsIfPresent(request.tools, (t) => ({
|
|
1737
1846
|
type: "function",
|
|
1738
1847
|
name: t.name,
|
|
1739
1848
|
description: t.description,
|
|
1740
1849
|
input_schema: t.inputSchema
|
|
1741
1850
|
}));
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1851
|
+
body.tool_choice = mapper$3.mapToolChoice(request.toolChoice, {
|
|
1852
|
+
auto: "auto",
|
|
1853
|
+
none: "none",
|
|
1854
|
+
tool: (name) => ({
|
|
1746
1855
|
type: "function",
|
|
1747
|
-
name
|
|
1748
|
-
}
|
|
1749
|
-
}
|
|
1856
|
+
name
|
|
1857
|
+
})
|
|
1858
|
+
});
|
|
1750
1859
|
if (request.temperature !== void 0) body.temperature = request.temperature;
|
|
1751
1860
|
if (request.maxOutputTokens !== void 0) body.max_output_tokens = request.maxOutputTokens;
|
|
1752
1861
|
if (request.metadata) body.metadata = request.metadata;
|
|
@@ -1754,155 +1863,108 @@ var ResponsesAdapter = class extends AdapterBase {
|
|
|
1754
1863
|
}
|
|
1755
1864
|
async *runStream(providerRequest, factory, request) {
|
|
1756
1865
|
const auxiliary = this.createAuxiliaryState(request);
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
});
|
|
1768
|
-
} catch (err) {
|
|
1769
|
-
throw new AIProviderError(err instanceof Error ? err.message : String(err), "PROVIDER_ERROR");
|
|
1770
|
-
}
|
|
1771
|
-
if (!response.ok) {
|
|
1772
|
-
const errorBody = await response.text().catch(() => "");
|
|
1773
|
-
throw providerHttpError(response.status, errorBody);
|
|
1774
|
-
}
|
|
1775
|
-
const reader = response.body?.getReader();
|
|
1776
|
-
if (!reader) throw new AIStreamError("Response body is not readable", "STREAM_ERROR");
|
|
1777
|
-
const parser = new IncrementalStreamParser(splitSSEFrames, (frame) => {
|
|
1778
|
-
let eventType = "";
|
|
1779
|
-
let dataStr = "";
|
|
1780
|
-
for (const rawLine of frame.split("\n")) {
|
|
1781
|
-
const line = rawLine.trim();
|
|
1782
|
-
if (line.startsWith("event: ")) eventType = line.slice(7).trim();
|
|
1783
|
-
else if (line.startsWith("data: ")) dataStr += line.slice(6);
|
|
1784
|
-
}
|
|
1785
|
-
if (!eventType) return { status: "ignored" };
|
|
1786
|
-
try {
|
|
1787
|
-
const data = JSON.parse(dataStr);
|
|
1788
|
-
return {
|
|
1789
|
-
status: "parsed",
|
|
1790
|
-
value: {
|
|
1791
|
-
type: eventType,
|
|
1792
|
-
data
|
|
1793
|
-
}
|
|
1794
|
-
};
|
|
1795
|
-
} catch {
|
|
1796
|
-
return { status: "malformed" };
|
|
1797
|
-
}
|
|
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
|
|
1798
1876
|
});
|
|
1877
|
+
const parser = createSseJsonParser();
|
|
1799
1878
|
const output = [];
|
|
1800
|
-
let streamDone = false;
|
|
1801
1879
|
let completedResponse;
|
|
1802
|
-
let completedEmitted = false;
|
|
1803
1880
|
let unknownEventsWarned = false;
|
|
1804
1881
|
const messageItemsWithDelta = /* @__PURE__ */ new Set();
|
|
1805
1882
|
const toolCallNames = /* @__PURE__ */ new Map();
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
toolCallNames.set(item.id, name);
|
|
1836
|
-
yield factory.toolCallStarted(item.id, name);
|
|
1837
|
-
break;
|
|
1838
|
-
}
|
|
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;
|
|
1839
1912
|
}
|
|
1840
|
-
continue;
|
|
1841
|
-
}
|
|
1842
|
-
if (sseEvent.type === "response.output_text.delta") {
|
|
1843
|
-
const data = sseEvent.data;
|
|
1844
|
-
yield factory.messageDelta(data.item_id, textBlock(data.delta));
|
|
1845
|
-
messageItemsWithDelta.add(data.item_id);
|
|
1846
|
-
continue;
|
|
1847
|
-
}
|
|
1848
|
-
if (sseEvent.type === "response.output_text.done") {
|
|
1849
|
-
const data = sseEvent.data;
|
|
1850
|
-
if (!messageItemsWithDelta.has(data.item_id) && data.text) yield factory.messageDelta(data.item_id, textBlock(data.text));
|
|
1851
|
-
yield factory.messageCompleted(data.item_id);
|
|
1852
|
-
output.push(messageItem([textBlock(data.text)], { id: data.item_id }));
|
|
1853
|
-
continue;
|
|
1854
|
-
}
|
|
1855
|
-
if (sseEvent.type === "response.reasoning.delta") {
|
|
1856
|
-
const data = sseEvent.data;
|
|
1857
|
-
yield factory.reasoningDelta(data.item_id, textBlock(data.delta));
|
|
1858
|
-
continue;
|
|
1859
|
-
}
|
|
1860
|
-
if (sseEvent.type === "response.reasoning.done") {
|
|
1861
|
-
const data = sseEvent.data;
|
|
1862
|
-
yield factory.reasoningCompleted(data.item_id);
|
|
1863
|
-
output.push(reasoningItem([textBlock(data.text)], "full", data.item_id));
|
|
1864
|
-
continue;
|
|
1865
1913
|
}
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
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");
|
|
1886
1956
|
continue;
|
|
1887
1957
|
}
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
}
|
|
1958
|
+
completedResponse = data.response;
|
|
1959
|
+
if (sseEvent.type === "response.failed") yield factory.responseWarning(`Response failed: ${extractFailureMessage(data.response)}`, "PROVIDER_FAILURE");
|
|
1960
|
+
continue;
|
|
1892
1961
|
}
|
|
1893
|
-
if (
|
|
1894
|
-
|
|
1895
|
-
|
|
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");
|
|
1896
1965
|
}
|
|
1897
1966
|
}
|
|
1898
|
-
} finally {
|
|
1899
|
-
try {
|
|
1900
|
-
if (!streamDone) await reader.cancel().catch(() => void 0);
|
|
1901
|
-
} finally {
|
|
1902
|
-
reader.releaseLock();
|
|
1903
|
-
}
|
|
1904
1967
|
}
|
|
1905
|
-
if (parser.getRemaining().trim().length > 0) yield factory.responseWarning("Stream ended with an incomplete Responses SSE frame", "STREAM_ERROR");
|
|
1906
1968
|
let rawResponseId;
|
|
1907
1969
|
if (completedResponse) {
|
|
1908
1970
|
rawResponseId = completedResponse.id;
|
|
@@ -1911,31 +1973,12 @@ var ResponsesAdapter = class extends AdapterBase {
|
|
|
1911
1973
|
const replay = [...replayFromOutput(output)];
|
|
1912
1974
|
if (completedResponse?.id) replay.push(opaqueItem("responses", "replay", { id: completedResponse.id }));
|
|
1913
1975
|
const stopReason = completedResponse ? this.inferStopReason(completedResponse) : void 0;
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
replay,
|
|
1921
|
-
stopReason,
|
|
1922
|
-
usage: auxiliaryResult.usage,
|
|
1923
|
-
billing: auxiliaryResult.billing,
|
|
1924
|
-
auxiliary: auxiliaryResult.auxiliary,
|
|
1925
|
-
warnings: auxiliaryResult.warnings,
|
|
1926
|
-
metadataSources: auxiliaryResult.metadataSources,
|
|
1927
|
-
rawResponseId
|
|
1928
|
-
}, factory);
|
|
1929
|
-
yield factory.responseCompleted({
|
|
1930
|
-
replay: finalResponse.replay,
|
|
1931
|
-
stopReason: finalResponse.stopReason,
|
|
1932
|
-
trace: finalResponse.backend,
|
|
1933
|
-
usage: finalResponse.usage,
|
|
1934
|
-
billing: finalResponse.billing,
|
|
1935
|
-
auxiliary: finalResponse.auxiliary,
|
|
1936
|
-
warnings: finalResponse.warnings
|
|
1937
|
-
});
|
|
1938
|
-
}
|
|
1976
|
+
if (gate.tryComplete()) yield* this.emitStreamCompleted(factory, request, auxiliary, {
|
|
1977
|
+
output,
|
|
1978
|
+
replay,
|
|
1979
|
+
stopReason,
|
|
1980
|
+
rawResponseId
|
|
1981
|
+
});
|
|
1939
1982
|
}
|
|
1940
1983
|
inferStopReason(response) {
|
|
1941
1984
|
if (response.status === "failed") return "error";
|
|
@@ -2086,7 +2129,7 @@ var MessagesAdapter = class extends AdapterBase {
|
|
|
2086
2129
|
break;
|
|
2087
2130
|
}
|
|
2088
2131
|
case "tool_result": {
|
|
2089
|
-
const content = mapper$2.
|
|
2132
|
+
const content = mapper$2.textFromBlocks(item.content, `tool_result ${item.callId} content`);
|
|
2090
2133
|
const block = {
|
|
2091
2134
|
type: "tool_result",
|
|
2092
2135
|
tool_use_id: item.callId,
|
|
@@ -2139,71 +2182,39 @@ var MessagesAdapter = class extends AdapterBase {
|
|
|
2139
2182
|
stream: true
|
|
2140
2183
|
};
|
|
2141
2184
|
if (systemPrompt) body.system = systemPrompt;
|
|
2142
|
-
|
|
2185
|
+
body.tools = mapper$2.mapToolsIfPresent(request.tools, (t) => ({
|
|
2143
2186
|
name: t.name,
|
|
2144
2187
|
description: t.description,
|
|
2145
2188
|
input_schema: t.inputSchema
|
|
2146
2189
|
}));
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2190
|
+
body.tool_choice = mapper$2.mapToolChoice(request.toolChoice, {
|
|
2191
|
+
auto: { type: "auto" },
|
|
2192
|
+
none: { type: "none" },
|
|
2193
|
+
tool: (name) => ({
|
|
2151
2194
|
type: "tool",
|
|
2152
|
-
name
|
|
2153
|
-
}
|
|
2154
|
-
}
|
|
2195
|
+
name
|
|
2196
|
+
})
|
|
2197
|
+
});
|
|
2155
2198
|
if (request.temperature !== void 0) body.temperature = request.temperature;
|
|
2156
2199
|
return body;
|
|
2157
2200
|
}
|
|
2158
2201
|
async *runStream(providerRequest, factory, request) {
|
|
2159
2202
|
const auxiliary = this.createAuxiliaryState(request);
|
|
2160
|
-
|
|
2203
|
+
const gate = createCompletionGate();
|
|
2161
2204
|
if (request.metadata) yield factory.responseWarning("Request metadata is not supported by the Messages adapter", "UNSUPPORTED_METADATA");
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
signal: request.signal
|
|
2173
|
-
});
|
|
2174
|
-
} catch (err) {
|
|
2175
|
-
throw new AIProviderError(err instanceof Error ? err.message : String(err), "PROVIDER_ERROR");
|
|
2176
|
-
}
|
|
2177
|
-
if (!response.ok) {
|
|
2178
|
-
const errorBody = await response.text().catch(() => "");
|
|
2179
|
-
throw providerHttpError(response.status, errorBody);
|
|
2180
|
-
}
|
|
2181
|
-
const reader = response.body?.getReader();
|
|
2182
|
-
if (!reader) throw new AIStreamError("Response body is not readable", "STREAM_ERROR");
|
|
2183
|
-
const parser = new IncrementalStreamParser(splitSSEFrames, (frame) => {
|
|
2184
|
-
let eventType = "";
|
|
2185
|
-
let dataStr = "";
|
|
2186
|
-
for (const rawLine of frame.split("\n")) {
|
|
2187
|
-
const line = rawLine.trim();
|
|
2188
|
-
if (line.startsWith("event: ")) eventType = line.slice(7).trim();
|
|
2189
|
-
else if (line.startsWith("data: ")) dataStr += line.slice(6);
|
|
2190
|
-
}
|
|
2191
|
-
if (!eventType) return { status: "ignored" };
|
|
2192
|
-
try {
|
|
2193
|
-
const data = JSON.parse(dataStr);
|
|
2194
|
-
return {
|
|
2195
|
-
status: "parsed",
|
|
2196
|
-
value: {
|
|
2197
|
-
type: eventType,
|
|
2198
|
-
data
|
|
2199
|
-
}
|
|
2200
|
-
};
|
|
2201
|
-
} catch {
|
|
2202
|
-
return { status: "malformed" };
|
|
2203
|
-
}
|
|
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
|
|
2204
2215
|
});
|
|
2216
|
+
const parser = createSseJsonParser();
|
|
2205
2217
|
const output = [];
|
|
2206
|
-
let streamDone = false;
|
|
2207
2218
|
let messageResponse;
|
|
2208
2219
|
let currentContentBlockIndex = -1;
|
|
2209
2220
|
let currentItemType = null;
|
|
@@ -2211,7 +2222,6 @@ var MessagesAdapter = class extends AdapterBase {
|
|
|
2211
2222
|
let currentToolName = "";
|
|
2212
2223
|
let currentArgsText = "";
|
|
2213
2224
|
let currentThinkingVisibility = "full";
|
|
2214
|
-
let hasStreamedReasoning = false;
|
|
2215
2225
|
const rawReplayContent = [];
|
|
2216
2226
|
let textBuffer = "";
|
|
2217
2227
|
let thinkingBuffer = "";
|
|
@@ -2220,160 +2230,142 @@ var MessagesAdapter = class extends AdapterBase {
|
|
|
2220
2230
|
let stopSequence;
|
|
2221
2231
|
let rawResponseId = "";
|
|
2222
2232
|
if (request.include?.providerMetadata !== "off") {
|
|
2223
|
-
const headerMetadata = pickProviderHeaders(
|
|
2233
|
+
const headerMetadata = pickProviderHeaders(headers);
|
|
2224
2234
|
auxiliary.recordProviderMetadata("header", Object.keys(headerMetadata).length > 0 ? { headers: headerMetadata } : void 0);
|
|
2225
2235
|
}
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
currentItemId = synthesizeItemId("reason-redacted", currentContentBlockIndex, rawResponseId);
|
|
2272
|
-
currentThinkingVisibility = "redacted";
|
|
2273
|
-
const data = block.data;
|
|
2274
|
-
yield factory.reasoningStarted(currentItemId, "redacted");
|
|
2275
|
-
yield factory.reasoningDelta(currentItemId, textBlock(data));
|
|
2276
|
-
const redactedItem = reasoningItem([textBlock(data)], "redacted", currentItemId);
|
|
2277
|
-
yield factory.reasoningCompleted(currentItemId);
|
|
2278
|
-
output.push(redactedItem);
|
|
2279
|
-
rawReplayContent.push({
|
|
2280
|
-
type: "redacted_thinking",
|
|
2281
|
-
data
|
|
2282
|
-
});
|
|
2283
|
-
currentItemType = null;
|
|
2284
|
-
break;
|
|
2285
|
-
}
|
|
2286
|
-
case "tool_use": {
|
|
2287
|
-
const tuBlock = block;
|
|
2288
|
-
currentItemType = "tool_call";
|
|
2289
|
-
currentItemId = tuBlock.id;
|
|
2290
|
-
currentToolName = tuBlock.name;
|
|
2291
|
-
currentArgsText = "";
|
|
2292
|
-
argsBuffer = "";
|
|
2293
|
-
yield factory.toolCallStarted(currentItemId, currentToolName);
|
|
2294
|
-
break;
|
|
2295
|
-
}
|
|
2296
|
-
}
|
|
2297
|
-
continue;
|
|
2298
|
-
}
|
|
2299
|
-
case "content_block_delta": {
|
|
2300
|
-
const delta = sseEvent.data.delta;
|
|
2301
|
-
switch (delta.type) {
|
|
2302
|
-
case "text_delta":
|
|
2303
|
-
if (currentItemType === "message" && currentItemId) {
|
|
2304
|
-
const txt = delta.text;
|
|
2305
|
-
textBuffer += txt;
|
|
2306
|
-
yield factory.messageDelta(currentItemId, textBlock(txt));
|
|
2307
|
-
}
|
|
2308
|
-
break;
|
|
2309
|
-
case "thinking_delta":
|
|
2310
|
-
if (currentItemType === "reasoning" && currentItemId) {
|
|
2311
|
-
const txt = delta.thinking;
|
|
2312
|
-
thinkingBuffer += txt;
|
|
2313
|
-
yield factory.reasoningDelta(currentItemId, textBlock(txt));
|
|
2314
|
-
}
|
|
2315
|
-
break;
|
|
2316
|
-
case "input_json_delta":
|
|
2317
|
-
if (currentItemType === "tool_call" && currentItemId) {
|
|
2318
|
-
const partial = delta.partial_json;
|
|
2319
|
-
argsBuffer += partial;
|
|
2320
|
-
yield factory.toolCallDelta(currentItemId, { argumentsText: partial });
|
|
2321
|
-
}
|
|
2322
|
-
break;
|
|
2323
|
-
}
|
|
2324
|
-
continue;
|
|
2325
|
-
}
|
|
2326
|
-
case "content_block_stop":
|
|
2327
|
-
if (currentItemType === "message" && currentItemId) {
|
|
2328
|
-
yield factory.messageCompleted(currentItemId);
|
|
2329
|
-
output.push(messageItem([textBlock(textBuffer)], { id: currentItemId }));
|
|
2330
|
-
rawReplayContent.push({
|
|
2331
|
-
type: "text",
|
|
2332
|
-
text: textBuffer
|
|
2333
|
-
});
|
|
2334
|
-
} 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);
|
|
2335
2281
|
yield factory.reasoningCompleted(currentItemId);
|
|
2336
|
-
output.push(
|
|
2337
|
-
rawReplayContent.push({
|
|
2338
|
-
type: "thinking",
|
|
2339
|
-
thinking: thinkingBuffer
|
|
2340
|
-
});
|
|
2341
|
-
} else if (currentItemType === "tool_call" && currentItemId) {
|
|
2342
|
-
const tcItem = toolCallItem(currentItemId, currentToolName, currentArgsText || argsBuffer);
|
|
2343
|
-
yield factory.toolCallCompleted(currentItemId);
|
|
2344
|
-
output.push(tcItem);
|
|
2282
|
+
output.push(redactedItem);
|
|
2345
2283
|
rawReplayContent.push({
|
|
2346
|
-
type: "
|
|
2347
|
-
|
|
2348
|
-
name: currentToolName,
|
|
2349
|
-
input: parseProviderToolUseInput(currentArgsText || argsBuffer)
|
|
2284
|
+
type: "redacted_thinking",
|
|
2285
|
+
data
|
|
2350
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;
|
|
2351
2299
|
}
|
|
2352
|
-
currentItemType = null;
|
|
2353
|
-
currentItemId = "";
|
|
2354
|
-
continue;
|
|
2355
|
-
case "message_delta": {
|
|
2356
|
-
stopReason = sseEvent.data.delta.stop_reason;
|
|
2357
|
-
stopSequence = sseEvent.data.delta.stop_sequence;
|
|
2358
|
-
const u = sseEvent.data.usage;
|
|
2359
|
-
if (u) auxiliary.recordUsage(usageFromAnthropicMessages(u), "stream", u);
|
|
2360
|
-
continue;
|
|
2361
2300
|
}
|
|
2362
|
-
|
|
2301
|
+
continue;
|
|
2363
2302
|
}
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
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;
|
|
2367
2329
|
}
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
|
|
2373
|
-
|
|
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;
|
|
2374
2367
|
}
|
|
2375
2368
|
}
|
|
2376
|
-
if (parser.getRemaining().trim().length > 0) yield factory.responseWarning("Stream ended with an incomplete Messages SSE frame", "STREAM_ERROR");
|
|
2377
2369
|
const replay = [...replayFromOutput(output)];
|
|
2378
2370
|
if (messageResponse) {
|
|
2379
2371
|
const replayContent = rawReplayContent.length > 0 ? rawReplayContent : messageResponse.content;
|
|
@@ -2391,32 +2383,12 @@ var MessagesAdapter = class extends AdapterBase {
|
|
|
2391
2383
|
stopReason,
|
|
2392
2384
|
stopSequence
|
|
2393
2385
|
}));
|
|
2394
|
-
if (
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
output,
|
|
2401
|
-
replay,
|
|
2402
|
-
stopReason: stopReason ? mapStopReason(stopReason) : void 0,
|
|
2403
|
-
usage: auxiliaryResult.usage,
|
|
2404
|
-
billing: auxiliaryResult.billing,
|
|
2405
|
-
auxiliary: auxiliaryResult.auxiliary,
|
|
2406
|
-
warnings: auxiliaryResult.warnings,
|
|
2407
|
-
metadataSources: auxiliaryResult.metadataSources,
|
|
2408
|
-
rawResponseId
|
|
2409
|
-
}, factory);
|
|
2410
|
-
yield factory.responseCompleted({
|
|
2411
|
-
replay: finalResponse.replay,
|
|
2412
|
-
stopReason: finalResponse.stopReason,
|
|
2413
|
-
trace: finalResponse.backend,
|
|
2414
|
-
usage: finalResponse.usage,
|
|
2415
|
-
billing: finalResponse.billing,
|
|
2416
|
-
auxiliary: finalResponse.auxiliary,
|
|
2417
|
-
warnings: finalResponse.warnings
|
|
2418
|
-
});
|
|
2419
|
-
}
|
|
2386
|
+
if (gate.tryComplete()) yield* this.emitStreamCompleted(factory, request, auxiliary, {
|
|
2387
|
+
output,
|
|
2388
|
+
replay,
|
|
2389
|
+
stopReason: stopReason ? mapStopReason(stopReason) : void 0,
|
|
2390
|
+
rawResponseId
|
|
2391
|
+
});
|
|
2420
2392
|
}
|
|
2421
2393
|
};
|
|
2422
2394
|
//#endregion
|
|
@@ -2527,7 +2499,7 @@ var ChatCompletionsAdapter = class extends AdapterBase {
|
|
|
2527
2499
|
for (const item of request.input) switch (item.type) {
|
|
2528
2500
|
case "message": {
|
|
2529
2501
|
const role = item.role;
|
|
2530
|
-
const text =
|
|
2502
|
+
const text = mapper$1.textFromBlocks(item.content, `input message (${item.role}) content`);
|
|
2531
2503
|
messages.push({
|
|
2532
2504
|
role,
|
|
2533
2505
|
content: text || null
|
|
@@ -2557,13 +2529,13 @@ var ChatCompletionsAdapter = class extends AdapterBase {
|
|
|
2557
2529
|
role: "tool",
|
|
2558
2530
|
tool_call_id: item.callId,
|
|
2559
2531
|
name: item.toolName,
|
|
2560
|
-
content:
|
|
2532
|
+
content: mapper$1.textFromBlocks(item.content, `tool_result ${item.callId} content`)
|
|
2561
2533
|
});
|
|
2562
2534
|
break;
|
|
2563
2535
|
case "reasoning":
|
|
2564
2536
|
messages.push({
|
|
2565
2537
|
role: "assistant",
|
|
2566
|
-
content:
|
|
2538
|
+
content: mapper$1.textFromBlocks(item.content, "reasoning content")
|
|
2567
2539
|
});
|
|
2568
2540
|
break;
|
|
2569
2541
|
case "opaque": {
|
|
@@ -2591,7 +2563,7 @@ var ChatCompletionsAdapter = class extends AdapterBase {
|
|
|
2591
2563
|
stream: true,
|
|
2592
2564
|
n: 1
|
|
2593
2565
|
};
|
|
2594
|
-
|
|
2566
|
+
body.tools = mapper$1.mapToolsIfPresent(request.tools, (t) => ({
|
|
2595
2567
|
type: "function",
|
|
2596
2568
|
function: {
|
|
2597
2569
|
name: t.name,
|
|
@@ -2599,14 +2571,14 @@ var ChatCompletionsAdapter = class extends AdapterBase {
|
|
|
2599
2571
|
parameters: t.inputSchema
|
|
2600
2572
|
}
|
|
2601
2573
|
}));
|
|
2602
|
-
|
|
2603
|
-
|
|
2604
|
-
|
|
2605
|
-
|
|
2574
|
+
body.tool_choice = mapper$1.mapToolChoice(request.toolChoice, {
|
|
2575
|
+
auto: "auto",
|
|
2576
|
+
none: "none",
|
|
2577
|
+
tool: (name) => ({
|
|
2606
2578
|
type: "function",
|
|
2607
|
-
function: { name
|
|
2608
|
-
}
|
|
2609
|
-
}
|
|
2579
|
+
function: { name }
|
|
2580
|
+
})
|
|
2581
|
+
});
|
|
2610
2582
|
if (request.temperature !== void 0) body.temperature = request.temperature;
|
|
2611
2583
|
if (request.maxOutputTokens !== void 0) body.max_tokens = request.maxOutputTokens;
|
|
2612
2584
|
if (request.metadata) body.metadata = request.metadata;
|
|
@@ -2614,42 +2586,19 @@ var ChatCompletionsAdapter = class extends AdapterBase {
|
|
|
2614
2586
|
}
|
|
2615
2587
|
async *runStream(providerRequest, factory, request) {
|
|
2616
2588
|
const auxiliary = this.createAuxiliaryState(request);
|
|
2617
|
-
|
|
2618
|
-
|
|
2619
|
-
|
|
2620
|
-
|
|
2621
|
-
|
|
2622
|
-
|
|
2623
|
-
|
|
2624
|
-
|
|
2625
|
-
|
|
2626
|
-
|
|
2627
|
-
});
|
|
2628
|
-
} catch (err) {
|
|
2629
|
-
throw new AIProviderError(err instanceof Error ? err.message : String(err), "PROVIDER_ERROR");
|
|
2630
|
-
}
|
|
2631
|
-
if (!response.ok) {
|
|
2632
|
-
const errorBody = await response.text().catch(() => "");
|
|
2633
|
-
throw providerHttpError(response.status, errorBody);
|
|
2634
|
-
}
|
|
2635
|
-
const reader = response.body?.getReader();
|
|
2636
|
-
if (!reader) throw new AIStreamError("Response body is not readable", "STREAM_ERROR");
|
|
2637
|
-
const parser = new IncrementalStreamParser(splitLines, (item) => {
|
|
2638
|
-
const trimmed = item.trim();
|
|
2639
|
-
if (!trimmed.startsWith("data: ")) return { status: "ignored" };
|
|
2640
|
-
const data = trimmed.slice(6).trim();
|
|
2641
|
-
if (data === "[DONE]") return { status: "ignored" };
|
|
2642
|
-
try {
|
|
2643
|
-
return {
|
|
2644
|
-
status: "parsed",
|
|
2645
|
-
value: JSON.parse(data)
|
|
2646
|
-
};
|
|
2647
|
-
} catch {
|
|
2648
|
-
return { status: "malformed" };
|
|
2649
|
-
}
|
|
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
|
|
2650
2599
|
});
|
|
2600
|
+
const parser = createChatCompletionsSseParser();
|
|
2651
2601
|
const output = [];
|
|
2652
|
-
let streamDone = false;
|
|
2653
2602
|
let responseId;
|
|
2654
2603
|
let accumulatedContent = "";
|
|
2655
2604
|
let accumulatedReasoning = "";
|
|
@@ -2657,9 +2606,7 @@ var ChatCompletionsAdapter = class extends AdapterBase {
|
|
|
2657
2606
|
let currentReasoningId = "";
|
|
2658
2607
|
let hasMessageStarted = false;
|
|
2659
2608
|
let hasReasoningStarted = false;
|
|
2660
|
-
let completedEmitted = false;
|
|
2661
2609
|
let warnedNonZeroChoice = false;
|
|
2662
|
-
const buildResponse = this.buildResponse.bind(this);
|
|
2663
2610
|
const pendingToolCalls = /* @__PURE__ */ new Map();
|
|
2664
2611
|
const reasoningByField = /* @__PURE__ */ new Map();
|
|
2665
2612
|
const finalizePendingTurn = () => {
|
|
@@ -2700,163 +2647,131 @@ var ChatCompletionsAdapter = class extends AdapterBase {
|
|
|
2700
2647
|
};
|
|
2701
2648
|
};
|
|
2702
2649
|
const emitCompleted = async function* (stopReason, assistantReplayMessage, rawResponseId) {
|
|
2703
|
-
if (
|
|
2650
|
+
if (!gate.tryComplete()) {
|
|
2704
2651
|
yield factory.responseWarning("Duplicate finish signal ignored", "DUPLICATE_FINISH");
|
|
2705
2652
|
return;
|
|
2706
2653
|
}
|
|
2707
|
-
completedEmitted = true;
|
|
2708
2654
|
const replay = [...replayFromOutput(output)];
|
|
2709
2655
|
if (assistantReplayMessage) replay.push(opaqueItem("chat.completions", "replay", {
|
|
2710
2656
|
replaceCanonical: true,
|
|
2711
2657
|
messages: [assistantReplayMessage]
|
|
2712
2658
|
}));
|
|
2713
|
-
|
|
2714
|
-
for (const event of auxiliaryResult.events) yield event;
|
|
2715
|
-
const finalResponse = buildResponse(request, {
|
|
2659
|
+
yield* this.emitStreamCompleted(factory, request, auxiliary, {
|
|
2716
2660
|
output,
|
|
2717
2661
|
replay,
|
|
2718
2662
|
stopReason,
|
|
2719
|
-
usage: auxiliaryResult.usage,
|
|
2720
|
-
billing: auxiliaryResult.billing,
|
|
2721
|
-
auxiliary: auxiliaryResult.auxiliary,
|
|
2722
|
-
warnings: auxiliaryResult.warnings,
|
|
2723
|
-
metadataSources: auxiliaryResult.metadataSources,
|
|
2724
2663
|
rawResponseId
|
|
2725
|
-
}, factory);
|
|
2726
|
-
yield factory.responseCompleted({
|
|
2727
|
-
replay: finalResponse.replay,
|
|
2728
|
-
stopReason: finalResponse.stopReason,
|
|
2729
|
-
trace: finalResponse.backend,
|
|
2730
|
-
usage: finalResponse.usage,
|
|
2731
|
-
billing: finalResponse.billing,
|
|
2732
|
-
auxiliary: finalResponse.auxiliary,
|
|
2733
|
-
warnings: finalResponse.warnings
|
|
2734
2664
|
});
|
|
2735
|
-
};
|
|
2736
|
-
|
|
2737
|
-
|
|
2738
|
-
|
|
2739
|
-
|
|
2740
|
-
|
|
2741
|
-
|
|
2742
|
-
|
|
2743
|
-
|
|
2744
|
-
|
|
2745
|
-
|
|
2746
|
-
|
|
2747
|
-
if (
|
|
2748
|
-
for (const
|
|
2749
|
-
|
|
2750
|
-
|
|
2751
|
-
|
|
2752
|
-
|
|
2753
|
-
if (!warnedNonZeroChoice) {
|
|
2754
|
-
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");
|
|
2755
|
-
warnedNonZeroChoice = true;
|
|
2756
|
-
}
|
|
2757
|
-
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;
|
|
2758
2683
|
}
|
|
2759
|
-
|
|
2760
|
-
|
|
2761
|
-
|
|
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");
|
|
2762
2705
|
}
|
|
2763
|
-
const
|
|
2764
|
-
|
|
2765
|
-
|
|
2766
|
-
|
|
2767
|
-
if (hasMessageStarted) return;
|
|
2768
|
-
currentMessageId = `msg-${chunk.id}`;
|
|
2769
|
-
hasMessageStarted = true;
|
|
2770
|
-
accumulatedContent = "";
|
|
2771
|
-
};
|
|
2772
|
-
if (reasoningDeltas.length > 0) {
|
|
2773
|
-
if (!hasReasoningStarted) {
|
|
2774
|
-
currentReasoningId = `reason-${chunk.id}`;
|
|
2775
|
-
hasReasoningStarted = true;
|
|
2776
|
-
accumulatedReasoning = "";
|
|
2777
|
-
yield factory.reasoningStarted(currentReasoningId, "full");
|
|
2778
|
-
}
|
|
2779
|
-
for (const reasoningDelta of reasoningDeltas) {
|
|
2780
|
-
accumulatedReasoning += reasoningDelta.text;
|
|
2781
|
-
reasoningByField.set(reasoningDelta.field, (reasoningByField.get(reasoningDelta.field) ?? "") + reasoningDelta.text);
|
|
2782
|
-
yield factory.reasoningDelta(currentReasoningId, textBlock(reasoningDelta.text));
|
|
2783
|
-
}
|
|
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));
|
|
2784
2710
|
}
|
|
2785
|
-
|
|
2786
|
-
|
|
2787
|
-
|
|
2788
|
-
|
|
2789
|
-
|
|
2790
|
-
accumulatedContent += delta.content;
|
|
2791
|
-
yield factory.messageDelta(currentMessageId, textBlock(delta.content));
|
|
2711
|
+
}
|
|
2712
|
+
if (delta.content) {
|
|
2713
|
+
if (!hasMessageStarted) {
|
|
2714
|
+
ensureMessageStarted();
|
|
2715
|
+
yield factory.messageStarted(currentMessageId);
|
|
2792
2716
|
}
|
|
2793
|
-
|
|
2794
|
-
|
|
2795
|
-
|
|
2796
|
-
|
|
2797
|
-
|
|
2798
|
-
|
|
2799
|
-
|
|
2800
|
-
if (tc.id) {
|
|
2801
|
-
pendingToolCalls.set(idx, {
|
|
2802
|
-
id: tc.id,
|
|
2803
|
-
name: tc.function?.name ?? "",
|
|
2804
|
-
args: ""
|
|
2805
|
-
});
|
|
2806
|
-
yield factory.toolCallStarted(tc.id, tc.function?.name ?? "");
|
|
2807
|
-
}
|
|
2808
|
-
if (tc.function?.arguments) {
|
|
2809
|
-
const pending = pendingToolCalls.get(idx);
|
|
2810
|
-
if (pending) {
|
|
2811
|
-
pending.args += tc.function.arguments;
|
|
2812
|
-
yield factory.toolCallDelta(pending.id, { argumentsText: tc.function.arguments });
|
|
2813
|
-
}
|
|
2814
|
-
}
|
|
2815
|
-
}
|
|
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);
|
|
2816
2724
|
}
|
|
2817
|
-
|
|
2818
|
-
|
|
2819
|
-
|
|
2820
|
-
|
|
2821
|
-
|
|
2822
|
-
|
|
2823
|
-
const fcId = `fc-${chunk.id}-0`;
|
|
2824
|
-
pendingToolCalls.set(0, {
|
|
2825
|
-
id: fcId,
|
|
2826
|
-
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 ?? "",
|
|
2827
2731
|
args: ""
|
|
2828
2732
|
});
|
|
2829
|
-
yield factory.toolCallStarted(
|
|
2733
|
+
yield factory.toolCallStarted(tc.id, tc.function?.name ?? "");
|
|
2830
2734
|
}
|
|
2831
|
-
if (
|
|
2832
|
-
const pending = pendingToolCalls.get(
|
|
2735
|
+
if (tc.function?.arguments) {
|
|
2736
|
+
const pending = pendingToolCalls.get(idx);
|
|
2833
2737
|
if (pending) {
|
|
2834
|
-
pending.args +=
|
|
2835
|
-
yield factory.toolCallDelta(pending.id, { argumentsText:
|
|
2738
|
+
pending.args += tc.function.arguments;
|
|
2739
|
+
yield factory.toolCallDelta(pending.id, { argumentsText: tc.function.arguments });
|
|
2836
2740
|
}
|
|
2837
2741
|
}
|
|
2838
2742
|
}
|
|
2839
|
-
|
|
2840
|
-
|
|
2841
|
-
|
|
2842
|
-
|
|
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
|
+
}
|
|
2843
2764
|
}
|
|
2844
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
|
+
}
|
|
2845
2771
|
}
|
|
2846
|
-
if (done) {
|
|
2847
|
-
streamDone = true;
|
|
2848
|
-
break;
|
|
2849
|
-
}
|
|
2850
|
-
}
|
|
2851
|
-
} finally {
|
|
2852
|
-
try {
|
|
2853
|
-
if (!streamDone) await reader.cancel().catch(() => void 0);
|
|
2854
|
-
} finally {
|
|
2855
|
-
reader.releaseLock();
|
|
2856
2772
|
}
|
|
2857
2773
|
}
|
|
2858
|
-
if (
|
|
2859
|
-
if (!completedEmitted && (hasMessageStarted || hasReasoningStarted || pendingToolCalls.size > 0)) {
|
|
2774
|
+
if (!gate.completed && (hasMessageStarted || hasReasoningStarted || pendingToolCalls.size > 0)) {
|
|
2860
2775
|
yield factory.responseWarning("Stream ended without a finish_reason", "INCOMPLETE_STREAM");
|
|
2861
2776
|
const { events, assistantReplayMessage } = finalizePendingTurn();
|
|
2862
2777
|
for (const event of events) yield event;
|
|
@@ -2923,7 +2838,7 @@ var OllamaAdapter = class extends AdapterBase {
|
|
|
2923
2838
|
const role = item.role;
|
|
2924
2839
|
messages.push({
|
|
2925
2840
|
role,
|
|
2926
|
-
content:
|
|
2841
|
+
content: mapper.textFromBlocks(item.content, `input message (${item.role}) content`)
|
|
2927
2842
|
});
|
|
2928
2843
|
break;
|
|
2929
2844
|
}
|
|
@@ -2949,7 +2864,7 @@ var OllamaAdapter = class extends AdapterBase {
|
|
|
2949
2864
|
if (queue && queue.length > 0) queue.shift();
|
|
2950
2865
|
messages.push({
|
|
2951
2866
|
role: "tool",
|
|
2952
|
-
content:
|
|
2867
|
+
content: mapper.textFromBlocks(item.content, `tool_result ${item.callId} content`)
|
|
2953
2868
|
});
|
|
2954
2869
|
break;
|
|
2955
2870
|
}
|
|
@@ -3010,57 +2925,31 @@ var OllamaAdapter = class extends AdapterBase {
|
|
|
3010
2925
|
}
|
|
3011
2926
|
async *runStream(providerRequest, factory, request) {
|
|
3012
2927
|
const auxiliary = this.createAuxiliaryState(request);
|
|
3013
|
-
|
|
2928
|
+
const gate = createCompletionGate();
|
|
3014
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);
|
|
3015
2930
|
if (request.metadata) yield factory.responseWarning("Request metadata is not supported by the Ollama adapter", "UNSUPPORTED_METADATA");
|
|
3016
2931
|
const headers = { "Content-Type": "application/json" };
|
|
3017
2932
|
if (this.apiKey) headers.Authorization = `Bearer ${this.apiKey}`;
|
|
3018
|
-
|
|
3019
|
-
|
|
3020
|
-
|
|
3021
|
-
|
|
3022
|
-
|
|
3023
|
-
|
|
3024
|
-
signal: request.signal
|
|
3025
|
-
});
|
|
3026
|
-
} catch (err) {
|
|
3027
|
-
throw new AIProviderError(err instanceof Error ? err.message : String(err), "PROVIDER_ERROR");
|
|
3028
|
-
}
|
|
3029
|
-
if (!response.ok) {
|
|
3030
|
-
const errorBody = await response.text().catch(() => "");
|
|
3031
|
-
throw providerHttpError(response.status, errorBody);
|
|
3032
|
-
}
|
|
3033
|
-
const reader = response.body?.getReader();
|
|
3034
|
-
if (!reader) throw new AIStreamError("Response body is not readable", "STREAM_ERROR");
|
|
3035
|
-
const parser = new IncrementalStreamParser(splitLines, (item) => {
|
|
3036
|
-
const trimmed = item.trim();
|
|
3037
|
-
if (!trimmed) return { status: "ignored" };
|
|
3038
|
-
try {
|
|
3039
|
-
const parsed = JSON.parse(trimmed);
|
|
3040
|
-
if (parsed && typeof parsed === "object" && "message" in parsed) return {
|
|
3041
|
-
status: "parsed",
|
|
3042
|
-
value: parsed
|
|
3043
|
-
};
|
|
3044
|
-
return { status: "malformed" };
|
|
3045
|
-
} catch {
|
|
3046
|
-
return { status: "malformed" };
|
|
3047
|
-
}
|
|
2933
|
+
const { reader } = await openProviderJsonStream({
|
|
2934
|
+
fetchFn: this.fetchFn,
|
|
2935
|
+
url: `${this.baseUrl}/api/chat`,
|
|
2936
|
+
headers,
|
|
2937
|
+
body: providerRequest,
|
|
2938
|
+
signal: request.signal
|
|
3048
2939
|
});
|
|
2940
|
+
const parser = createNdjsonLineParser((value) => !!value && typeof value === "object" && "message" in value);
|
|
3049
2941
|
const output = [];
|
|
3050
|
-
let streamDone = false;
|
|
3051
2942
|
let responseId;
|
|
3052
2943
|
let accumulatedContent = "";
|
|
3053
2944
|
let currentMessageId = "";
|
|
3054
2945
|
let hasMessageStarted = false;
|
|
3055
2946
|
let pendingToolCalls = [];
|
|
3056
2947
|
let toolCallIndex = 0;
|
|
3057
|
-
const buildResponse = this.buildResponse.bind(this);
|
|
3058
2948
|
const emitCompleted = async function* (stopReason, rawResponseId) {
|
|
3059
|
-
if (
|
|
2949
|
+
if (!gate.tryComplete()) {
|
|
3060
2950
|
yield factory.responseWarning("Duplicate finish signal ignored", "DUPLICATE_FINISH");
|
|
3061
2951
|
return;
|
|
3062
2952
|
}
|
|
3063
|
-
completedEmitted = true;
|
|
3064
2953
|
const replay = replayFromOutput(output);
|
|
3065
2954
|
if (accumulatedContent || pendingToolCalls.length > 0) replay.push(opaqueItem("ollama", "replay", {
|
|
3066
2955
|
role: "assistant",
|
|
@@ -3073,113 +2962,82 @@ var OllamaAdapter = class extends AdapterBase {
|
|
|
3073
2962
|
}
|
|
3074
2963
|
}))
|
|
3075
2964
|
}));
|
|
3076
|
-
|
|
3077
|
-
for (const event of auxiliaryResult.events) yield event;
|
|
3078
|
-
const finalResponse = buildResponse(request, {
|
|
2965
|
+
yield* this.emitStreamCompleted(factory, request, auxiliary, {
|
|
3079
2966
|
output,
|
|
3080
2967
|
replay,
|
|
3081
2968
|
stopReason,
|
|
3082
|
-
usage: auxiliaryResult.usage,
|
|
3083
|
-
billing: auxiliaryResult.billing,
|
|
3084
|
-
auxiliary: auxiliaryResult.auxiliary,
|
|
3085
|
-
warnings: auxiliaryResult.warnings,
|
|
3086
|
-
metadataSources: auxiliaryResult.metadataSources,
|
|
3087
2969
|
rawResponseId
|
|
3088
|
-
}, factory);
|
|
3089
|
-
yield factory.responseCompleted({
|
|
3090
|
-
replay: finalResponse.replay,
|
|
3091
|
-
stopReason: finalResponse.stopReason,
|
|
3092
|
-
trace: finalResponse.backend,
|
|
3093
|
-
usage: finalResponse.usage,
|
|
3094
|
-
billing: finalResponse.billing,
|
|
3095
|
-
auxiliary: finalResponse.auxiliary,
|
|
3096
|
-
warnings: finalResponse.warnings
|
|
3097
2970
|
});
|
|
3098
|
-
};
|
|
3099
|
-
|
|
3100
|
-
|
|
3101
|
-
|
|
3102
|
-
|
|
3103
|
-
|
|
3104
|
-
|
|
3105
|
-
|
|
3106
|
-
|
|
3107
|
-
|
|
3108
|
-
|
|
3109
|
-
|
|
3110
|
-
if (
|
|
3111
|
-
|
|
3112
|
-
|
|
3113
|
-
|
|
3114
|
-
|
|
3115
|
-
|
|
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);
|
|
3116
2993
|
}
|
|
3117
|
-
|
|
3118
|
-
|
|
3119
|
-
|
|
3120
|
-
|
|
3121
|
-
|
|
3122
|
-
|
|
3123
|
-
|
|
3124
|
-
|
|
3125
|
-
|
|
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);
|
|
3126
3011
|
}
|
|
3127
|
-
if (
|
|
3128
|
-
const
|
|
3129
|
-
|
|
3130
|
-
|
|
3131
|
-
id: tcId,
|
|
3132
|
-
name: tc.function.name,
|
|
3133
|
-
argumentsText: argsText
|
|
3134
|
-
});
|
|
3012
|
+
if (hasMessageStarted) {
|
|
3013
|
+
const message = messageItem([textBlock(accumulatedContent)], { id: currentMessageId });
|
|
3014
|
+
yield factory.messageCompleted(currentMessageId);
|
|
3015
|
+
if (accumulatedContent) output.push(message);
|
|
3135
3016
|
}
|
|
3136
|
-
if (
|
|
3137
|
-
|
|
3138
|
-
|
|
3139
|
-
|
|
3140
|
-
|
|
3141
|
-
|
|
3142
|
-
|
|
3143
|
-
const message = messageItem([textBlock(accumulatedContent)], { id: currentMessageId });
|
|
3144
|
-
yield factory.messageCompleted(currentMessageId);
|
|
3145
|
-
if (accumulatedContent) output.push(message);
|
|
3146
|
-
}
|
|
3147
|
-
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);
|
|
3148
|
-
for (const pending of pendingToolCalls) {
|
|
3149
|
-
const toolCall = toolCallItem(pending.id, pending.name, pending.argumentsText);
|
|
3150
|
-
yield factory.toolCallStarted(pending.id, pending.name);
|
|
3151
|
-
yield factory.toolCallDelta(pending.id, { argumentsText: pending.argumentsText });
|
|
3152
|
-
yield factory.toolCallCompleted(pending.id);
|
|
3153
|
-
output.push(toolCall);
|
|
3154
|
-
}
|
|
3155
|
-
if (request.include?.usage !== "off" && (chunk.prompt_eval_count !== void 0 || chunk.eval_count !== void 0)) auxiliary.recordUsage(usageFromOllama({
|
|
3156
|
-
prompt_eval_count: chunk.prompt_eval_count,
|
|
3157
|
-
eval_count: chunk.eval_count
|
|
3158
|
-
}), "final", {
|
|
3159
|
-
prompt_eval_count: chunk.prompt_eval_count,
|
|
3160
|
-
eval_count: chunk.eval_count
|
|
3161
|
-
});
|
|
3162
|
-
yield* emitCompleted(chunk.done_reason ? mapStopReason(chunk.done_reason) : void 0, chunk.created_at);
|
|
3163
|
-
accumulatedContent = "";
|
|
3164
|
-
currentMessageId = "";
|
|
3165
|
-
hasMessageStarted = false;
|
|
3166
|
-
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);
|
|
3167
3024
|
}
|
|
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 = [];
|
|
3168
3037
|
}
|
|
3169
|
-
if (done) {
|
|
3170
|
-
streamDone = true;
|
|
3171
|
-
break;
|
|
3172
|
-
}
|
|
3173
|
-
}
|
|
3174
|
-
} finally {
|
|
3175
|
-
try {
|
|
3176
|
-
if (!streamDone) await reader.cancel().catch(() => void 0);
|
|
3177
|
-
} finally {
|
|
3178
|
-
reader.releaseLock();
|
|
3179
3038
|
}
|
|
3180
3039
|
}
|
|
3181
|
-
if (
|
|
3182
|
-
if (!completedEmitted && (hasMessageStarted || pendingToolCalls.length > 0)) {
|
|
3040
|
+
if (!gate.completed && (hasMessageStarted || pendingToolCalls.length > 0)) {
|
|
3183
3041
|
yield factory.responseWarning("Stream ended without a done signal", "INCOMPLETE_STREAM");
|
|
3184
3042
|
if (hasMessageStarted) {
|
|
3185
3043
|
const message = messageItem([textBlock(accumulatedContent)], { id: currentMessageId });
|
|
@@ -3619,6 +3477,6 @@ function cloneItem(item) {
|
|
|
3619
3477
|
return structuredClone(item);
|
|
3620
3478
|
}
|
|
3621
3479
|
//#endregion
|
|
3622
|
-
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 };
|
|
3623
3481
|
|
|
3624
3482
|
//# sourceMappingURL=index.mjs.map
|