@absolutejs/ai 0.0.24 → 0.0.26
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/ai/index.js +139 -22
- package/dist/ai/index.js.map +9 -9
- package/dist/ai/providers/anthropic.js +56 -19
- package/dist/ai/providers/anthropic.js.map +4 -4
- package/dist/ai/providers/gemini.js.map +2 -2
- package/dist/ai/providers/ollama.js.map +2 -2
- package/dist/ai/providers/openai.js +2 -7
- package/dist/ai/providers/openai.js.map +3 -3
- package/dist/ai/providers/openaiCompatible.js +2 -7
- package/dist/ai/providers/openaiCompatible.js.map +3 -3
- package/dist/ai/providers/openaiResponses.js +2 -7
- package/dist/ai/providers/openaiResponses.js.map +4 -4
- package/dist/ai/tools/index.js +8 -3
- package/dist/ai/tools/index.js.map +4 -4
- package/dist/src/ai/streamAIToSSE.d.ts +4 -2
- package/dist/types/ai.d.ts +23 -0
- package/dist/types/anthropic.d.ts +9 -0
- package/package.json +172 -172
package/dist/ai/index.js
CHANGED
|
@@ -292,12 +292,7 @@ var EFFORT_ORDER = [
|
|
|
292
292
|
"high",
|
|
293
293
|
"max"
|
|
294
294
|
];
|
|
295
|
-
var ANTHROPIC_EFFORT = [
|
|
296
|
-
/opus-4-[5-8]/,
|
|
297
|
-
/sonnet-4-6/,
|
|
298
|
-
/fable-5/,
|
|
299
|
-
/mythos-5/
|
|
300
|
-
];
|
|
295
|
+
var ANTHROPIC_EFFORT = [/opus-4-[5-8]/, /sonnet-4-6/, /fable-5/, /mythos-5/];
|
|
301
296
|
var ANTHROPIC_ADAPTIVE_ONLY = [];
|
|
302
297
|
var ANTHROPIC_LEGACY_THINKING = [
|
|
303
298
|
/sonnet-4-5/,
|
|
@@ -1526,7 +1521,7 @@ var gemini = (config2) => {
|
|
|
1526
1521
|
var h2IfHttps4 = (url) => url.startsWith("https://") ? { protocol: "http2" } : {};
|
|
1527
1522
|
var DEFAULT_BASE_URL4 = "https://api.anthropic.com";
|
|
1528
1523
|
var API_VERSION = "2023-06-01";
|
|
1529
|
-
var
|
|
1524
|
+
var DEFAULT_MAX_TOKENS = 32000;
|
|
1530
1525
|
var EVENT_PREFIX_LENGTH2 = 7;
|
|
1531
1526
|
var DATA_PREFIX_LENGTH2 = 6;
|
|
1532
1527
|
var EMPTY_CHUNKS = [];
|
|
@@ -1577,16 +1572,42 @@ var mapToolDefinition2 = (tool) => ({
|
|
|
1577
1572
|
input_schema: tool.input_schema,
|
|
1578
1573
|
name: tool.name
|
|
1579
1574
|
});
|
|
1580
|
-
var
|
|
1575
|
+
var cacheLastContentBlock = (msg) => {
|
|
1576
|
+
const cacheControl = { type: "ephemeral" };
|
|
1577
|
+
if (typeof msg.content === "string") {
|
|
1578
|
+
return {
|
|
1579
|
+
content: [
|
|
1580
|
+
{ cache_control: cacheControl, text: msg.content, type: "text" }
|
|
1581
|
+
],
|
|
1582
|
+
role: msg.role
|
|
1583
|
+
};
|
|
1584
|
+
}
|
|
1585
|
+
if (msg.content.length === 0)
|
|
1586
|
+
return msg;
|
|
1587
|
+
const blocks = [...msg.content];
|
|
1588
|
+
blocks[blocks.length - 1] = {
|
|
1589
|
+
...blocks[blocks.length - 1],
|
|
1590
|
+
cache_control: cacheControl
|
|
1591
|
+
};
|
|
1592
|
+
return { content: blocks, role: msg.role };
|
|
1593
|
+
};
|
|
1594
|
+
var buildRequestBody4 = (params, configuredMax, promptCaching) => {
|
|
1581
1595
|
const messages = params.messages.filter((msg) => msg.role !== "system").map(mapMessage);
|
|
1596
|
+
if (promptCaching && messages.length > 1) {
|
|
1597
|
+
const last = messages[messages.length - 1];
|
|
1598
|
+
if (last) {
|
|
1599
|
+
messages[messages.length - 1] = cacheLastContentBlock(last);
|
|
1600
|
+
}
|
|
1601
|
+
}
|
|
1602
|
+
const max = typeof params.maxTokens === "number" ? params.maxTokens : configuredMax;
|
|
1582
1603
|
const body = {
|
|
1583
|
-
max_tokens:
|
|
1604
|
+
max_tokens: max,
|
|
1584
1605
|
messages,
|
|
1585
1606
|
model: params.model,
|
|
1586
1607
|
stream: true
|
|
1587
1608
|
};
|
|
1588
1609
|
if (params.systemPrompt) {
|
|
1589
|
-
body.system = params.cacheSystemPrompt ? [
|
|
1610
|
+
body.system = promptCaching || params.cacheSystemPrompt ? [
|
|
1590
1611
|
{
|
|
1591
1612
|
cache_control: { type: "ephemeral" },
|
|
1592
1613
|
text: params.systemPrompt,
|
|
@@ -1595,7 +1616,14 @@ var buildRequestBody4 = (params) => {
|
|
|
1595
1616
|
] : params.systemPrompt;
|
|
1596
1617
|
}
|
|
1597
1618
|
if (params.tools && params.tools.length > 0) {
|
|
1598
|
-
|
|
1619
|
+
const tools = params.tools.map(mapToolDefinition2);
|
|
1620
|
+
if (promptCaching) {
|
|
1621
|
+
tools[tools.length - 1] = {
|
|
1622
|
+
...tools[tools.length - 1],
|
|
1623
|
+
cache_control: { type: "ephemeral" }
|
|
1624
|
+
};
|
|
1625
|
+
}
|
|
1626
|
+
body.tools = tools;
|
|
1599
1627
|
if (params.toolChoice === "auto" || params.toolChoice === "none") {
|
|
1600
1628
|
body.tool_choice = { type: params.toolChoice };
|
|
1601
1629
|
} else if (params.toolChoice === "required") {
|
|
@@ -1604,8 +1632,6 @@ var buildRequestBody4 = (params) => {
|
|
|
1604
1632
|
body.tool_choice = { name: params.toolChoice.name, type: "tool" };
|
|
1605
1633
|
}
|
|
1606
1634
|
}
|
|
1607
|
-
if (typeof params.maxTokens === "number")
|
|
1608
|
-
body.max_tokens = params.maxTokens;
|
|
1609
1635
|
if (params.stopSequences && params.stopSequences.length > 0) {
|
|
1610
1636
|
body.stop_sequences = params.stopSequences;
|
|
1611
1637
|
}
|
|
@@ -1629,7 +1655,7 @@ var buildRequestBody4 = (params) => {
|
|
|
1629
1655
|
const budget = resolveBudgetTokens(params.reasoning);
|
|
1630
1656
|
if (budget) {
|
|
1631
1657
|
body.thinking = { budget_tokens: budget, type: "enabled" };
|
|
1632
|
-
body.max_tokens = Math.max(
|
|
1658
|
+
body.max_tokens = Math.max(max, budget + max);
|
|
1633
1659
|
}
|
|
1634
1660
|
}
|
|
1635
1661
|
return body;
|
|
@@ -1778,6 +1804,10 @@ var extractUsage4 = (usageRecord, existingUsage) => {
|
|
|
1778
1804
|
var handleMessageDelta = (parsed, state) => {
|
|
1779
1805
|
const deltaUsage = getRecord(parsed, "usage");
|
|
1780
1806
|
state.usage = extractUsage4(deltaUsage, state.usage);
|
|
1807
|
+
const delta = getRecord(parsed, "delta");
|
|
1808
|
+
const stopReason = delta ? getString(delta, "stop_reason") : "";
|
|
1809
|
+
if (stopReason)
|
|
1810
|
+
state.stopReason = stopReason;
|
|
1781
1811
|
};
|
|
1782
1812
|
var handleMessageStart = (parsed, state) => {
|
|
1783
1813
|
const message = getRecord(parsed, "message");
|
|
@@ -1820,7 +1850,11 @@ var processEvent = (eventType, parsed, state) => {
|
|
|
1820
1850
|
return;
|
|
1821
1851
|
}
|
|
1822
1852
|
case "message_stop": {
|
|
1823
|
-
return {
|
|
1853
|
+
return {
|
|
1854
|
+
stopReason: state.stopReason,
|
|
1855
|
+
type: "done",
|
|
1856
|
+
usage: state.usage
|
|
1857
|
+
};
|
|
1824
1858
|
}
|
|
1825
1859
|
case "error": {
|
|
1826
1860
|
handleError(parsed);
|
|
@@ -1896,6 +1930,7 @@ async function* parseSSEStream4(body, signal) {
|
|
|
1896
1930
|
currentToolId: "",
|
|
1897
1931
|
currentToolName: "",
|
|
1898
1932
|
isThinkingBlock: false,
|
|
1933
|
+
stopReason: "",
|
|
1899
1934
|
thinkingSignature: "",
|
|
1900
1935
|
toolInputJson: "",
|
|
1901
1936
|
usage: undefined
|
|
@@ -1906,8 +1941,8 @@ async function* parseSSEStream4(body, signal) {
|
|
|
1906
1941
|
reader.releaseLock();
|
|
1907
1942
|
}
|
|
1908
1943
|
}
|
|
1909
|
-
var fetchAndStream = async function* (baseUrl, config2, params) {
|
|
1910
|
-
const body = buildRequestBody4(params);
|
|
1944
|
+
var fetchAndStream = async function* (baseUrl, config2, params, configuredMax, promptCaching) {
|
|
1945
|
+
const body = buildRequestBody4(params, configuredMax, promptCaching);
|
|
1911
1946
|
const target = `${baseUrl}/v1/messages`;
|
|
1912
1947
|
const response = await fetch(target, {
|
|
1913
1948
|
...h2IfHttps4(target),
|
|
@@ -1935,8 +1970,10 @@ var fetchAndStream = async function* (baseUrl, config2, params) {
|
|
|
1935
1970
|
};
|
|
1936
1971
|
var anthropic = (config2) => {
|
|
1937
1972
|
const baseUrl = config2.baseUrl ?? DEFAULT_BASE_URL4;
|
|
1973
|
+
const configuredMax = config2.maxTokens ?? DEFAULT_MAX_TOKENS;
|
|
1974
|
+
const promptCaching = config2.promptCaching ?? true;
|
|
1938
1975
|
return instrumentAIProvider({
|
|
1939
|
-
stream: (params) => fetchAndStream(baseUrl, config2, params)
|
|
1976
|
+
stream: (params) => fetchAndStream(baseUrl, config2, params, configuredMax, promptCaching)
|
|
1940
1977
|
}, "anthropic");
|
|
1941
1978
|
};
|
|
1942
1979
|
|
|
@@ -2672,6 +2709,42 @@ var handleStreamError = async (socket, err, messageId, conversationId, signal, s
|
|
|
2672
2709
|
|
|
2673
2710
|
// src/ai/streamAIToSSE.ts
|
|
2674
2711
|
var DEFAULT_MAX_TURNS2 = 10;
|
|
2712
|
+
var DEFAULT_HEARTBEAT_MS = 15000;
|
|
2713
|
+
var HEARTBEAT_TICK = Symbol("heartbeat-tick");
|
|
2714
|
+
var withHeartbeat = async function* (source, signal, intervalMs) {
|
|
2715
|
+
if (!Number.isFinite(intervalMs) || intervalMs <= 0) {
|
|
2716
|
+
yield* source;
|
|
2717
|
+
return;
|
|
2718
|
+
}
|
|
2719
|
+
try {
|
|
2720
|
+
let next = source.next();
|
|
2721
|
+
for (;; ) {
|
|
2722
|
+
let timer;
|
|
2723
|
+
const tick = new Promise((resolve) => {
|
|
2724
|
+
timer = setTimeout(() => resolve(HEARTBEAT_TICK), intervalMs);
|
|
2725
|
+
});
|
|
2726
|
+
let winner;
|
|
2727
|
+
try {
|
|
2728
|
+
winner = await Promise.race([next, tick]);
|
|
2729
|
+
} finally {
|
|
2730
|
+
if (timer)
|
|
2731
|
+
clearTimeout(timer);
|
|
2732
|
+
}
|
|
2733
|
+
if (winner === HEARTBEAT_TICK) {
|
|
2734
|
+
if (signal.aborted)
|
|
2735
|
+
return;
|
|
2736
|
+
yield { data: "", event: "ping" };
|
|
2737
|
+
continue;
|
|
2738
|
+
}
|
|
2739
|
+
if (winner.done)
|
|
2740
|
+
return;
|
|
2741
|
+
yield winner.value;
|
|
2742
|
+
next = source.next();
|
|
2743
|
+
}
|
|
2744
|
+
} finally {
|
|
2745
|
+
await source.return?.(undefined);
|
|
2746
|
+
}
|
|
2747
|
+
};
|
|
2675
2748
|
var buildToolDefinitions2 = (tools) => Object.entries(tools).map(([name, def]) => ({
|
|
2676
2749
|
description: def.description,
|
|
2677
2750
|
input_schema: def.input,
|
|
@@ -2695,7 +2768,7 @@ var streamAIToSSE = async function* (conversationId, messageId, options, rendere
|
|
|
2695
2768
|
const maxTurns = options.maxTurns ?? DEFAULT_MAX_TURNS2;
|
|
2696
2769
|
const messages = options.messages ? [...options.messages] : [];
|
|
2697
2770
|
try {
|
|
2698
|
-
yield* streamTurns(options, renderers, messages, signal, startTime, maxTurns);
|
|
2771
|
+
yield* withHeartbeat(streamTurns(options, renderers, messages, signal, startTime, maxTurns), signal, options.heartbeatMs ?? DEFAULT_HEARTBEAT_MS);
|
|
2699
2772
|
} catch (err) {
|
|
2700
2773
|
if (signal.aborted)
|
|
2701
2774
|
return;
|
|
@@ -2790,9 +2863,21 @@ var processChunk2 = function* (chunk, chunkState, renderers, options, fullRespon
|
|
|
2790
2863
|
case "done":
|
|
2791
2864
|
maybeFlushThinking(chunkState);
|
|
2792
2865
|
chunkState.usage = chunk.usage;
|
|
2866
|
+
chunkState.stopReason = chunk.stopReason;
|
|
2793
2867
|
break;
|
|
2794
2868
|
}
|
|
2795
2869
|
};
|
|
2870
|
+
var truncateToolResult = (result, max) => {
|
|
2871
|
+
if (result.length <= max)
|
|
2872
|
+
return result;
|
|
2873
|
+
const omitted = result.length - max;
|
|
2874
|
+
const headLen = Math.ceil(max / 2);
|
|
2875
|
+
const tailLen = max - headLen;
|
|
2876
|
+
const marker = `
|
|
2877
|
+
<result truncated to ${max} chars; ${omitted} omitted \u2014 re-read a narrower slice if needed>
|
|
2878
|
+
`;
|
|
2879
|
+
return result.slice(0, headLen) + marker + result.slice(result.length - tailLen);
|
|
2880
|
+
};
|
|
2796
2881
|
var executeToolCalls = async function* (pendingToolCalls, options, renderers, turnState) {
|
|
2797
2882
|
const toolResultBlocks = [];
|
|
2798
2883
|
for (const toolCall of pendingToolCalls) {
|
|
@@ -2802,8 +2887,9 @@ var executeToolCalls = async function* (pendingToolCalls, options, renderers, tu
|
|
|
2802
2887
|
turnState.allToolsHtml = turnState.allToolsHtml.replace(renderers.toolRunning(toolCall.name, toolCall.input), renderers.toolComplete(toolCall.name, result));
|
|
2803
2888
|
yield { data: turnState.allToolsHtml, event: "tools" };
|
|
2804
2889
|
options.onToolUse?.(toolCall.name, toolCall.input, result);
|
|
2890
|
+
const resultContent = options.maxToolResultChars !== undefined ? truncateToolResult(result, options.maxToolResultChars) : result;
|
|
2805
2891
|
toolResultBlocks.push({
|
|
2806
|
-
content:
|
|
2892
|
+
content: resultContent,
|
|
2807
2893
|
tool_use_id: toolCall.id,
|
|
2808
2894
|
type: "tool_result"
|
|
2809
2895
|
});
|
|
@@ -2848,14 +2934,17 @@ var streamTurns = async function* (options, renderers, messages, signal, startTi
|
|
|
2848
2934
|
turn: 0
|
|
2849
2935
|
};
|
|
2850
2936
|
const toolDefs = options.tools ? buildToolDefinitions2(options.tools) : undefined;
|
|
2937
|
+
let runningTotalTokens = 0;
|
|
2851
2938
|
for (;turnState.turn <= maxTurns && !signal.aborted; turnState.turn++) {
|
|
2852
2939
|
const chunkState = {
|
|
2853
2940
|
contentBlocks: [],
|
|
2854
2941
|
currentThinking: null,
|
|
2855
2942
|
pendingToolCalls: [],
|
|
2943
|
+
stopReason: undefined,
|
|
2856
2944
|
usage: undefined
|
|
2857
2945
|
};
|
|
2858
2946
|
const stream = options.provider.stream({
|
|
2947
|
+
maxTokens: options.maxTokens,
|
|
2859
2948
|
messages: turnState.currentMessages,
|
|
2860
2949
|
model: options.model,
|
|
2861
2950
|
reasoning: options.reasoning,
|
|
@@ -2864,6 +2953,29 @@ var streamTurns = async function* (options, renderers, messages, signal, startTi
|
|
|
2864
2953
|
tools: toolDefs
|
|
2865
2954
|
});
|
|
2866
2955
|
yield* consumeStream2(stream, chunkState, renderers, options, turnState, signal);
|
|
2956
|
+
options.onTurn?.(turnState.turn, chunkState.usage);
|
|
2957
|
+
runningTotalTokens += (chunkState.usage?.inputTokens ?? 0) + (chunkState.usage?.outputTokens ?? 0);
|
|
2958
|
+
if (chunkState.stopReason === "max_tokens") {
|
|
2959
|
+
yield {
|
|
2960
|
+
data: renderers.error(`Response truncated at max_tokens (output=${chunkState.usage?.outputTokens ?? "?"}). ` + `Raise maxTokens on the provider/options, split the request, or reduce upstream context.`),
|
|
2961
|
+
event: "status"
|
|
2962
|
+
};
|
|
2963
|
+
return;
|
|
2964
|
+
}
|
|
2965
|
+
if (options.maxTotalTokens && runningTotalTokens >= options.maxTotalTokens) {
|
|
2966
|
+
yield {
|
|
2967
|
+
data: renderers.error(`Stopped: token budget reached (${runningTotalTokens}/${options.maxTotalTokens} tokens over ` + `${turnState.turn} turns). Narrow the request or raise maxTotalTokens.`),
|
|
2968
|
+
event: "status"
|
|
2969
|
+
};
|
|
2970
|
+
return;
|
|
2971
|
+
}
|
|
2972
|
+
if (options.maxDurationMs && Date.now() - startTime >= options.maxDurationMs) {
|
|
2973
|
+
yield {
|
|
2974
|
+
data: renderers.error(`Stopped: time budget reached (${Math.round((Date.now() - startTime) / 1000)}s over ` + `${turnState.turn} turns). Narrow the request or raise maxDurationMs.`),
|
|
2975
|
+
event: "status"
|
|
2976
|
+
};
|
|
2977
|
+
return;
|
|
2978
|
+
}
|
|
2867
2979
|
if (shouldStopToolLoop(chunkState, turnState, signal)) {
|
|
2868
2980
|
return void (yield yieldCompletion(renderers, options, turnState.fullResponse, chunkState.usage, startTime));
|
|
2869
2981
|
}
|
|
@@ -3253,7 +3365,12 @@ var toProviderTools = (tools) => Object.entries(tools).map(([name, definition])
|
|
|
3253
3365
|
name
|
|
3254
3366
|
}));
|
|
3255
3367
|
var generateAIWithTools = async (options) => {
|
|
3256
|
-
const {
|
|
3368
|
+
const {
|
|
3369
|
+
maxTurns = DEFAULT_TOOL_MAX_TURNS,
|
|
3370
|
+
onToolUse,
|
|
3371
|
+
tools,
|
|
3372
|
+
...base
|
|
3373
|
+
} = options;
|
|
3257
3374
|
const providerTools = toProviderTools(tools);
|
|
3258
3375
|
const toolCalls = [];
|
|
3259
3376
|
let usage;
|
|
@@ -4283,5 +4400,5 @@ export {
|
|
|
4283
4400
|
PROVIDER_STATUS_PAGES
|
|
4284
4401
|
};
|
|
4285
4402
|
|
|
4286
|
-
//# debugId=
|
|
4403
|
+
//# debugId=90792F1972AEBAA464756E2164756E21
|
|
4287
4404
|
//# sourceMappingURL=index.js.map
|