@absolutejs/ai 0.0.54-beta.0 → 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 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
- if (params.tools && params.tools.length > 0) {
1919
- const tools = params.tools.map(mapToolDefinition2);
1974
+ const clientTools = params.tools?.map(mapToolDefinition2) ?? [];
1975
+ if (clientTools.length > 0) {
1920
1976
  if (caching) {
1921
- tools[tools.length - 1] = {
1922
- ...tools[tools.length - 1],
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 requestOptionsFor = (params, defaults) => {
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 = requestOptionsFor(params, config2.requestOptions);
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 = requestOptionsFor(params, config2.requestOptions);
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=1D47D14F189FCCCF64756E2164756E21
7749
+ //# debugId=F018C48519101C9264756E2164756E21
7673
7750
  //# sourceMappingURL=index.js.map