@absolutejs/ai 0.4.0 → 0.5.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/CHANGELOG.md +17 -0
- package/README.md +15 -0
- package/changelog.json +57 -0
- package/dist/ai/index.js +140 -87
- package/dist/ai/index.js.map +15 -14
- package/dist/ai/providers/anthropic.js +45 -50
- package/dist/ai/providers/anthropic.js.map +4 -4
- package/dist/ai/providers/gemini.js +6 -3
- package/dist/ai/providers/gemini.js.map +4 -4
- package/dist/ai/providers/ollama.js +4 -2
- package/dist/ai/providers/ollama.js.map +4 -4
- package/dist/ai/providers/openai.js +59 -11
- package/dist/ai/providers/openai.js.map +5 -5
- package/dist/ai/providers/openaiCompatible.js +59 -11
- package/dist/ai/providers/openaiCompatible.js.map +5 -5
- package/dist/ai/providers/openaiResponses.js +29 -4
- package/dist/ai/providers/openaiResponses.js.map +4 -4
- package/dist/ai/providers/openrouter.js +102 -59
- package/dist/ai/providers/openrouter.js.map +6 -6
- package/dist/ai/tools/index.js +6 -1
- package/dist/ai/tools/index.js.map +3 -3
- package/dist/ai/tools/untrusted.js +6 -1
- package/dist/ai/tools/untrusted.js.map +3 -3
- package/dist/src/ai/index.d.ts +1 -0
- package/dist/src/ai/streamAIWithTools.d.ts +4 -0
- package/dist/src/ai/toolOutput.d.ts +8 -0
- package/dist/types/ai.d.ts +23 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,23 @@ This file is generated by `absolute-changelog` from the entries in
|
|
|
6
6
|
`changelog/`. Edit an entry, not this file — and add new ones under
|
|
7
7
|
`changelog/unreleased/`.
|
|
8
8
|
|
|
9
|
+
## 0.5.0 — 2026-10-19
|
|
10
|
+
|
|
11
|
+
### Breaking
|
|
12
|
+
|
|
13
|
+
- **AIToolDefinition.handler may return { text, images } as well as a string** (`AIToolDefinition`, `toolOutputText`)
|
|
14
|
+
_Migration:_ Code that calls a tool's handler itself (a wrapper, a test) now receives AIToolOutput, a string or { text, images }. Read its text with toolOutputText(output). Handlers that return a string need no change.
|
|
15
|
+
|
|
16
|
+
### Added
|
|
17
|
+
|
|
18
|
+
- **Tools can return images for the model to see, such as a screenshot: a handler returns { text, images } and the tool loops send the images inside the tool result, mapped for Anthropic, OpenAI Responses, OpenAI Chat Completions, Gemini and Ollama** (`AIToolResult`, `AIToolOutput`, `toolOutputText`, `AIProviderContentBlock`, `anthropic`, `openai`, `openaiResponses`, `gemini`, `ollama`, `streamAIToSSE`, `streamAIWithTools`, `generateAIWithTools`, `hardenUntrustedTool`, `createAIStoredToolResultRecovery`)
|
|
19
|
+
|
|
20
|
+
## 0.4.1 — 2026-10-17
|
|
21
|
+
|
|
22
|
+
### Fixed
|
|
23
|
+
|
|
24
|
+
- **Preserve provider usage snapshots through stream interruptions, distinguish partial from final usage, and retain tool-input start events without application patches** (`AIUsageUpdateChunk`, `anthropic`, `openai`, `openaiResponses`, `AIChunk`, `StreamAIWithToolsEvent`, `AIToolInputStartChunk`)
|
|
25
|
+
|
|
9
26
|
## 0.4.0 — 2026-10-16
|
|
10
27
|
|
|
11
28
|
### Added
|
package/README.md
CHANGED
|
@@ -560,3 +560,18 @@ are documented provider limits, not a substitute for runtime token budgeting.
|
|
|
560
560
|
Regional features are omitted when they are not consistently supported.
|
|
561
561
|
|
|
562
562
|
`@absolutejs/ai/research` separates an injected `SearchProvider` from evidence-only synthesis. It returns sources, findings, searches, limitations and explicit availability, and excludes invented source references/quotes. Quotation checks establish provenance; they are not a substitute for claim entailment or entity review. Hosts can set `maxRetries: 0` on one-shot or stream requests when they own attempt accounting, and independently bound `maxRepairAttempts` and disable context recovery.
|
|
563
|
+
|
|
564
|
+
### Interrupted streams and usage
|
|
565
|
+
|
|
566
|
+
Provider streams can emit `usage_update` chunks before `done`. Each carries a
|
|
567
|
+
provider-reported cumulative snapshot: replace the previous snapshot for that
|
|
568
|
+
attempt; never sum snapshots. `final: true` means the provider reported complete
|
|
569
|
+
usage. An absent or false `final` means partial evidence, not a final bill.
|
|
570
|
+
Anthropic exposes message-start and message-delta usage; OpenAI Chat exposes its
|
|
571
|
+
usage chunk, and Responses preserves usage even on incomplete responses.
|
|
572
|
+
|
|
573
|
+
Persist snapshots while consuming the stream. Cancellation or transport failure
|
|
574
|
+
can prevent `done`, and no adapter can reconstruct usage the provider never sent.
|
|
575
|
+
`onUsage` still reports only complete usage, once per instrumented stream. Keep
|
|
576
|
+
credit reservations separate from charges and reconcile unknown outcomes in the
|
|
577
|
+
host application. The package does not invent token counts or decide billing policy.
|
package/changelog.json
CHANGED
|
@@ -2,6 +2,63 @@
|
|
|
2
2
|
"contract": 1,
|
|
3
3
|
"name": "@absolutejs/ai",
|
|
4
4
|
"releases": [
|
|
5
|
+
{
|
|
6
|
+
"changes": [
|
|
7
|
+
{
|
|
8
|
+
"kind": "breaking",
|
|
9
|
+
"migration": {
|
|
10
|
+
"instruction": "Code that calls a tool's handler itself (a wrapper, a test) now receives AIToolOutput, a string or { text, images }. Read its text with toolOutputText(output). Handlers that return a string need no change.",
|
|
11
|
+
"manual": true
|
|
12
|
+
},
|
|
13
|
+
"summary": "AIToolDefinition.handler may return { text, images } as well as a string",
|
|
14
|
+
"symbols": [
|
|
15
|
+
"AIToolDefinition",
|
|
16
|
+
"toolOutputText"
|
|
17
|
+
]
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
"kind": "added",
|
|
21
|
+
"summary": "Tools can return images for the model to see, such as a screenshot: a handler returns { text, images } and the tool loops send the images inside the tool result, mapped for Anthropic, OpenAI Responses, OpenAI Chat Completions, Gemini and Ollama",
|
|
22
|
+
"symbols": [
|
|
23
|
+
"AIToolResult",
|
|
24
|
+
"AIToolOutput",
|
|
25
|
+
"toolOutputText",
|
|
26
|
+
"AIProviderContentBlock",
|
|
27
|
+
"anthropic",
|
|
28
|
+
"openai",
|
|
29
|
+
"openaiResponses",
|
|
30
|
+
"gemini",
|
|
31
|
+
"ollama",
|
|
32
|
+
"streamAIToSSE",
|
|
33
|
+
"streamAIWithTools",
|
|
34
|
+
"generateAIWithTools",
|
|
35
|
+
"hardenUntrustedTool",
|
|
36
|
+
"createAIStoredToolResultRecovery"
|
|
37
|
+
]
|
|
38
|
+
}
|
|
39
|
+
],
|
|
40
|
+
"date": "2026-10-19",
|
|
41
|
+
"version": "0.5.0"
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
"changes": [
|
|
45
|
+
{
|
|
46
|
+
"kind": "fixed",
|
|
47
|
+
"summary": "Preserve provider usage snapshots through stream interruptions, distinguish partial from final usage, and retain tool-input start events without application patches",
|
|
48
|
+
"symbols": [
|
|
49
|
+
"AIUsageUpdateChunk",
|
|
50
|
+
"anthropic",
|
|
51
|
+
"openai",
|
|
52
|
+
"openaiResponses",
|
|
53
|
+
"AIChunk",
|
|
54
|
+
"StreamAIWithToolsEvent",
|
|
55
|
+
"AIToolInputStartChunk"
|
|
56
|
+
]
|
|
57
|
+
}
|
|
58
|
+
],
|
|
59
|
+
"date": "2026-10-17",
|
|
60
|
+
"version": "0.4.1"
|
|
61
|
+
},
|
|
5
62
|
{
|
|
6
63
|
"changes": [
|
|
7
64
|
{
|
package/dist/ai/index.js
CHANGED
|
@@ -258,6 +258,14 @@ var parseAIMessage = (raw) => {
|
|
|
258
258
|
};
|
|
259
259
|
var serializeAIMessage = (message) => JSON.stringify(message);
|
|
260
260
|
|
|
261
|
+
// src/ai/toolOutput.ts
|
|
262
|
+
var toolOutputText = (output) => typeof output === "string" ? output : output.text;
|
|
263
|
+
var toolOutputImages = (output) => typeof output === "string" || !output.images?.length ? undefined : output.images;
|
|
264
|
+
var toolResultBlock = (toolUseId, output, content = toolOutputText(output)) => {
|
|
265
|
+
const images = toolOutputImages(output);
|
|
266
|
+
return images ? { content, images, tool_use_id: toolUseId, type: "tool_result" } : { content, tool_use_id: toolUseId, type: "tool_result" };
|
|
267
|
+
};
|
|
268
|
+
|
|
261
269
|
// src/ai/inputCapacity.ts
|
|
262
270
|
class AIInputError extends Error {
|
|
263
271
|
code;
|
|
@@ -643,14 +651,11 @@ var processToolTurn = async (socket, options, state, messageId, conversationId,
|
|
|
643
651
|
const toolResultBlocks = [];
|
|
644
652
|
for (const toolCall of toolCalls) {
|
|
645
653
|
await sendToolRunning(socket, toolCall.name, toolCall.input, messageId, conversationId);
|
|
646
|
-
const
|
|
654
|
+
const output = await executeTool(options, toolCall.name, toolCall.input);
|
|
655
|
+
const result = toolOutputText(output);
|
|
647
656
|
await sendToolComplete(socket, toolCall.name, result, messageId, conversationId);
|
|
648
657
|
options.onToolUse?.(toolCall.name, toolCall.input, result);
|
|
649
|
-
toolResultBlocks.push(
|
|
650
|
-
content: result,
|
|
651
|
-
tool_use_id: toolCall.id,
|
|
652
|
-
type: "tool_result"
|
|
653
|
-
});
|
|
658
|
+
toolResultBlocks.push(toolResultBlock(toolCall.id, output));
|
|
654
659
|
state.executedToolKeys.add(serializeToolCall(toolCall.name, toolCall.input));
|
|
655
660
|
}
|
|
656
661
|
state.currentMessages.push({
|
|
@@ -1112,7 +1117,8 @@ var executeToolCalls = async function* (pendingToolCalls, options, renderers, tu
|
|
|
1112
1117
|
turnState.allToolsHtml += renderers.toolRunning(toolCall.name, toolCall.input);
|
|
1113
1118
|
yield { data: turnState.allToolsHtml, event: "tools" };
|
|
1114
1119
|
}
|
|
1115
|
-
const
|
|
1120
|
+
const output = await executeTool2(options, toolCall.name, toolCall.input);
|
|
1121
|
+
const result = toolOutputText(output);
|
|
1116
1122
|
if (options.structuredEvents) {
|
|
1117
1123
|
yield {
|
|
1118
1124
|
data: JSON.stringify({
|
|
@@ -1129,11 +1135,7 @@ var executeToolCalls = async function* (pendingToolCalls, options, renderers, tu
|
|
|
1129
1135
|
}
|
|
1130
1136
|
options.onToolUse?.(toolCall.name, toolCall.input, result);
|
|
1131
1137
|
const resultContent = options.maxToolResultChars !== undefined ? truncateToolResult(result, options.maxToolResultChars) : result;
|
|
1132
|
-
toolResultBlocks.push(
|
|
1133
|
-
content: resultContent,
|
|
1134
|
-
tool_use_id: toolCall.id,
|
|
1135
|
-
type: "tool_result"
|
|
1136
|
-
});
|
|
1138
|
+
toolResultBlocks.push(toolResultBlock(toolCall.id, output, resultContent));
|
|
1137
1139
|
}
|
|
1138
1140
|
return toolResultBlocks;
|
|
1139
1141
|
};
|
|
@@ -1833,12 +1835,8 @@ var generateAIWithTools = async (options) => {
|
|
|
1833
1835
|
const resultBlocks = await Promise.all(result.toolCalls.map(async (call) => {
|
|
1834
1836
|
const definition = tools[call.name];
|
|
1835
1837
|
const output = definition ? await Promise.resolve(definition.handler(call.input)).catch((err) => `Error: ${err instanceof Error ? err.message : String(err)}`) : `Error: unknown tool "${call.name}"`;
|
|
1836
|
-
onToolUse?.(call.name, call.input, output);
|
|
1837
|
-
return
|
|
1838
|
-
content: output,
|
|
1839
|
-
tool_use_id: call.id,
|
|
1840
|
-
type: "tool_result"
|
|
1841
|
-
};
|
|
1838
|
+
onToolUse?.(call.name, call.input, toolOutputText(output));
|
|
1839
|
+
return toolResultBlock(call.id, output);
|
|
1842
1840
|
}));
|
|
1843
1841
|
return [
|
|
1844
1842
|
...result.requestMessages ?? messages,
|
|
@@ -1988,14 +1986,15 @@ var flushThinking3 = (blocks, thinking) => {
|
|
|
1988
1986
|
var executeToolHandler = async (tools, call) => {
|
|
1989
1987
|
const definition = tools[call.name];
|
|
1990
1988
|
if (!definition) {
|
|
1991
|
-
return { ok: false,
|
|
1989
|
+
return { ok: false, output: `Error: unknown tool "${call.name}"` };
|
|
1992
1990
|
}
|
|
1993
1991
|
try {
|
|
1994
|
-
|
|
1992
|
+
const output = await definition.handler(call.input);
|
|
1993
|
+
return { ok: true, output };
|
|
1995
1994
|
} catch (err) {
|
|
1996
1995
|
return {
|
|
1997
1996
|
ok: false,
|
|
1998
|
-
|
|
1997
|
+
output: `Error: ${err instanceof Error ? err.message : String(err)}`
|
|
1999
1998
|
};
|
|
2000
1999
|
}
|
|
2001
2000
|
};
|
|
@@ -2074,6 +2073,8 @@ var streamAIWithTools = async function* (options) {
|
|
|
2074
2073
|
transcript: chunk.transcript,
|
|
2075
2074
|
type: "audio"
|
|
2076
2075
|
};
|
|
2076
|
+
} else if (chunk.type === "tool_input_start") {
|
|
2077
|
+
yield chunk;
|
|
2077
2078
|
} else if (chunk.type === "tool_use") {
|
|
2078
2079
|
thinking = flushThinking3(blocks, thinking);
|
|
2079
2080
|
pending.push({ id: chunk.id, input: chunk.input, name: chunk.name });
|
|
@@ -2140,7 +2141,8 @@ var streamAIWithTools = async function* (options) {
|
|
|
2140
2141
|
type: "tool_start"
|
|
2141
2142
|
};
|
|
2142
2143
|
const startedAt = Date.now();
|
|
2143
|
-
const { ok,
|
|
2144
|
+
const { ok, output } = await executeToolHandler(tools, call);
|
|
2145
|
+
const result = toolOutputText(output);
|
|
2144
2146
|
executedKeys.add(toolCallKey(call));
|
|
2145
2147
|
yield {
|
|
2146
2148
|
id: call.id,
|
|
@@ -2151,11 +2153,7 @@ var streamAIWithTools = async function* (options) {
|
|
|
2151
2153
|
result,
|
|
2152
2154
|
type: "tool_result"
|
|
2153
2155
|
};
|
|
2154
|
-
resultBlocks.push(
|
|
2155
|
-
content: result,
|
|
2156
|
-
tool_use_id: call.id,
|
|
2157
|
-
type: "tool_result"
|
|
2158
|
-
});
|
|
2156
|
+
resultBlocks.push(toolResultBlock(call.id, output));
|
|
2159
2157
|
if (ok && stopAfterTools?.includes(call.name)) {
|
|
2160
2158
|
terminalToolSucceeded = true;
|
|
2161
2159
|
break;
|
|
@@ -4874,7 +4872,7 @@ async function* tapStream(source, params, providerName) {
|
|
|
4874
4872
|
let lastUsage;
|
|
4875
4873
|
try {
|
|
4876
4874
|
for await (const chunk of source) {
|
|
4877
|
-
if (chunk.type === "done" && chunk.usage) {
|
|
4875
|
+
if ((chunk.type === "done" || chunk.type === "usage_update" && chunk.final) && chunk.usage) {
|
|
4878
4876
|
lastUsage = chunk.usage;
|
|
4879
4877
|
}
|
|
4880
4878
|
yield chunk;
|
|
@@ -5177,7 +5175,10 @@ var convertToolBlock = (block) => {
|
|
|
5177
5175
|
if (block.type === "tool_result") {
|
|
5178
5176
|
return {
|
|
5179
5177
|
call_id: block.tool_use_id,
|
|
5180
|
-
output:
|
|
5178
|
+
output: block.images?.length ? [
|
|
5179
|
+
{ text: block.content, type: "input_text" },
|
|
5180
|
+
...block.images.map((source) => mapBlockToResponsesFormat({ source, type: "image" }))
|
|
5181
|
+
] : block.content,
|
|
5181
5182
|
type: "function_call_output"
|
|
5182
5183
|
};
|
|
5183
5184
|
}
|
|
@@ -5487,6 +5488,8 @@ var processCompleted = function* (parsed) {
|
|
|
5487
5488
|
const { response } = parsed;
|
|
5488
5489
|
const usage = extractUsage(response);
|
|
5489
5490
|
const metadata = extractResponseMetadata(response);
|
|
5491
|
+
if (usage)
|
|
5492
|
+
yield { type: "usage_update", usage, metadata, final: true };
|
|
5490
5493
|
if (isRecordArray(response.output)) {
|
|
5491
5494
|
yield* extractCitationsFromOutput(response.output);
|
|
5492
5495
|
yield* extractImageFromOutput(response.output);
|
|
@@ -5525,6 +5528,15 @@ var processSSEEvent = function* (eventType, parsed, pendingCalls, providerName)
|
|
|
5525
5528
|
break;
|
|
5526
5529
|
case "response.output_item.added":
|
|
5527
5530
|
processOutputItemAdded(parsed, pendingCalls);
|
|
5531
|
+
if (isRecord2(parsed.item) && parsed.item.type === "function_call" && typeof parsed.item.name === "string") {
|
|
5532
|
+
const id = typeof parsed.item.call_id === "string" ? parsed.item.call_id : parsed.item.id;
|
|
5533
|
+
if (typeof id === "string")
|
|
5534
|
+
yield {
|
|
5535
|
+
type: "tool_input_start",
|
|
5536
|
+
id,
|
|
5537
|
+
name: parsed.item.name
|
|
5538
|
+
};
|
|
5539
|
+
}
|
|
5528
5540
|
break;
|
|
5529
5541
|
case "response.output_item.done":
|
|
5530
5542
|
yield* processOutputItemDone(parsed);
|
|
@@ -5541,8 +5553,19 @@ var processSSEEvent = function* (eventType, parsed, pendingCalls, providerName)
|
|
|
5541
5553
|
case "response.failed":
|
|
5542
5554
|
case "response.incomplete":
|
|
5543
5555
|
case "response.error":
|
|
5544
|
-
case "error":
|
|
5556
|
+
case "error": {
|
|
5557
|
+
if (isRecord2(parsed.response)) {
|
|
5558
|
+
const usage = extractUsage(parsed.response);
|
|
5559
|
+
if (usage)
|
|
5560
|
+
yield {
|
|
5561
|
+
type: "usage_update",
|
|
5562
|
+
usage,
|
|
5563
|
+
metadata: extractResponseMetadata(parsed.response),
|
|
5564
|
+
final: false
|
|
5565
|
+
};
|
|
5566
|
+
}
|
|
5545
5567
|
throw responseFailure(eventType, parsed, providerName);
|
|
5568
|
+
}
|
|
5546
5569
|
}
|
|
5547
5570
|
};
|
|
5548
5571
|
var flushSSEBuffer = function* (state) {
|
|
@@ -5685,6 +5708,7 @@ var NOT_FOUND4 = -1;
|
|
|
5685
5708
|
var isRecord3 = (value) => typeof value === "object" && value !== null;
|
|
5686
5709
|
var isRecordArray2 = (value) => Array.isArray(value) && value.length > 0 && isRecord3(value[0]);
|
|
5687
5710
|
var hasArrayContent = (msg) => typeof msg.content !== "string" && Array.isArray(msg.content);
|
|
5711
|
+
var imageUrl = (source) => source.type === "url" ? source.url : `data:${source.media_type};base64,${source.data}`;
|
|
5688
5712
|
var buildToolMessages = (blocks) => {
|
|
5689
5713
|
const toolUseBlocks = blocks.filter((block) => block.type === "tool_use");
|
|
5690
5714
|
const toolResultBlocks = blocks.filter((block) => block.type === "tool_result");
|
|
@@ -5710,6 +5734,18 @@ var buildToolMessages = (blocks) => {
|
|
|
5710
5734
|
tool_call_id: result.tool_use_id
|
|
5711
5735
|
});
|
|
5712
5736
|
}
|
|
5737
|
+
const images = toolResultBlocks.flatMap((result) => result.images ?? []);
|
|
5738
|
+
if (images.length > 0)
|
|
5739
|
+
messages.push({
|
|
5740
|
+
content: [
|
|
5741
|
+
{ text: "Images returned by the tool calls above:", type: "text" },
|
|
5742
|
+
...images.map((source) => ({
|
|
5743
|
+
image_url: { url: imageUrl(source) },
|
|
5744
|
+
type: "image_url"
|
|
5745
|
+
}))
|
|
5746
|
+
],
|
|
5747
|
+
role: "user"
|
|
5748
|
+
});
|
|
5713
5749
|
return messages;
|
|
5714
5750
|
};
|
|
5715
5751
|
var mapToolDefinitions = (tools) => tools.map((tool) => ({
|
|
@@ -5723,9 +5759,7 @@ var mapToolDefinitions = (tools) => tools.map((tool) => ({
|
|
|
5723
5759
|
var mapContentBlockToOpenAI = (block) => {
|
|
5724
5760
|
if (block.type === "image") {
|
|
5725
5761
|
return {
|
|
5726
|
-
image_url: {
|
|
5727
|
-
url: block.source.type === "url" ? block.source.url : `data:${block.source.media_type};base64,${block.source.data}`
|
|
5728
|
-
},
|
|
5762
|
+
image_url: { url: imageUrl(block.source) },
|
|
5729
5763
|
type: "image_url"
|
|
5730
5764
|
};
|
|
5731
5765
|
}
|
|
@@ -5964,6 +5998,14 @@ var processDelta = function* (delta, pendingToolCalls) {
|
|
|
5964
5998
|
}
|
|
5965
5999
|
if (isRecordArray2(delta.tool_calls)) {
|
|
5966
6000
|
processToolCallDeltas(delta.tool_calls, pendingToolCalls);
|
|
6001
|
+
for (const call of delta.tool_calls) {
|
|
6002
|
+
if (isRecord3(call.function) && typeof call.function.name === "string" && call.function.name)
|
|
6003
|
+
yield {
|
|
6004
|
+
type: "tool_input_start",
|
|
6005
|
+
id: typeof call.id === "string" ? call.id : String(call.index),
|
|
6006
|
+
name: call.function.name
|
|
6007
|
+
};
|
|
6008
|
+
}
|
|
5967
6009
|
}
|
|
5968
6010
|
if (Array.isArray(delta.annotations)) {
|
|
5969
6011
|
for (const annotation of delta.annotations) {
|
|
@@ -6062,7 +6104,12 @@ var processSSELine2 = function* (line, pendingToolCalls, providerName) {
|
|
|
6062
6104
|
}
|
|
6063
6105
|
const usageUpdate = narrowUsageRecord(parsed);
|
|
6064
6106
|
if (usageUpdate) {
|
|
6065
|
-
yield {
|
|
6107
|
+
yield {
|
|
6108
|
+
type: "usage_update",
|
|
6109
|
+
usage: usageUpdate,
|
|
6110
|
+
final: Array.isArray(parsed.choices) && parsed.choices.length === 0,
|
|
6111
|
+
metadata: narrowResponseMetadata(parsed)
|
|
6112
|
+
};
|
|
6066
6113
|
}
|
|
6067
6114
|
const metadata = narrowResponseMetadata(parsed);
|
|
6068
6115
|
if (metadata) {
|
|
@@ -6091,10 +6138,9 @@ var processSSELine2 = function* (line, pendingToolCalls, providerName) {
|
|
|
6091
6138
|
}
|
|
6092
6139
|
yield* processChoice(firstChoice, pendingToolCalls);
|
|
6093
6140
|
};
|
|
6094
|
-
var isUsageUpdate = (chunk) => chunk.type === "usage_update";
|
|
6095
6141
|
var collectYieldableChunks = (line, pendingToolCalls, usageRef, metadataRef, providerName) => {
|
|
6096
6142
|
const allChunks = Array.from(processSSELine2(line, pendingToolCalls, providerName));
|
|
6097
|
-
const usageChunks = allChunks.filter(
|
|
6143
|
+
const usageChunks = allChunks.filter((chunk) => chunk.type === "usage_update");
|
|
6098
6144
|
const lastUsage = usageChunks.at(NOT_FOUND4);
|
|
6099
6145
|
if (lastUsage) {
|
|
6100
6146
|
usageRef.current = lastUsage.usage;
|
|
@@ -6111,7 +6157,7 @@ var collectYieldableChunks = (line, pendingToolCalls, usageRef, metadataRef, pro
|
|
|
6111
6157
|
}
|
|
6112
6158
|
};
|
|
6113
6159
|
}
|
|
6114
|
-
return allChunks.filter((chunk) =>
|
|
6160
|
+
return allChunks.filter((chunk) => chunk.type !== "response_metadata").map((chunk) => chunk.type === "usage_update" ? { ...chunk, metadata: metadataRef.current } : chunk);
|
|
6115
6161
|
};
|
|
6116
6162
|
var processSSELines2 = function* (lines, pendingToolCalls, usageRef, metadataRef, providerName) {
|
|
6117
6163
|
for (const line of lines) {
|
|
@@ -6336,7 +6382,10 @@ var convertMessageContent = (content) => {
|
|
|
6336
6382
|
if (typeof content === "string") {
|
|
6337
6383
|
return [{ text: content }];
|
|
6338
6384
|
}
|
|
6339
|
-
return content.
|
|
6385
|
+
return content.flatMap((block) => block.type === "tool_result" && block.images?.length ? [
|
|
6386
|
+
mapContentBlock(block),
|
|
6387
|
+
...block.images.map((source) => mapContentBlock({ source, type: "image" }))
|
|
6388
|
+
] : [mapContentBlock(block)]).filter((mapped) => mapped !== null);
|
|
6340
6389
|
};
|
|
6341
6390
|
var hasFunctionResponse = (content) => content.some((block) => block.type === "tool_result");
|
|
6342
6391
|
var convertSingleMessage = (msg) => {
|
|
@@ -6613,7 +6662,6 @@ var API_VERSION = "2023-06-01";
|
|
|
6613
6662
|
var DEFAULT_MAX_TOKENS = 32000;
|
|
6614
6663
|
var EVENT_PREFIX_LENGTH2 = 7;
|
|
6615
6664
|
var DATA_PREFIX_LENGTH2 = 6;
|
|
6616
|
-
var EMPTY_CHUNKS = [];
|
|
6617
6665
|
var isRecord5 = (val) => typeof val === "object" && val !== null;
|
|
6618
6666
|
var mapContentBlock2 = (block) => {
|
|
6619
6667
|
if (block.type === "thinking") {
|
|
@@ -6637,7 +6685,10 @@ var mapContentBlock2 = (block) => {
|
|
|
6637
6685
|
}
|
|
6638
6686
|
if (block.type === "tool_result") {
|
|
6639
6687
|
return {
|
|
6640
|
-
content: block.
|
|
6688
|
+
content: block.images?.length ? [
|
|
6689
|
+
{ text: block.content, type: "text" },
|
|
6690
|
+
...block.images.map((source) => ({ source, type: "image" }))
|
|
6691
|
+
] : block.content,
|
|
6641
6692
|
tool_use_id: block.tool_use_id,
|
|
6642
6693
|
type: "tool_result"
|
|
6643
6694
|
};
|
|
@@ -7141,11 +7192,21 @@ var processEvent = (eventType, parsed, state) => {
|
|
|
7141
7192
|
case "message_delta": {
|
|
7142
7193
|
handleMessageDelta(parsed, state);
|
|
7143
7194
|
mergeMetadata(parsed, state);
|
|
7144
|
-
return
|
|
7195
|
+
return state.usage ? {
|
|
7196
|
+
type: "usage_update",
|
|
7197
|
+
usage: state.usage,
|
|
7198
|
+
metadata: state.metadata,
|
|
7199
|
+
final: false
|
|
7200
|
+
} : undefined;
|
|
7145
7201
|
}
|
|
7146
7202
|
case "message_start": {
|
|
7147
7203
|
handleMessageStart(parsed, state);
|
|
7148
|
-
return
|
|
7204
|
+
return state.usage ? {
|
|
7205
|
+
type: "usage_update",
|
|
7206
|
+
usage: state.usage,
|
|
7207
|
+
metadata: state.metadata,
|
|
7208
|
+
final: false
|
|
7209
|
+
} : undefined;
|
|
7149
7210
|
}
|
|
7150
7211
|
case "message_stop": {
|
|
7151
7212
|
mergeMetadata(parsed, state);
|
|
@@ -7179,49 +7240,6 @@ var processSingleEvent = (event, state) => {
|
|
|
7179
7240
|
}
|
|
7180
7241
|
return processEvent(eventType, parsed, state);
|
|
7181
7242
|
};
|
|
7182
|
-
var collectChunk = (event, state) => {
|
|
7183
|
-
const chunk = processSingleEvent(event, state);
|
|
7184
|
-
return chunk ? [chunk] : [];
|
|
7185
|
-
};
|
|
7186
|
-
var processBufferedEvents = (eventsText, state) => {
|
|
7187
|
-
const events = eventsText.split(`
|
|
7188
|
-
|
|
7189
|
-
`);
|
|
7190
|
-
state.buffer = events.pop() ?? "";
|
|
7191
|
-
return events.flatMap((event) => collectChunk(event, state));
|
|
7192
|
-
};
|
|
7193
|
-
var readNextChunks = async (reader, decoder, state, signal) => {
|
|
7194
|
-
if (signal?.aborted) {
|
|
7195
|
-
return { chunks: EMPTY_CHUNKS, done: true };
|
|
7196
|
-
}
|
|
7197
|
-
const { done, value } = await reader.read();
|
|
7198
|
-
if (done) {
|
|
7199
|
-
return { chunks: EMPTY_CHUNKS, done: true };
|
|
7200
|
-
}
|
|
7201
|
-
const rawText = state.buffer + decoder.decode(value, { stream: true });
|
|
7202
|
-
const chunks = processBufferedEvents(rawText, state);
|
|
7203
|
-
return { chunks, done: false };
|
|
7204
|
-
};
|
|
7205
|
-
var findDoneChunk = (chunks) => chunks.findIndex((c) => c.type === "done");
|
|
7206
|
-
var sseStreamLoop = async (reader, decoder, state, signal) => {
|
|
7207
|
-
const result = await readNextChunks(reader, decoder, state, signal);
|
|
7208
|
-
if (result.done) {
|
|
7209
|
-
return { chunks: result.chunks, finished: true };
|
|
7210
|
-
}
|
|
7211
|
-
const doneIdx = findDoneChunk(result.chunks);
|
|
7212
|
-
if (doneIdx >= 0) {
|
|
7213
|
-
return { chunks: result.chunks.slice(0, doneIdx + 1), finished: true };
|
|
7214
|
-
}
|
|
7215
|
-
return { chunks: result.chunks, finished: false };
|
|
7216
|
-
};
|
|
7217
|
-
async function* streamChunks(reader, decoder, state, signal) {
|
|
7218
|
-
let finished = false;
|
|
7219
|
-
while (!finished) {
|
|
7220
|
-
const result = await sseStreamLoop(reader, decoder, state, signal);
|
|
7221
|
-
({ finished } = result);
|
|
7222
|
-
yield* result.chunks;
|
|
7223
|
-
}
|
|
7224
|
-
}
|
|
7225
7243
|
async function* parseSSEStream4(body, providerName, signal) {
|
|
7226
7244
|
const reader = body.getReader();
|
|
7227
7245
|
const decoder = new TextDecoder;
|
|
@@ -7239,8 +7257,34 @@ async function* parseSSEStream4(body, providerName, signal) {
|
|
|
7239
7257
|
providerBlockInputJson: ""
|
|
7240
7258
|
};
|
|
7241
7259
|
try {
|
|
7242
|
-
|
|
7260
|
+
while (true) {
|
|
7261
|
+
signal?.throwIfAborted();
|
|
7262
|
+
const { done, value } = await reader.read();
|
|
7263
|
+
if (done)
|
|
7264
|
+
return;
|
|
7265
|
+
const events = (state.buffer + decoder.decode(value, { stream: true })).split(`
|
|
7266
|
+
|
|
7267
|
+
`);
|
|
7268
|
+
state.buffer = events.pop() ?? "";
|
|
7269
|
+
for (const event of events) {
|
|
7270
|
+
const chunk = processSingleEvent(event, state);
|
|
7271
|
+
if (!chunk)
|
|
7272
|
+
continue;
|
|
7273
|
+
if (chunk.type === "tool_input" && chunk.receivedChars === 0)
|
|
7274
|
+
yield {
|
|
7275
|
+
type: "tool_input_start",
|
|
7276
|
+
id: chunk.id,
|
|
7277
|
+
name: chunk.name
|
|
7278
|
+
};
|
|
7279
|
+
yield chunk;
|
|
7280
|
+
if (chunk.type === "done")
|
|
7281
|
+
return;
|
|
7282
|
+
}
|
|
7283
|
+
}
|
|
7243
7284
|
} finally {
|
|
7285
|
+
await reader.cancel().catch(() => {
|
|
7286
|
+
return;
|
|
7287
|
+
});
|
|
7244
7288
|
reader.releaseLock();
|
|
7245
7289
|
}
|
|
7246
7290
|
}
|
|
@@ -7390,8 +7434,10 @@ var convertMessage2 = (msg) => {
|
|
|
7390
7434
|
});
|
|
7391
7435
|
}
|
|
7392
7436
|
for (const block of toolResultBlocks) {
|
|
7437
|
+
const images = (block.images ?? []).flatMap((source) => source.type === "base64" ? [source.data] : []);
|
|
7393
7438
|
results.push({
|
|
7394
7439
|
content: typeof block.content === "string" ? block.content : "",
|
|
7440
|
+
...images.length > 0 ? { images } : {},
|
|
7395
7441
|
role: "tool"
|
|
7396
7442
|
});
|
|
7397
7443
|
}
|
|
@@ -8998,6 +9044,12 @@ var createAIStoredToolResultRecovery = (toolNames) => async ({ params }) => {
|
|
|
8998
9044
|
if (block.type !== "tool_result" || !eligible.has(block.tool_use_id) || block === newest)
|
|
8999
9045
|
return block;
|
|
9000
9046
|
const marker = "[Earlier lookup removed from working context. Its immutable original remains saved. The original tool call and source/version arguments above are unchanged: repeat that lookup if needed to verify facts. Do not infer facts from this marker.]";
|
|
9047
|
+
if (block.images?.length)
|
|
9048
|
+
return {
|
|
9049
|
+
content: marker,
|
|
9050
|
+
tool_use_id: block.tool_use_id,
|
|
9051
|
+
type: block.type
|
|
9052
|
+
};
|
|
9001
9053
|
return block.content.length > marker.length ? { ...block, content: marker } : block;
|
|
9002
9054
|
})
|
|
9003
9055
|
}));
|
|
@@ -9139,10 +9191,11 @@ export {
|
|
|
9139
9191
|
streamAIWithTools,
|
|
9140
9192
|
streamWithAIContext,
|
|
9141
9193
|
tableCard,
|
|
9194
|
+
toolOutputText,
|
|
9142
9195
|
verifyOpenRouterWebhookSignature,
|
|
9143
9196
|
withResilience,
|
|
9144
9197
|
xai
|
|
9145
9198
|
};
|
|
9146
9199
|
|
|
9147
|
-
//# debugId=
|
|
9200
|
+
//# debugId=94F48F9A5CBF872A64756E2164756E21
|
|
9148
9201
|
//# sourceMappingURL=index.js.map
|