@absolutejs/ai 0.0.54 → 0.0.55
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +25 -0
- package/dist/ai/index.js +86 -9
- package/dist/ai/index.js.map +4 -4
- package/dist/ai/providers/anthropic.js +76 -5
- package/dist/ai/providers/anthropic.js.map +3 -3
- package/dist/ai/providers/openrouter.js +79 -8
- package/dist/ai/providers/openrouter.js.map +3 -3
- package/dist/src/ai/index.d.ts +1 -0
- package/dist/types/anthropic.d.ts +26 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -5,6 +5,31 @@ Standalone AI runtime and provider package extracted from AbsoluteJS.
|
|
|
5
5
|
This package currently focuses on generic AI/chat/provider functionality.
|
|
6
6
|
RAG remains a separate package.
|
|
7
7
|
|
|
8
|
+
## Anthropic hosted web search
|
|
9
|
+
|
|
10
|
+
The Anthropic provider can request native hosted web search without a custom
|
|
11
|
+
client tool. Search blocks are replayable provider data, citations are emitted
|
|
12
|
+
as portable `citation` chunks, usage includes `serverToolUse`, and
|
|
13
|
+
`streamAIWithTools` continues `pause_turn` responses automatically.
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
const result = await generateAI({
|
|
17
|
+
provider: anthropic({ apiKey: process.env.ANTHROPIC_API_KEY }),
|
|
18
|
+
model: "claude-sonnet-4-6",
|
|
19
|
+
messages: [{ role: "user", content: "What changed today?" }],
|
|
20
|
+
providerOptions: {
|
|
21
|
+
anthropic: {
|
|
22
|
+
serverTools: [
|
|
23
|
+
{
|
|
24
|
+
type: "anthropic:web_search",
|
|
25
|
+
parameters: { maxUses: 3 },
|
|
26
|
+
},
|
|
27
|
+
],
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
});
|
|
31
|
+
```
|
|
32
|
+
|
|
8
33
|
Provider traffic can cross a trusted control plane without reimplementing a
|
|
9
34
|
vendor protocol. `remoteProvider()` carries normalized provider parameters and
|
|
10
35
|
chunks over SSE, while `createProviderProxyResponse()` hosts any
|
package/dist/ai/index.js
CHANGED
|
@@ -1870,6 +1870,61 @@ var mapToolDefinition2 = (tool) => ({
|
|
|
1870
1870
|
input_schema: tool.input_schema,
|
|
1871
1871
|
name: tool.name
|
|
1872
1872
|
});
|
|
1873
|
+
var requestOptionsFor = (params) => {
|
|
1874
|
+
const raw = params.providerOptions?.anthropic;
|
|
1875
|
+
if (raw === undefined)
|
|
1876
|
+
return {};
|
|
1877
|
+
if (!isRecord4(raw)) {
|
|
1878
|
+
throw new Error("providerOptions.anthropic must be an object");
|
|
1879
|
+
}
|
|
1880
|
+
if (raw.serverTools !== undefined && !Array.isArray(raw.serverTools)) {
|
|
1881
|
+
throw new Error("Anthropic serverTools must be an array");
|
|
1882
|
+
}
|
|
1883
|
+
return raw;
|
|
1884
|
+
};
|
|
1885
|
+
var assertDomains = (domains) => {
|
|
1886
|
+
for (const domain of domains ?? []) {
|
|
1887
|
+
if (!domain || domain.includes("://")) {
|
|
1888
|
+
throw new Error("Anthropic web-search domains must be non-empty bare domains without a scheme");
|
|
1889
|
+
}
|
|
1890
|
+
}
|
|
1891
|
+
};
|
|
1892
|
+
var mapServerTool = (tool) => {
|
|
1893
|
+
const parameters = tool.parameters ?? {};
|
|
1894
|
+
if (parameters.allowedDomains && parameters.blockedDomains) {
|
|
1895
|
+
throw new Error("Anthropic web search accepts allowedDomains or blockedDomains, not both");
|
|
1896
|
+
}
|
|
1897
|
+
assertDomains(parameters.allowedDomains);
|
|
1898
|
+
assertDomains(parameters.blockedDomains);
|
|
1899
|
+
if (parameters.maxUses !== undefined && (!Number.isInteger(parameters.maxUses) || parameters.maxUses < 1)) {
|
|
1900
|
+
throw new Error("Anthropic web-search maxUses must be a positive integer");
|
|
1901
|
+
}
|
|
1902
|
+
if (parameters.responseInclusion !== undefined && parameters.version !== "web_search_20260318") {
|
|
1903
|
+
throw new Error("Anthropic web-search responseInclusion requires web_search_20260318");
|
|
1904
|
+
}
|
|
1905
|
+
const location = parameters.userLocation;
|
|
1906
|
+
if (location && !location.city && !location.country && !location.region && !location.timezone) {
|
|
1907
|
+
throw new Error("Anthropic web-search userLocation requires a city, region, country, or timezone");
|
|
1908
|
+
}
|
|
1909
|
+
return {
|
|
1910
|
+
...parameters.allowedCallers ? { allowed_callers: [...parameters.allowedCallers] } : {},
|
|
1911
|
+
...parameters.allowedDomains ? { allowed_domains: [...parameters.allowedDomains] } : {},
|
|
1912
|
+
...parameters.blockedDomains ? { blocked_domains: [...parameters.blockedDomains] } : {},
|
|
1913
|
+
...parameters.maxUses === undefined ? {} : { max_uses: parameters.maxUses },
|
|
1914
|
+
name: "web_search",
|
|
1915
|
+
...parameters.responseInclusion ? { response_inclusion: parameters.responseInclusion } : {},
|
|
1916
|
+
type: parameters.version ?? "web_search_20250305",
|
|
1917
|
+
...location ? {
|
|
1918
|
+
user_location: {
|
|
1919
|
+
...location.city ? { city: location.city } : {},
|
|
1920
|
+
...location.country ? { country: location.country } : {},
|
|
1921
|
+
...location.region ? { region: location.region } : {},
|
|
1922
|
+
...location.timezone ? { timezone: location.timezone } : {},
|
|
1923
|
+
type: "approximate"
|
|
1924
|
+
}
|
|
1925
|
+
} : {}
|
|
1926
|
+
};
|
|
1927
|
+
};
|
|
1873
1928
|
var cacheLastContentBlock = (msg) => {
|
|
1874
1929
|
const cacheControl = { type: "ephemeral" };
|
|
1875
1930
|
if (typeof msg.content === "string") {
|
|
@@ -1892,6 +1947,7 @@ var cacheLastContentBlock = (msg) => {
|
|
|
1892
1947
|
var buildRequestBody4 = (params, configuredMax, configCaching) => {
|
|
1893
1948
|
const caching = params.promptCaching ?? configCaching;
|
|
1894
1949
|
const cacheSystem = params.cacheSystemPrompt ?? caching;
|
|
1950
|
+
const options = requestOptionsFor(params);
|
|
1895
1951
|
const messages = params.messages.filter((msg) => msg.role !== "system").map(mapMessage);
|
|
1896
1952
|
if (caching && messages.length > 1) {
|
|
1897
1953
|
const last = messages[messages.length - 1];
|
|
@@ -1915,14 +1971,18 @@ var buildRequestBody4 = (params, configuredMax, configCaching) => {
|
|
|
1915
1971
|
}
|
|
1916
1972
|
] : params.systemPrompt;
|
|
1917
1973
|
}
|
|
1918
|
-
|
|
1919
|
-
|
|
1974
|
+
const clientTools = params.tools?.map(mapToolDefinition2) ?? [];
|
|
1975
|
+
if (clientTools.length > 0) {
|
|
1920
1976
|
if (caching) {
|
|
1921
|
-
|
|
1922
|
-
...
|
|
1977
|
+
clientTools[clientTools.length - 1] = {
|
|
1978
|
+
...clientTools[clientTools.length - 1],
|
|
1923
1979
|
cache_control: { type: "ephemeral" }
|
|
1924
1980
|
};
|
|
1925
1981
|
}
|
|
1982
|
+
}
|
|
1983
|
+
const serverTools = (options.serverTools ?? []).map(mapServerTool);
|
|
1984
|
+
const tools = [...clientTools, ...serverTools];
|
|
1985
|
+
if (tools.length > 0) {
|
|
1926
1986
|
body.tools = tools;
|
|
1927
1987
|
if (params.toolChoice === "auto" || params.toolChoice === "none") {
|
|
1928
1988
|
body.tool_choice = { type: params.toolChoice };
|
|
@@ -2060,6 +2120,17 @@ var handleContentBlockDelta = (parsed, state) => {
|
|
|
2060
2120
|
type: "text"
|
|
2061
2121
|
};
|
|
2062
2122
|
}
|
|
2123
|
+
if (delta.type === "citations_delta") {
|
|
2124
|
+
const citation = getRecord(delta, "citation");
|
|
2125
|
+
if (citation?.type === "web_search_result_location" && getString(citation, "url")) {
|
|
2126
|
+
return {
|
|
2127
|
+
content: getString(citation, "cited_text") || undefined,
|
|
2128
|
+
title: getString(citation, "title") || undefined,
|
|
2129
|
+
type: "citation",
|
|
2130
|
+
url: getString(citation, "url")
|
|
2131
|
+
};
|
|
2132
|
+
}
|
|
2133
|
+
}
|
|
2063
2134
|
if (delta.type === "input_json_delta") {
|
|
2064
2135
|
if (state.currentProviderBlock) {
|
|
2065
2136
|
state.providerBlockInputJson += getString(delta, "partial_json");
|
|
@@ -3163,7 +3234,7 @@ var mapRouting = (routing, allowedProviders) => {
|
|
|
3163
3234
|
wire.zdr = routing.zdr;
|
|
3164
3235
|
return wire;
|
|
3165
3236
|
};
|
|
3166
|
-
var
|
|
3237
|
+
var requestOptionsFor2 = (params, defaults) => {
|
|
3167
3238
|
const supplied = params.providerOptions?.openrouter;
|
|
3168
3239
|
if (supplied !== undefined && (typeof supplied !== "object" || !supplied)) {
|
|
3169
3240
|
throw new Error("providerOptions.openrouter must be an object");
|
|
@@ -3183,7 +3254,7 @@ var resolveAttributionHeaders = async (config2, params) => {
|
|
|
3183
3254
|
if (config2.appCategories?.length) {
|
|
3184
3255
|
headers.set("X-OpenRouter-Categories", config2.appCategories.join(","));
|
|
3185
3256
|
}
|
|
3186
|
-
const options =
|
|
3257
|
+
const options = requestOptionsFor2(params, config2.requestOptions);
|
|
3187
3258
|
if (options.routerMetadata ?? true)
|
|
3188
3259
|
headers.set("X-OpenRouter-Metadata", "enabled");
|
|
3189
3260
|
if (options.sessionId)
|
|
@@ -3348,7 +3419,7 @@ var snapshotPolicy = (config2) => ({
|
|
|
3348
3419
|
allowedPresets: config2.allowedPresets ? [...config2.allowedPresets] : undefined
|
|
3349
3420
|
});
|
|
3350
3421
|
var transformOpenRouterRequest = (config2, allowedModels, allowedPresets, body, params, skin = "openai") => {
|
|
3351
|
-
const options =
|
|
3422
|
+
const options = requestOptionsFor2(params, config2.requestOptions);
|
|
3352
3423
|
assertRequestOptions(options, allowedModels, allowedPresets, config2.allowedProviders);
|
|
3353
3424
|
const transformed = { ...body, ...options.extraBody };
|
|
3354
3425
|
if (options.audioOutput) {
|
|
@@ -5169,6 +5240,7 @@ var streamAIWithTools = async function* (options) {
|
|
|
5169
5240
|
const blocks = [];
|
|
5170
5241
|
const pending = [];
|
|
5171
5242
|
let thinking = null;
|
|
5243
|
+
let stopReason;
|
|
5172
5244
|
let turnUsage;
|
|
5173
5245
|
for await (const chunk of stream) {
|
|
5174
5246
|
if (base.signal?.aborted)
|
|
@@ -5212,11 +5284,12 @@ var streamAIWithTools = async function* (options) {
|
|
|
5212
5284
|
});
|
|
5213
5285
|
} else if (chunk.type === "done") {
|
|
5214
5286
|
thinking = flushThinking3(blocks, thinking);
|
|
5287
|
+
stopReason = chunk.stopReason;
|
|
5215
5288
|
turnUsage = chunk.usage;
|
|
5216
5289
|
}
|
|
5217
5290
|
}
|
|
5218
5291
|
thinking = flushThinking3(blocks, thinking);
|
|
5219
|
-
return { blocks, pending, usage: turnUsage };
|
|
5292
|
+
return { blocks, pending, stopReason, usage: turnUsage };
|
|
5220
5293
|
};
|
|
5221
5294
|
while (turn < maxTurns) {
|
|
5222
5295
|
turn += 1;
|
|
@@ -5225,6 +5298,10 @@ var streamAIWithTools = async function* (options) {
|
|
|
5225
5298
|
yield { type: "turn", usage: outcome.usage };
|
|
5226
5299
|
const { blocks, pending } = outcome;
|
|
5227
5300
|
allToolCalls.push(...pending);
|
|
5301
|
+
if (outcome.stopReason === "pause_turn" && turn < maxTurns && base.signal?.aborted !== true) {
|
|
5302
|
+
messages.push({ content: blocks, role: "assistant" });
|
|
5303
|
+
continue;
|
|
5304
|
+
}
|
|
5228
5305
|
const allRepeats = pending.length > 0 && pending.every((call) => executedKeys.has(toolCallKey(call)));
|
|
5229
5306
|
const finished = pending.length === 0 || turn >= maxTurns || allRepeats || base.signal?.aborted === true;
|
|
5230
5307
|
if (finished)
|
|
@@ -7669,5 +7746,5 @@ export {
|
|
|
7669
7746
|
BUILTIN_UI_CARDS
|
|
7670
7747
|
};
|
|
7671
7748
|
|
|
7672
|
-
//# debugId=
|
|
7749
|
+
//# debugId=F018C48519101C9264756E2164756E21
|
|
7673
7750
|
//# sourceMappingURL=index.js.map
|