@absolutejs/ai 0.0.50 → 0.0.51

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.
Files changed (36) hide show
  1. package/README.md +22 -12
  2. package/dist/ai/client/index.js +30 -1
  3. package/dist/ai/client/index.js.map +5 -5
  4. package/dist/ai/index.js +531 -56
  5. package/dist/ai/index.js.map +15 -15
  6. package/dist/ai/providers/anthropic.js +100 -25
  7. package/dist/ai/providers/anthropic.js.map +5 -5
  8. package/dist/ai/providers/gemini.js +5 -2
  9. package/dist/ai/providers/gemini.js.map +4 -4
  10. package/dist/ai/providers/ollama.js +5 -2
  11. package/dist/ai/providers/ollama.js.map +4 -4
  12. package/dist/ai/providers/openai.js +56 -10
  13. package/dist/ai/providers/openai.js.map +5 -5
  14. package/dist/ai/providers/openaiCompatible.js +56 -10
  15. package/dist/ai/providers/openaiCompatible.js.map +5 -5
  16. package/dist/ai/providers/openaiResponses.js +100 -16
  17. package/dist/ai/providers/openaiResponses.js.map +5 -5
  18. package/dist/ai/providers/openrouter.js +888 -38
  19. package/dist/ai/providers/openrouter.js.map +10 -9
  20. package/dist/angular/ai/index.js +30 -1
  21. package/dist/angular/ai/index.js.map +5 -5
  22. package/dist/react/ai/index.js +30 -1
  23. package/dist/react/ai/index.js.map +5 -5
  24. package/dist/src/ai/client/actions.d.ts +53 -0
  25. package/dist/src/ai/errors/providerError.d.ts +3 -0
  26. package/dist/src/ai/index.d.ts +2 -2
  27. package/dist/src/ai/providers/openrouter.d.ts +46 -2
  28. package/dist/src/ai/providers/openrouterClient.d.ts +132 -3
  29. package/dist/src/ai/streamAIWithTools.d.ts +6 -0
  30. package/dist/svelte/ai/index.js +30 -1
  31. package/dist/svelte/ai/index.js.map +5 -5
  32. package/dist/types/ai.d.ts +54 -3
  33. package/dist/types/anthropic.d.ts +13 -5
  34. package/dist/vue/ai/index.js +30 -1
  35. package/dist/vue/ai/index.js.map +5 -5
  36. package/package.json +1 -1
@@ -43,6 +43,7 @@ class ProviderError extends Error {
43
43
  status;
44
44
  type;
45
45
  retryable;
46
+ metadata;
46
47
  statusPageUrl;
47
48
  constructor(init) {
48
49
  super(init.message, init.cause === undefined ? undefined : { cause: init.cause });
@@ -51,6 +52,7 @@ class ProviderError extends Error {
51
52
  this.status = init.status ?? null;
52
53
  this.type = init.type ?? null;
53
54
  this.retryable = init.retryable;
55
+ this.metadata = init.metadata;
54
56
  this.statusPageUrl = providerStatusPage(init.provider);
55
57
  }
56
58
  static fromResponse(provider, status, body, type) {
@@ -223,7 +225,8 @@ var withResilience = (provider, providerName = "unknown") => {
223
225
  yield* attempt(params, attemptNo + 1);
224
226
  return;
225
227
  }
226
- noteFailure(providerName, providerError);
228
+ if (providerError.retryable)
229
+ noteFailure(providerName, providerError);
227
230
  throw providerError;
228
231
  }
229
232
  };
@@ -505,6 +508,9 @@ var buildRequestBody = (params, capabilityModel = params.model) => {
505
508
  content: mapOpenAIContent(msg),
506
509
  role: msg.role
507
510
  })), params);
511
+ if (params.systemPrompt) {
512
+ messages.unshift({ content: params.systemPrompt, role: "system" });
513
+ }
508
514
  const body = {
509
515
  messages,
510
516
  model: params.model,
@@ -648,6 +654,18 @@ var processDelta = function* (delta, pendingToolCalls) {
648
654
  if (typeof delta.content === "string") {
649
655
  yield { content: delta.content, type: "text" };
650
656
  }
657
+ if (isRecord(delta.audio)) {
658
+ const audio = delta.audio;
659
+ if (typeof audio.data === "string" || typeof audio.transcript === "string") {
660
+ yield {
661
+ audioId: typeof audio.id === "string" ? audio.id : undefined,
662
+ data: typeof audio.data === "string" ? audio.data : "",
663
+ format: typeof audio.format === "string" ? audio.format : "pcm16",
664
+ transcript: typeof audio.transcript === "string" ? audio.transcript : undefined,
665
+ type: "audio"
666
+ };
667
+ }
668
+ }
651
669
  if (isRecordArray(delta.tool_calls)) {
652
670
  processToolCallDeltas(delta.tool_calls, pendingToolCalls);
653
671
  }
@@ -716,7 +734,7 @@ var narrowUsageRecord = (parsed) => {
716
734
  }
717
735
  return normalized;
718
736
  };
719
- var processSSELine = function* (line, pendingToolCalls) {
737
+ var processSSELine = function* (line, pendingToolCalls, providerName) {
720
738
  const trimmed = line.trim();
721
739
  if (!trimmed || !trimmed.startsWith("data: ")) {
722
740
  return;
@@ -732,6 +750,20 @@ var processSSELine = function* (line, pendingToolCalls) {
732
750
  } catch {
733
751
  return;
734
752
  }
753
+ if (isRecord(parsed.error)) {
754
+ const error = parsed.error;
755
+ const metadata2 = isRecord(error.metadata) ? error.metadata : undefined;
756
+ const status = typeof error.code === "number" ? error.code : null;
757
+ const type = metadata2 && typeof metadata2.error_type === "string" ? metadata2.error_type : typeof error.error_type === "string" ? error.error_type : null;
758
+ throw new ProviderError({
759
+ message: typeof error.message === "string" ? error.message : "OpenRouter stream failed",
760
+ metadata: metadata2,
761
+ provider: providerName,
762
+ retryable: status === 408 || status === 409 || status === 425 || status === 429 || status !== null && status >= 500,
763
+ status,
764
+ type
765
+ });
766
+ }
735
767
  const usageUpdate = narrowUsageRecord(parsed);
736
768
  if (usageUpdate) {
737
769
  yield { type: "usage_update", usage: usageUpdate };
@@ -748,11 +780,24 @@ var processSSELine = function* (line, pendingToolCalls) {
748
780
  if (!firstChoice) {
749
781
  return;
750
782
  }
783
+ if (isRecord(firstChoice.error)) {
784
+ const error = firstChoice.error;
785
+ const metadata2 = isRecord(error.metadata) ? error.metadata : undefined;
786
+ const status = typeof error.code === "number" ? error.code : null;
787
+ throw new ProviderError({
788
+ message: typeof error.message === "string" ? error.message : "OpenRouter stream failed",
789
+ metadata: metadata2,
790
+ provider: providerName,
791
+ retryable: status === 429 || status !== null && status >= 500,
792
+ status,
793
+ type: metadata2 && typeof metadata2.error_type === "string" ? metadata2.error_type : null
794
+ });
795
+ }
751
796
  yield* processChoice(firstChoice, pendingToolCalls);
752
797
  };
753
798
  var isUsageUpdate = (chunk) => chunk.type === "usage_update";
754
- var collectYieldableChunks = (line, pendingToolCalls, usageRef, metadataRef) => {
755
- const allChunks = Array.from(processSSELine(line, pendingToolCalls));
799
+ var collectYieldableChunks = (line, pendingToolCalls, usageRef, metadataRef, providerName) => {
800
+ const allChunks = Array.from(processSSELine(line, pendingToolCalls, providerName));
756
801
  const usageChunks = allChunks.filter(isUsageUpdate);
757
802
  const lastUsage = usageChunks.at(NOT_FOUND);
758
803
  if (lastUsage) {
@@ -772,9 +817,9 @@ var collectYieldableChunks = (line, pendingToolCalls, usageRef, metadataRef) =>
772
817
  }
773
818
  return allChunks.filter((chunk) => !isUsageUpdate(chunk) && chunk.type !== "response_metadata");
774
819
  };
775
- var processSSELines = function* (lines, pendingToolCalls, usageRef, metadataRef) {
820
+ var processSSELines = function* (lines, pendingToolCalls, usageRef, metadataRef, providerName) {
776
821
  for (const line of lines) {
777
- yield* collectYieldableChunks(line, pendingToolCalls, usageRef, metadataRef);
822
+ yield* collectYieldableChunks(line, pendingToolCalls, usageRef, metadataRef, providerName);
778
823
  }
779
824
  };
780
825
  var processStreamValue = (value, decoder, state) => {
@@ -787,16 +832,17 @@ var processStreamValue = (value, decoder, state) => {
787
832
  var drainReader = async function* (reader, decoder, state, signal) {
788
833
  for (let result = await reader.read();!result.done && !signal?.aborted; result = await reader.read()) {
789
834
  const lines = processStreamValue(result.value, decoder, state);
790
- yield* processSSELines(lines, state.pendingToolCalls, state.usageRef, state.metadataRef);
835
+ yield* processSSELines(lines, state.pendingToolCalls, state.usageRef, state.metadataRef, state.providerName);
791
836
  }
792
837
  };
793
- var parseSSEStream = async function* (body, initialMetadata, signal) {
838
+ var parseSSEStream = async function* (body, initialMetadata, providerName = "openai", signal) {
794
839
  const reader = body.getReader();
795
840
  const decoder = new TextDecoder;
796
841
  const state = {
797
842
  buffer: "",
798
843
  metadataRef: { current: initialMetadata },
799
844
  pendingToolCalls: new Map,
845
+ providerName,
800
846
  usageRef: { current: undefined }
801
847
  };
802
848
  try {
@@ -841,7 +887,7 @@ var fetchOpenAIStream = async function* (baseUrl, apiKey, body, fetchImpl, heade
841
887
  cacheStatus: response.headers.get("X-OpenRouter-Cache-Status") ?? undefined,
842
888
  cacheTtl: response.headers.get("X-OpenRouter-Cache-TTL") ?? undefined
843
889
  }
844
- }, signal);
890
+ }, providerName, signal);
845
891
  };
846
892
  var openai = (config2) => {
847
893
  const baseUrl = config2.baseUrl ?? DEFAULT_BASE_URL;
@@ -923,7 +969,12 @@ var mapContentToResponsesFormat = (content) => {
923
969
  };
924
970
  var hasToolBlocks = (content) => content.some((block) => block.type === "tool_use" || block.type === "tool_result");
925
971
  var convertToolBlock = (block) => {
972
+ if (block.type === "provider_data" && block.provider === "openrouter") {
973
+ return { ...block.data };
974
+ }
926
975
  if (block.type === "tool_use") {
976
+ if (block.providerData)
977
+ return { ...block.providerData };
927
978
  return {
928
979
  arguments: typeof block.input === "string" ? block.input : JSON.stringify(block.input),
929
980
  call_id: block.id,
@@ -1059,11 +1110,32 @@ var extractUsage2 = (response) => {
1059
1110
  const { usage } = response;
1060
1111
  const input = typeof usage.input_tokens === "number" ? usage.input_tokens : 0;
1061
1112
  const cached = isRecord2(usage.input_tokens_details) && typeof usage.input_tokens_details.cached_tokens === "number" ? usage.input_tokens_details.cached_tokens : 0;
1062
- return {
1113
+ const outputDetails = isRecord2(usage.output_tokens_details) ? usage.output_tokens_details : undefined;
1114
+ const inputDetails = isRecord2(usage.input_tokens_details) ? usage.input_tokens_details : undefined;
1115
+ const costDetails = isRecord2(usage.cost_details) ? usage.cost_details : undefined;
1116
+ const normalized = {
1063
1117
  cacheReadInputTokens: cached,
1118
+ cacheWriteInputTokens: inputDetails && typeof inputDetails.cache_write_tokens === "number" ? inputDetails.cache_write_tokens : undefined,
1119
+ costCredits: typeof usage.cost === "number" ? usage.cost : undefined,
1064
1120
  inputTokens: Math.max(0, input - cached),
1065
- outputTokens: typeof usage.output_tokens === "number" ? usage.output_tokens : 0
1121
+ outputTokens: typeof usage.output_tokens === "number" ? usage.output_tokens : 0,
1122
+ reasoningTokens: outputDetails && typeof outputDetails.reasoning_tokens === "number" ? outputDetails.reasoning_tokens : undefined,
1123
+ upstreamInferenceCostCredits: costDetails && typeof costDetails.upstream_inference_cost === "number" ? costDetails.upstream_inference_cost : undefined
1066
1124
  };
1125
+ if (isRecord2(usage.server_tool_use)) {
1126
+ normalized.serverToolUse = Object.fromEntries(Object.entries(usage.server_tool_use).filter((entry) => typeof entry[1] === "number"));
1127
+ }
1128
+ return normalized;
1129
+ };
1130
+ var extractResponseMetadata = (response) => {
1131
+ const providerMetadata = isRecord2(response.openrouter_metadata) ? response.openrouter_metadata : undefined;
1132
+ const generationId = typeof response.id === "string" ? response.id : undefined;
1133
+ const model = typeof response.model === "string" ? response.model : undefined;
1134
+ const provider = typeof response.provider === "string" ? response.provider : undefined;
1135
+ const serviceTier = typeof response.service_tier === "string" ? response.service_tier : undefined;
1136
+ if (!providerMetadata && !generationId && !model && !provider && !serviceTier)
1137
+ return;
1138
+ return { generationId, model, provider, providerMetadata, serviceTier };
1067
1139
  };
1068
1140
  var extractMimeFormat = (mimeType) => {
1069
1141
  if (typeof mimeType !== "string") {
@@ -1120,6 +1192,7 @@ var processFunctionCallArgumentsDone = function* (parsed, pendingCalls) {
1120
1192
  id: callId || pending?.callId || itemId,
1121
1193
  input: parseToolInput2(args),
1122
1194
  name,
1195
+ providerData: pending?.providerData ? { ...pending.providerData, arguments: args } : undefined,
1123
1196
  type: "tool_use"
1124
1197
  };
1125
1198
  };
@@ -1138,9 +1211,21 @@ var processOutputItemAdded = (parsed, pendingCalls) => {
1138
1211
  pendingCalls.set(itemId, {
1139
1212
  arguments: "",
1140
1213
  callId,
1141
- name
1214
+ name,
1215
+ providerData: { ...item }
1142
1216
  });
1143
1217
  };
1218
+ var processOutputItemDone = function* (parsed) {
1219
+ if (!isRecord2(parsed.item) || typeof parsed.item.type !== "string")
1220
+ return;
1221
+ if (!parsed.item.type.startsWith("openrouter:"))
1222
+ return;
1223
+ yield {
1224
+ data: { ...parsed.item },
1225
+ provider: "openrouter",
1226
+ type: "provider_event"
1227
+ };
1228
+ };
1144
1229
  var isCompletedImageGeneration = (item) => item.type === "image_generation_call" && item.status === "completed" && typeof item.result === "string" && item.result !== "";
1145
1230
  var buildImageChunk = (item) => ({
1146
1231
  data: typeof item.result === "string" ? item.result : "",
@@ -1156,6 +1241,30 @@ var extractImageFromOutput = function* (output) {
1156
1241
  yield buildImageChunk(item);
1157
1242
  }
1158
1243
  };
1244
+ var extractCitationsFromOutput = function* (output) {
1245
+ for (const item of output) {
1246
+ if (!isRecordArray2(item.content))
1247
+ continue;
1248
+ for (const content of item.content) {
1249
+ if (!Array.isArray(content.annotations))
1250
+ continue;
1251
+ for (const annotation of content.annotations) {
1252
+ if (!isRecord2(annotation) || annotation.type !== "url_citation")
1253
+ continue;
1254
+ if (typeof annotation.url !== "string")
1255
+ continue;
1256
+ yield {
1257
+ content: typeof annotation.content === "string" ? annotation.content : undefined,
1258
+ endIndex: typeof annotation.end_index === "number" ? annotation.end_index : undefined,
1259
+ startIndex: typeof annotation.start_index === "number" ? annotation.start_index : undefined,
1260
+ title: typeof annotation.title === "string" ? annotation.title : undefined,
1261
+ type: "citation",
1262
+ url: annotation.url
1263
+ };
1264
+ }
1265
+ }
1266
+ }
1267
+ };
1159
1268
  var processCompleted = function* (parsed) {
1160
1269
  if (!isRecord2(parsed.response)) {
1161
1270
  yield { type: "done", usage: undefined };
@@ -1163,12 +1272,26 @@ var processCompleted = function* (parsed) {
1163
1272
  }
1164
1273
  const { response } = parsed;
1165
1274
  const usage = extractUsage2(response);
1275
+ const metadata = extractResponseMetadata(response);
1166
1276
  if (isRecordArray2(response.output)) {
1277
+ yield* extractCitationsFromOutput(response.output);
1167
1278
  yield* extractImageFromOutput(response.output);
1168
1279
  }
1169
- yield { type: "done", usage };
1280
+ yield { metadata, type: "done", usage };
1170
1281
  };
1171
- var processSSEEvent = function* (eventType, parsed, pendingCalls) {
1282
+ var responseFailure = (eventType, parsed, providerName) => {
1283
+ const response = isRecord2(parsed.response) ? parsed.response : parsed;
1284
+ const error = isRecord2(response.error) ? response.error : undefined;
1285
+ const type = typeof response.error_type === "string" ? response.error_type : error && typeof error.code === "string" ? error.code : eventType;
1286
+ return new ProviderError({
1287
+ message: error && typeof error.message === "string" ? error.message : `OpenRouter Responses API: ${eventType}`,
1288
+ metadata: response,
1289
+ provider: providerName,
1290
+ retryable: type === "rate_limit_exceeded" || type === "provider_overloaded" || type === "provider_unavailable" || type === "server",
1291
+ type
1292
+ });
1293
+ };
1294
+ var processSSEEvent = function* (eventType, parsed, pendingCalls, providerName) {
1172
1295
  switch (eventType) {
1173
1296
  case "response.reasoning_summary_text.delta": {
1174
1297
  const delta = typeof parsed.delta === "string" ? parsed.delta : "";
@@ -1189,6 +1312,9 @@ var processSSEEvent = function* (eventType, parsed, pendingCalls) {
1189
1312
  case "response.output_item.added":
1190
1313
  processOutputItemAdded(parsed, pendingCalls);
1191
1314
  break;
1315
+ case "response.output_item.done":
1316
+ yield* processOutputItemDone(parsed);
1317
+ break;
1192
1318
  case "response.function_call_arguments.delta":
1193
1319
  processFunctionCallArgumentsDelta(parsed, pendingCalls);
1194
1320
  break;
@@ -1199,11 +1325,10 @@ var processSSEEvent = function* (eventType, parsed, pendingCalls) {
1199
1325
  yield* processCompleted(parsed);
1200
1326
  break;
1201
1327
  case "response.failed":
1202
- case "response.incomplete": {
1203
- const respObj = isRecord2(parsed.response) ? parsed.response : parsed;
1204
- const errMsg = isRecord2(respObj.error) && typeof respObj.error.message === "string" ? respObj.error.message : `OpenAI Responses API: ${eventType}`;
1205
- throw new Error(errMsg);
1206
- }
1328
+ case "response.incomplete":
1329
+ case "response.error":
1330
+ case "error":
1331
+ throw responseFailure(eventType, parsed, providerName);
1207
1332
  }
1208
1333
  };
1209
1334
  var flushSSEBuffer = function* (state) {
@@ -1212,7 +1337,7 @@ var flushSSEBuffer = function* (state) {
1212
1337
  }
1213
1338
  const parsed = parseJSON(state.buffer);
1214
1339
  if (parsed) {
1215
- yield* processSSEEvent(state.currentEvent, parsed, state.pendingCalls);
1340
+ yield* processSSEEvent(state.currentEvent, parsed, state.pendingCalls, state.providerName);
1216
1341
  }
1217
1342
  state.currentEvent = "";
1218
1343
  state.buffer = "";
@@ -1250,17 +1375,19 @@ var drainReader2 = async function* (reader, decoder, state, signal) {
1250
1375
  yield* processSSELines2([textBuffer, ""], state);
1251
1376
  }
1252
1377
  };
1253
- var parseSSEStream2 = async function* (body, signal) {
1378
+ var parseSSEStream2 = async function* (body, providerName, signal) {
1254
1379
  const reader = body.getReader();
1255
1380
  const decoder = new TextDecoder;
1256
1381
  const state = {
1257
1382
  buffer: "",
1258
1383
  currentEvent: "",
1259
1384
  pendingCalls: new Map,
1260
- usage: undefined
1385
+ usage: undefined,
1386
+ providerName
1261
1387
  };
1262
1388
  try {
1263
1389
  yield* drainReader2(reader, decoder, state, signal);
1390
+ yield* flushSSEBuffer(state);
1264
1391
  } finally {
1265
1392
  reader.releaseLock();
1266
1393
  }
@@ -1288,7 +1415,7 @@ var fetchResponsesStream = async function* (baseUrl, apiKey, body, fetchImpl, he
1288
1415
  retryable: true
1289
1416
  });
1290
1417
  }
1291
- yield* parseSSEStream2(response.body, signal);
1418
+ yield* parseSSEStream2(response.body, providerName, signal);
1292
1419
  };
1293
1420
  var resolveImageModels = (imageModels) => {
1294
1421
  if (!imageModels) {
@@ -1324,8 +1451,546 @@ var openaiResponses = (config2) => {
1324
1451
  }, providerName);
1325
1452
  };
1326
1453
 
1454
+ // src/ai/providers/anthropic.ts
1455
+ var h2IfHttps3 = (url) => url.startsWith("https://") ? { protocol: "http2" } : {};
1456
+ var DEFAULT_BASE_URL3 = "https://api.anthropic.com";
1457
+ var API_VERSION = "2023-06-01";
1458
+ var DEFAULT_MAX_TOKENS = 32000;
1459
+ var EVENT_PREFIX_LENGTH2 = 7;
1460
+ var DATA_PREFIX_LENGTH2 = 6;
1461
+ var EMPTY_CHUNKS = [];
1462
+ var isRecord3 = (val) => typeof val === "object" && val !== null;
1463
+ var mapContentBlock = (block) => {
1464
+ if (block.type === "thinking") {
1465
+ return {
1466
+ signature: block.signature,
1467
+ thinking: block.thinking,
1468
+ type: "thinking"
1469
+ };
1470
+ }
1471
+ if (block.type === "image") {
1472
+ return {
1473
+ source: block.source,
1474
+ type: "image"
1475
+ };
1476
+ }
1477
+ if (block.type === "document") {
1478
+ return {
1479
+ source: block.source,
1480
+ type: "document"
1481
+ };
1482
+ }
1483
+ if (block.type === "tool_result") {
1484
+ return {
1485
+ content: block.content,
1486
+ tool_use_id: block.tool_use_id,
1487
+ type: "tool_result"
1488
+ };
1489
+ }
1490
+ if (block.type === "tool_use") {
1491
+ if (block.providerData)
1492
+ return { ...block.providerData };
1493
+ return {
1494
+ id: block.id,
1495
+ input: block.input,
1496
+ name: block.name,
1497
+ type: "tool_use"
1498
+ };
1499
+ }
1500
+ if (block.type === "audio" || block.type === "video") {
1501
+ throw new Error(`Anthropic does not support ${block.type} content blocks`);
1502
+ }
1503
+ if (block.type === "provider_data") {
1504
+ return { ...block.data };
1505
+ }
1506
+ return { text: block.content, type: "text" };
1507
+ };
1508
+ var mapMessage = (msg) => ({
1509
+ content: typeof msg.content === "string" ? msg.content : msg.content.map(mapContentBlock),
1510
+ role: msg.role === "system" ? "user" : msg.role
1511
+ });
1512
+ var mapToolDefinition2 = (tool) => ({
1513
+ description: tool.description,
1514
+ input_schema: tool.input_schema,
1515
+ name: tool.name
1516
+ });
1517
+ var cacheLastContentBlock = (msg) => {
1518
+ const cacheControl = { type: "ephemeral" };
1519
+ if (typeof msg.content === "string") {
1520
+ return {
1521
+ content: [
1522
+ { cache_control: cacheControl, text: msg.content, type: "text" }
1523
+ ],
1524
+ role: msg.role
1525
+ };
1526
+ }
1527
+ if (msg.content.length === 0)
1528
+ return msg;
1529
+ const blocks = [...msg.content];
1530
+ blocks[blocks.length - 1] = {
1531
+ ...blocks[blocks.length - 1],
1532
+ cache_control: cacheControl
1533
+ };
1534
+ return { content: blocks, role: msg.role };
1535
+ };
1536
+ var buildRequestBody3 = (params, configuredMax, configCaching) => {
1537
+ const caching = params.promptCaching ?? configCaching;
1538
+ const cacheSystem = params.cacheSystemPrompt ?? caching;
1539
+ const messages = params.messages.filter((msg) => msg.role !== "system").map(mapMessage);
1540
+ if (caching && messages.length > 1) {
1541
+ const last = messages[messages.length - 1];
1542
+ if (last) {
1543
+ messages[messages.length - 1] = cacheLastContentBlock(last);
1544
+ }
1545
+ }
1546
+ const max = typeof params.maxTokens === "number" ? params.maxTokens : configuredMax;
1547
+ const body = {
1548
+ max_tokens: max,
1549
+ messages,
1550
+ model: params.model,
1551
+ stream: true
1552
+ };
1553
+ if (params.systemPrompt) {
1554
+ body.system = cacheSystem ? [
1555
+ {
1556
+ cache_control: { type: "ephemeral" },
1557
+ text: params.systemPrompt,
1558
+ type: "text"
1559
+ }
1560
+ ] : params.systemPrompt;
1561
+ }
1562
+ if (params.tools && params.tools.length > 0) {
1563
+ const tools = params.tools.map(mapToolDefinition2);
1564
+ if (caching) {
1565
+ tools[tools.length - 1] = {
1566
+ ...tools[tools.length - 1],
1567
+ cache_control: { type: "ephemeral" }
1568
+ };
1569
+ }
1570
+ body.tools = tools;
1571
+ if (params.toolChoice === "auto" || params.toolChoice === "none") {
1572
+ body.tool_choice = { type: params.toolChoice };
1573
+ } else if (params.toolChoice === "required") {
1574
+ body.tool_choice = { type: "any" };
1575
+ } else if (params.toolChoice && typeof params.toolChoice === "object") {
1576
+ body.tool_choice = { name: params.toolChoice.name, type: "tool" };
1577
+ }
1578
+ }
1579
+ if (params.stopSequences && params.stopSequences.length > 0) {
1580
+ body.stop_sequences = params.stopSequences;
1581
+ }
1582
+ const mode = params.reasoning ? anthropicReasoningMode(params.model) : "none";
1583
+ const thinkingActive = mode !== "none";
1584
+ if (!thinkingActive && anthropicSupportsSampling(params.model)) {
1585
+ if (typeof params.temperature === "number") {
1586
+ body.temperature = params.temperature;
1587
+ }
1588
+ if (typeof params.topP === "number")
1589
+ body.top_p = params.topP;
1590
+ }
1591
+ if (mode === "effort" || mode === "adaptive") {
1592
+ body.thinking = { type: "adaptive" };
1593
+ if (mode === "effort" && params.reasoning) {
1594
+ const effort = anthropicEffortValue(params.model, params.reasoning);
1595
+ if (effort)
1596
+ body.output_config = { effort };
1597
+ }
1598
+ } else if (mode === "legacy" && params.reasoning) {
1599
+ const budget = resolveBudgetTokens(params.reasoning);
1600
+ if (budget) {
1601
+ body.thinking = { budget_tokens: budget, type: "enabled" };
1602
+ body.max_tokens = Math.max(max, budget + max);
1603
+ }
1604
+ }
1605
+ return body;
1606
+ };
1607
+ var classifyLine = (line) => {
1608
+ if (line.startsWith("event: ")) {
1609
+ return {
1610
+ field: "event",
1611
+ value: line.slice(EVENT_PREFIX_LENGTH2)
1612
+ };
1613
+ }
1614
+ if (line.startsWith("data: ")) {
1615
+ return {
1616
+ field: "data",
1617
+ value: line.slice(DATA_PREFIX_LENGTH2)
1618
+ };
1619
+ }
1620
+ return;
1621
+ };
1622
+ var applyClassified = (acc, classified) => {
1623
+ if (!classified) {
1624
+ return acc;
1625
+ }
1626
+ if (classified.field === "event") {
1627
+ return { eventData: acc.eventData, eventType: classified.value };
1628
+ }
1629
+ return { eventData: classified.value, eventType: acc.eventType };
1630
+ };
1631
+ var parseEventLines = (event) => event.split(`
1632
+ `).reduce((acc, line) => applyClassified(acc, classifyLine(line)), {
1633
+ eventData: "",
1634
+ eventType: ""
1635
+ });
1636
+ var safeParse = (text) => {
1637
+ try {
1638
+ const result = JSON.parse(text);
1639
+ return result;
1640
+ } catch {
1641
+ return;
1642
+ }
1643
+ };
1644
+ var tryParseJson = (text) => {
1645
+ const result = safeParse(text);
1646
+ if (isRecord3(result)) {
1647
+ return result;
1648
+ }
1649
+ return;
1650
+ };
1651
+ var getRecord = (obj, key) => {
1652
+ const val = obj[key];
1653
+ if (isRecord3(val)) {
1654
+ return val;
1655
+ }
1656
+ return;
1657
+ };
1658
+ var getString = (obj, key) => {
1659
+ const val = obj[key];
1660
+ if (typeof val === "string") {
1661
+ return val;
1662
+ }
1663
+ return "";
1664
+ };
1665
+ var getNumber = (obj, key) => {
1666
+ const val = obj[key];
1667
+ if (typeof val === "number") {
1668
+ return val;
1669
+ }
1670
+ return 0;
1671
+ };
1672
+ var handleContentBlockStart = (parsed, state) => {
1673
+ const block = getRecord(parsed, "content_block");
1674
+ if (block && block.type === "tool_use") {
1675
+ state.currentToolId = getString(block, "id");
1676
+ state.currentToolName = getString(block, "name");
1677
+ state.toolInputJson = "";
1678
+ state.isThinkingBlock = false;
1679
+ state.currentProviderBlock = undefined;
1680
+ } else if (block && block.type === "thinking") {
1681
+ state.isThinkingBlock = true;
1682
+ state.thinkingSignature = "";
1683
+ state.currentProviderBlock = undefined;
1684
+ } else {
1685
+ state.isThinkingBlock = false;
1686
+ state.currentProviderBlock = block && block.type !== "text" ? { ...block } : undefined;
1687
+ state.providerBlockInputJson = "";
1688
+ }
1689
+ };
1690
+ var handleContentBlockDelta = (parsed, state) => {
1691
+ const delta = getRecord(parsed, "delta");
1692
+ if (!delta) {
1693
+ return;
1694
+ }
1695
+ if (delta.type === "thinking_delta") {
1696
+ return {
1697
+ content: getString(delta, "thinking"),
1698
+ type: "thinking"
1699
+ };
1700
+ }
1701
+ if (delta.type === "text_delta") {
1702
+ return {
1703
+ content: getString(delta, "text"),
1704
+ type: "text"
1705
+ };
1706
+ }
1707
+ if (delta.type === "input_json_delta") {
1708
+ if (state.currentProviderBlock) {
1709
+ state.providerBlockInputJson += getString(delta, "partial_json");
1710
+ } else {
1711
+ state.toolInputJson += getString(delta, "partial_json");
1712
+ }
1713
+ }
1714
+ if (delta.type === "signature_delta") {
1715
+ state.thinkingSignature += getString(delta, "signature");
1716
+ }
1717
+ return;
1718
+ };
1719
+ var handleContentBlockStop = (state) => {
1720
+ if (state.isThinkingBlock && state.thinkingSignature) {
1721
+ state.isThinkingBlock = false;
1722
+ const signature = state.thinkingSignature;
1723
+ state.thinkingSignature = "";
1724
+ return {
1725
+ content: "",
1726
+ signature,
1727
+ type: "thinking"
1728
+ };
1729
+ }
1730
+ if (state.currentProviderBlock) {
1731
+ const data = { ...state.currentProviderBlock };
1732
+ if (state.providerBlockInputJson) {
1733
+ data.input = tryParseJson(state.providerBlockInputJson) ?? state.providerBlockInputJson;
1734
+ }
1735
+ state.currentProviderBlock = undefined;
1736
+ state.providerBlockInputJson = "";
1737
+ return {
1738
+ data,
1739
+ provider: state.providerName,
1740
+ type: "provider_event"
1741
+ };
1742
+ }
1743
+ if (!state.currentToolId) {
1744
+ return;
1745
+ }
1746
+ const input = tryParseJson(state.toolInputJson) ?? state.toolInputJson;
1747
+ const chunk = {
1748
+ id: state.currentToolId,
1749
+ input,
1750
+ name: state.currentToolName,
1751
+ type: "tool_use"
1752
+ };
1753
+ state.currentToolId = "";
1754
+ state.currentToolName = "";
1755
+ state.toolInputJson = "";
1756
+ return chunk;
1757
+ };
1758
+ var extractUsage3 = (usageRecord, existingUsage) => {
1759
+ if (!usageRecord) {
1760
+ return existingUsage;
1761
+ }
1762
+ const normalized = {
1763
+ cacheReadInputTokens: getNumber(usageRecord, "cache_read_input_tokens") || existingUsage?.cacheReadInputTokens || 0,
1764
+ cacheWriteInputTokens: getNumber(usageRecord, "cache_creation_input_tokens") || existingUsage?.cacheWriteInputTokens || 0,
1765
+ inputTokens: getNumber(usageRecord, "input_tokens") || existingUsage?.inputTokens || 0,
1766
+ outputTokens: getNumber(usageRecord, "output_tokens") || existingUsage?.outputTokens || 0,
1767
+ costCredits: getNumber(usageRecord, "cost") || existingUsage?.costCredits,
1768
+ reasoningTokens: getNumber(usageRecord, "reasoning_tokens") || existingUsage?.reasoningTokens,
1769
+ upstreamInferenceCostCredits: getNumber(getRecord(usageRecord, "cost_details") ?? {}, "upstream_inference_cost") || existingUsage?.upstreamInferenceCostCredits
1770
+ };
1771
+ const serverToolUse = getRecord(usageRecord, "server_tool_use");
1772
+ if (serverToolUse) {
1773
+ normalized.serverToolUse = Object.fromEntries(Object.entries(serverToolUse).filter((entry) => typeof entry[1] === "number"));
1774
+ }
1775
+ return normalized;
1776
+ };
1777
+ var mergeMetadata = (source, state) => {
1778
+ const providerMetadata = getRecord(source, "openrouter_metadata");
1779
+ const generationId = getString(source, "id") || undefined;
1780
+ const model = getString(source, "model") || undefined;
1781
+ const provider = getString(source, "provider") || undefined;
1782
+ const serviceTier = getString(source, "service_tier") || undefined;
1783
+ if (!providerMetadata && !generationId && !model && !provider && !serviceTier)
1784
+ return;
1785
+ state.metadata = {
1786
+ ...state.metadata,
1787
+ generationId: generationId ?? state.metadata?.generationId,
1788
+ model: model ?? state.metadata?.model,
1789
+ provider: provider ?? state.metadata?.provider,
1790
+ providerMetadata: {
1791
+ ...state.metadata?.providerMetadata,
1792
+ ...providerMetadata
1793
+ },
1794
+ serviceTier: serviceTier ?? state.metadata?.serviceTier
1795
+ };
1796
+ };
1797
+ var handleMessageDelta = (parsed, state) => {
1798
+ const deltaUsage = getRecord(parsed, "usage");
1799
+ state.usage = extractUsage3(deltaUsage, state.usage);
1800
+ const delta = getRecord(parsed, "delta");
1801
+ const stopReason = delta ? getString(delta, "stop_reason") : "";
1802
+ if (stopReason)
1803
+ state.stopReason = stopReason;
1804
+ };
1805
+ var handleMessageStart = (parsed, state) => {
1806
+ const message = getRecord(parsed, "message");
1807
+ if (!message) {
1808
+ return;
1809
+ }
1810
+ const startUsage = getRecord(message, "usage");
1811
+ state.usage = extractUsage3(startUsage, state.usage);
1812
+ mergeMetadata(message, state);
1813
+ };
1814
+ var handleError = (parsed, state) => {
1815
+ const error = getRecord(parsed, "error");
1816
+ const errorMessage = error ? getString(error, "message") : "";
1817
+ const nativeErrorType = error ? getString(error, "type") : "";
1818
+ const errorType = error ? getString(error, "error_type") || nativeErrorType : "";
1819
+ const retryable = errorType === "provider_overloaded" || errorType === "rate_limit_exceeded" || errorType === "provider_unavailable" || errorType === "server" || nativeErrorType === "overloaded_error" || nativeErrorType === "rate_limit_error" || nativeErrorType === "api_error";
1820
+ throw new ProviderError({
1821
+ message: errorMessage || "Anthropic API error",
1822
+ metadata: error,
1823
+ provider: state.providerName,
1824
+ retryable,
1825
+ type: errorType || null
1826
+ });
1827
+ };
1828
+ var processEvent = (eventType, parsed, state) => {
1829
+ switch (eventType) {
1830
+ case "content_block_start": {
1831
+ handleContentBlockStart(parsed, state);
1832
+ return;
1833
+ }
1834
+ case "content_block_delta": {
1835
+ return handleContentBlockDelta(parsed, state);
1836
+ }
1837
+ case "content_block_stop": {
1838
+ return handleContentBlockStop(state);
1839
+ }
1840
+ case "message_delta": {
1841
+ handleMessageDelta(parsed, state);
1842
+ mergeMetadata(parsed, state);
1843
+ return;
1844
+ }
1845
+ case "message_start": {
1846
+ handleMessageStart(parsed, state);
1847
+ return;
1848
+ }
1849
+ case "message_stop": {
1850
+ mergeMetadata(parsed, state);
1851
+ return {
1852
+ stopReason: state.stopReason,
1853
+ metadata: state.metadata,
1854
+ type: "done",
1855
+ usage: state.usage
1856
+ };
1857
+ }
1858
+ case "error": {
1859
+ handleError(parsed, state);
1860
+ return;
1861
+ }
1862
+ default: {
1863
+ return;
1864
+ }
1865
+ }
1866
+ };
1867
+ var processSingleEvent = (event, state) => {
1868
+ if (!event.trim()) {
1869
+ return;
1870
+ }
1871
+ const { eventData, eventType } = parseEventLines(event);
1872
+ if (!eventData) {
1873
+ return;
1874
+ }
1875
+ const parsed = tryParseJson(eventData);
1876
+ if (!parsed) {
1877
+ return;
1878
+ }
1879
+ return processEvent(eventType, parsed, state);
1880
+ };
1881
+ var collectChunk = (event, state) => {
1882
+ const chunk = processSingleEvent(event, state);
1883
+ return chunk ? [chunk] : [];
1884
+ };
1885
+ var processBufferedEvents = (eventsText, state) => {
1886
+ const events = eventsText.split(`
1887
+
1888
+ `);
1889
+ state.buffer = events.pop() ?? "";
1890
+ return events.flatMap((event) => collectChunk(event, state));
1891
+ };
1892
+ var readNextChunks = async (reader, decoder, state, signal) => {
1893
+ if (signal?.aborted) {
1894
+ return { chunks: EMPTY_CHUNKS, done: true };
1895
+ }
1896
+ const { done, value } = await reader.read();
1897
+ if (done) {
1898
+ return { chunks: EMPTY_CHUNKS, done: true };
1899
+ }
1900
+ const rawText = state.buffer + decoder.decode(value, { stream: true });
1901
+ const chunks = processBufferedEvents(rawText, state);
1902
+ return { chunks, done: false };
1903
+ };
1904
+ var findDoneChunk = (chunks) => chunks.findIndex((c) => c.type === "done");
1905
+ var sseStreamLoop = async (reader, decoder, state, signal) => {
1906
+ const result = await readNextChunks(reader, decoder, state, signal);
1907
+ if (result.done) {
1908
+ return { chunks: result.chunks, finished: true };
1909
+ }
1910
+ const doneIdx = findDoneChunk(result.chunks);
1911
+ if (doneIdx >= 0) {
1912
+ return { chunks: result.chunks.slice(0, doneIdx + 1), finished: true };
1913
+ }
1914
+ return { chunks: result.chunks, finished: false };
1915
+ };
1916
+ async function* streamChunks(reader, decoder, state, signal) {
1917
+ let finished = false;
1918
+ while (!finished) {
1919
+ const result = await sseStreamLoop(reader, decoder, state, signal);
1920
+ ({ finished } = result);
1921
+ yield* result.chunks;
1922
+ }
1923
+ }
1924
+ async function* parseSSEStream3(body, providerName, signal) {
1925
+ const reader = body.getReader();
1926
+ const decoder = new TextDecoder;
1927
+ const state = {
1928
+ buffer: "",
1929
+ currentToolId: "",
1930
+ currentToolName: "",
1931
+ isThinkingBlock: false,
1932
+ stopReason: "",
1933
+ thinkingSignature: "",
1934
+ toolInputJson: "",
1935
+ usage: undefined,
1936
+ providerName,
1937
+ providerBlockInputJson: ""
1938
+ };
1939
+ try {
1940
+ yield* streamChunks(reader, decoder, state, signal);
1941
+ } finally {
1942
+ reader.releaseLock();
1943
+ }
1944
+ }
1945
+ var fetchAndStream = async function* (baseUrl, config2, params, configuredMax, promptCaching, providerName) {
1946
+ const builtBody = buildRequestBody3(params, configuredMax, promptCaching);
1947
+ const body = config2.transformRequestBody ? config2.transformRequestBody(builtBody, params) : builtBody;
1948
+ const target = `${baseUrl}/v1/messages`;
1949
+ const fetchImpl = config2.fetch ?? fetch;
1950
+ const token = config2.tokenSource ? await Promise.resolve(config2.tokenSource()) : config2.apiKey;
1951
+ const suppliedHeaders = typeof config2.headers === "function" ? await config2.headers(params) : config2.headers ?? {};
1952
+ const requestHeaders = new Headers(suppliedHeaders);
1953
+ requestHeaders.set("Content-Type", "application/json");
1954
+ if (config2.authStyle === "bearer") {
1955
+ requestHeaders.set("Authorization", `Bearer ${token}`);
1956
+ } else {
1957
+ requestHeaders.set("anthropic-version", API_VERSION);
1958
+ requestHeaders.set("x-api-key", token);
1959
+ }
1960
+ const response = await fetchImpl(target, {
1961
+ ...h2IfHttps3(target),
1962
+ body: JSON.stringify(body),
1963
+ headers: requestHeaders,
1964
+ method: "POST",
1965
+ signal: params.signal
1966
+ });
1967
+ if (!response.ok) {
1968
+ const errorText = await response.text();
1969
+ throw ProviderError.fromResponse(providerName, response.status, errorText);
1970
+ }
1971
+ if (!response.body) {
1972
+ throw new ProviderError({
1973
+ message: `${providerName} Messages API returned no response body`,
1974
+ provider: providerName,
1975
+ retryable: true
1976
+ });
1977
+ }
1978
+ yield* parseSSEStream3(response.body, providerName, params.signal);
1979
+ };
1980
+ var anthropic = (config2) => {
1981
+ if (!config2.apiKey && !config2.tokenSource)
1982
+ throw new Error("anthropic() requires either apiKey or tokenSource");
1983
+ const baseUrl = config2.baseUrl ?? DEFAULT_BASE_URL3;
1984
+ const configuredMax = config2.maxTokens ?? DEFAULT_MAX_TOKENS;
1985
+ const promptCaching = config2.promptCaching ?? true;
1986
+ const providerName = config2.providerName ?? "anthropic";
1987
+ return instrumentAIProvider({
1988
+ stream: (params) => fetchAndStream(baseUrl, config2, params, configuredMax, promptCaching, providerName)
1989
+ }, providerName);
1990
+ };
1991
+
1327
1992
  // src/ai/providers/openrouterClient.ts
1328
- var DEFAULT_BASE_URL3 = "https://openrouter.ai/api/v1";
1993
+ var DEFAULT_BASE_URL4 = "https://openrouter.ai/api/v1";
1329
1994
  var withoutLatestPrefix = (model) => model.startsWith("~") ? model.slice(1) : model;
1330
1995
  var openRouterModelMatchesRule = (model, rule) => {
1331
1996
  const normalizedModel = withoutLatestPrefix(model);
@@ -1351,10 +2016,89 @@ var withQuery = (url, query) => {
1351
2016
  }
1352
2017
  return result.toString();
1353
2018
  };
2019
+ var parseImageSSE = async function* (response) {
2020
+ if (!response.body)
2021
+ throw new Error("OpenRouter image stream has no body");
2022
+ const reader = response.body.getReader();
2023
+ const decoder = new TextDecoder;
2024
+ let buffer = "";
2025
+ try {
2026
+ for (;; ) {
2027
+ const result = await reader.read();
2028
+ buffer += decoder.decode(result.value, { stream: !result.done });
2029
+ const lines = buffer.split(`
2030
+ `);
2031
+ buffer = lines.pop() ?? "";
2032
+ for (const line of lines) {
2033
+ if (!line.startsWith("data: "))
2034
+ continue;
2035
+ const data = line.slice(6);
2036
+ if (data === "[DONE]")
2037
+ return;
2038
+ try {
2039
+ const parsed = JSON.parse(data);
2040
+ if (parsed && typeof parsed === "object" && "type" in parsed)
2041
+ yield parsed;
2042
+ } catch {}
2043
+ }
2044
+ if (result.done)
2045
+ break;
2046
+ }
2047
+ if (buffer.startsWith("data: ")) {
2048
+ const parsed = JSON.parse(buffer.slice(6));
2049
+ if (parsed && typeof parsed === "object" && "type" in parsed)
2050
+ yield parsed;
2051
+ }
2052
+ } finally {
2053
+ reader.releaseLock();
2054
+ }
2055
+ };
2056
+ var toBytes = (value) => typeof value === "string" ? new TextEncoder().encode(value) : value;
2057
+ var hexToBytes = (hex) => {
2058
+ if (!/^[0-9a-f]+$/iu.test(hex) || hex.length % 2 !== 0)
2059
+ return;
2060
+ const bytes = new Uint8Array(hex.length / 2);
2061
+ for (let index = 0;index < bytes.length; index += 1) {
2062
+ bytes[index] = Number.parseInt(hex.slice(index * 2, index * 2 + 2), 16);
2063
+ }
2064
+ return bytes;
2065
+ };
2066
+ var constantTimeEqual = (left, right) => {
2067
+ if (left.length !== right.length)
2068
+ return false;
2069
+ let mismatch = 0;
2070
+ for (let index = 0;index < left.length; index += 1)
2071
+ mismatch |= (left[index] ?? 0) ^ (right[index] ?? 0);
2072
+ return mismatch === 0;
2073
+ };
2074
+ var verifyOpenRouterWebhookSignature = async (options) => {
2075
+ const fields = new Map(options.header.split(",").map((part) => {
2076
+ const [key2, ...rest] = part.trim().split("=");
2077
+ return [key2, rest.join("=")];
2078
+ }));
2079
+ const timestamp = fields.get("t");
2080
+ const supplied = fields.get("v1");
2081
+ if (!timestamp || !supplied)
2082
+ return false;
2083
+ const timestampNumber = Number(timestamp);
2084
+ const now = options.nowSeconds ?? Math.floor(Date.now() / 1000);
2085
+ const tolerance = options.toleranceSeconds ?? 300;
2086
+ if (!Number.isFinite(timestampNumber) || Math.abs(now - timestampNumber) > tolerance)
2087
+ return false;
2088
+ const key = await crypto.subtle.importKey("raw", Uint8Array.from(toBytes(options.secret)).buffer, { hash: "SHA-256", name: "HMAC" }, false, ["sign"]);
2089
+ const prefix = new TextEncoder().encode(`${timestamp},`);
2090
+ const body = toBytes(options.body);
2091
+ const payload = new Uint8Array(prefix.length + body.length);
2092
+ payload.set(prefix);
2093
+ payload.set(body, prefix.length);
2094
+ const expected = new Uint8Array(await crypto.subtle.sign("HMAC", key, payload.buffer));
2095
+ const suppliedBytes = hexToBytes(supplied);
2096
+ return suppliedBytes ? constantTimeEqual(expected, suppliedBytes) : false;
2097
+ };
1354
2098
  var createOpenRouterClient = (config2) => {
1355
2099
  if (!config2.apiKey && !config2.tokenSource)
1356
2100
  throw new Error("createOpenRouterClient() requires either apiKey or tokenSource");
1357
- const baseUrl = (config2.baseUrl ?? DEFAULT_BASE_URL3).replace(/\/$/, "");
2101
+ const baseUrl = (config2.baseUrl ?? DEFAULT_BASE_URL4).replace(/\/$/, "");
1358
2102
  const fetchImpl = config2.fetch ?? globalThis.fetch;
1359
2103
  const allowedModels = config2.allowedModels ? [...config2.allowedModels] : undefined;
1360
2104
  const requestRaw = async (path, options = {}) => {
@@ -1414,6 +2158,13 @@ var createOpenRouterClient = (config2) => {
1414
2158
  method: "POST"
1415
2159
  });
1416
2160
  },
2161
+ deleteFile: (id, workspaceId) => request(`/files/${encodeURIComponent(id)}`, { method: "DELETE", query: { workspace_id: workspaceId } }),
2162
+ downloadFile: (id, workspaceId) => requestRaw(`/files/${encodeURIComponent(id)}/content`, {
2163
+ query: { workspace_id: workspaceId }
2164
+ }),
2165
+ downloadVideo: (id, index = 0) => requestRaw(`/videos/${encodeURIComponent(id)}/content`, {
2166
+ query: { index }
2167
+ }),
1417
2168
  generateVideo: (body) => {
1418
2169
  assertAllowedModel(body.model, allowedModels);
1419
2170
  return request("/videos", {
@@ -1424,6 +2175,9 @@ var createOpenRouterClient = (config2) => {
1424
2175
  getBatch: (id) => request(`/batches/${encodeURIComponent(id)}`),
1425
2176
  getCredits: () => request("/credits"),
1426
2177
  getCurrentKey: () => request("/key"),
2178
+ getFile: (id, workspaceId) => request(`/files/${encodeURIComponent(id)}`, {
2179
+ query: { workspace_id: workspaceId }
2180
+ }),
1427
2181
  getGeneration: (id) => request("/generation", {
1428
2182
  query: { id }
1429
2183
  }),
@@ -1431,15 +2185,47 @@ var createOpenRouterClient = (config2) => {
1431
2185
  assertAllowedModel(model, allowedModels);
1432
2186
  return request(`/models/${encodeModelPath(model)}/endpoints`);
1433
2187
  },
2188
+ getModel: (model) => {
2189
+ assertAllowedModel(model, allowedModels);
2190
+ return request(`/model/${encodeModelPath(model)}`);
2191
+ },
2192
+ getImageModelEndpoints: (model) => {
2193
+ assertAllowedModel(model, allowedModels);
2194
+ return request(`/images/models/${encodeModelPath(model)}/endpoints`);
2195
+ },
1434
2196
  getVideo: (id) => request(`/videos/${encodeURIComponent(id)}`),
1435
2197
  listImageModels: async () => filterModelList(await request("/images/models")),
2198
+ listFiles: (query) => request("/files", { query }),
1436
2199
  listModels,
2200
+ listUserModels: async () => filterModelList(await request("/models/user")),
2201
+ listZdrEndpoints: async () => {
2202
+ const result = await request("/endpoints/zdr");
2203
+ if (!allowedModels)
2204
+ return result;
2205
+ return {
2206
+ ...result,
2207
+ data: result.data.filter((endpoint) => allowedModels.some((rule) => openRouterModelMatchesRule(endpoint.model_id, rule)))
2208
+ };
2209
+ },
2210
+ countModels: (outputModalities) => request("/models/count", {
2211
+ query: { output_modalities: outputModalities }
2212
+ }),
1437
2213
  listPresets: (offset = 0, limit = 100) => request("/presets", { query: { limit, offset } }),
2214
+ listPresetVersions: (slug, offset = 0, limit = 100) => request(`/presets/${encodeURIComponent(slug)}/versions`, { query: { limit, offset } }),
1438
2215
  listProviders: () => request("/providers"),
1439
2216
  listRerankModels: async () => filterModelList(await request("/rerank/models")),
1440
2217
  listVideoModels: async () => filterModelList(await request("/videos/models")),
1441
2218
  request,
1442
2219
  requestRaw,
2220
+ streamImage: async function* (body, options = {}) {
2221
+ assertAllowedModel(body.model, allowedModels);
2222
+ const response = await requestRaw("/images", {
2223
+ ...options,
2224
+ body: { ...body, stream: true },
2225
+ method: "POST"
2226
+ });
2227
+ yield* parseImageSSE(response);
2228
+ },
1443
2229
  respond: (body) => {
1444
2230
  assertAllowedModel(body.model, allowedModels);
1445
2231
  return body.stream ? requestRaw("/responses", { body, method: "POST" }) : request("/responses", {
@@ -1471,12 +2257,24 @@ var createOpenRouterClient = (config2) => {
1471
2257
  body,
1472
2258
  method: "POST"
1473
2259
  });
2260
+ },
2261
+ uploadFile: (file, options = {}) => {
2262
+ const body = new FormData;
2263
+ if (options.filename)
2264
+ body.append("file", file, options.filename);
2265
+ else
2266
+ body.append("file", file);
2267
+ return request("/files", {
2268
+ body,
2269
+ method: "POST",
2270
+ query: { workspace_id: options.workspaceId }
2271
+ });
1474
2272
  }
1475
2273
  };
1476
2274
  };
1477
2275
 
1478
2276
  // src/ai/providers/openrouter.ts
1479
- var DEFAULT_BASE_URL4 = "https://openrouter.ai/api";
2277
+ var DEFAULT_BASE_URL5 = "https://openrouter.ai/api";
1480
2278
  var MAX_APP_CATEGORIES = 2;
1481
2279
  var withoutLatestPrefix2 = (model) => model.startsWith("~") ? model.slice(1) : model;
1482
2280
  var modelForOpenAICapabilities = (model) => {
@@ -1607,7 +2405,7 @@ var assertAllowedPreset = (preset, allowedPresets) => {
1607
2405
  var assertIndirectModels = (value, allowedModels, key = "") => {
1608
2406
  if (key === "model" && typeof value === "string")
1609
2407
  assertAllowedModel2(value, allowedModels);
1610
- if (key === "models" && Array.isArray(value)) {
2408
+ if ((key === "models" || key === "analysis_models") && Array.isArray(value)) {
1611
2409
  for (const model of value) {
1612
2410
  if (typeof model === "string")
1613
2411
  assertAllowedModel2(model, allowedModels);
@@ -1638,6 +2436,7 @@ var assertRequestOptions = (options, allowedModels, allowedPresets, allowedProvi
1638
2436
  assertAllowedModel2(model, allowedModels);
1639
2437
  }
1640
2438
  assertIndirectModels(options.serverTools, allowedModels);
2439
+ assertIndirectModels(options.messagesTools, allowedModels);
1641
2440
  assertIndirectModels(options.plugins, allowedModels);
1642
2441
  if (options.extraBody) {
1643
2442
  const unsafe = Object.keys(options.extraBody).find((key) => SECURITY_SENSITIVE_EXTRA_BODY_FIELDS.has(key));
@@ -1656,17 +2455,41 @@ var snapshotPolicy = (config2) => ({
1656
2455
  allowedModels: config2.allowedModels ? [...config2.allowedModels] : undefined,
1657
2456
  allowedPresets: config2.allowedPresets ? [...config2.allowedPresets] : undefined
1658
2457
  });
1659
- var transformOpenRouterRequest = (config2, allowedModels, allowedPresets, body, params) => {
2458
+ var transformOpenRouterRequest = (config2, allowedModels, allowedPresets, body, params, skin = "openai") => {
1660
2459
  const options = requestOptionsFor(params, config2.requestOptions);
1661
2460
  assertRequestOptions(options, allowedModels, allowedPresets, config2.allowedProviders);
1662
2461
  const transformed = { ...body, ...options.extraBody };
1663
- if (params.reasoning?.budgetTokens !== undefined) {
1664
- transformed.reasoning = { max_tokens: params.reasoning.budgetTokens };
1665
- delete transformed.reasoning_effort;
1666
- } else if (params.reasoning?.effort) {
1667
- transformed.reasoning = { effort: params.reasoning.effort };
1668
- delete transformed.reasoning_effort;
1669
- }
2462
+ if (options.audioOutput) {
2463
+ transformed.audio = options.audioOutput;
2464
+ transformed.modalities = ["text", "audio"];
2465
+ }
2466
+ if (skin === "openai") {
2467
+ const requestedReasoning = {};
2468
+ if (params.reasoning?.budgetTokens !== undefined) {
2469
+ requestedReasoning.max_tokens = params.reasoning.budgetTokens;
2470
+ delete transformed.reasoning_effort;
2471
+ } else if (params.reasoning?.effort) {
2472
+ requestedReasoning.effort = params.reasoning.effort;
2473
+ delete transformed.reasoning_effort;
2474
+ }
2475
+ if (options.reasoning) {
2476
+ Object.assign(requestedReasoning, options.reasoning);
2477
+ if (options.reasoning.maxTokens !== undefined) {
2478
+ requestedReasoning.max_tokens = options.reasoning.maxTokens;
2479
+ delete requestedReasoning.maxTokens;
2480
+ }
2481
+ }
2482
+ if (Object.keys(requestedReasoning).length > 0)
2483
+ transformed.reasoning = requestedReasoning;
2484
+ }
2485
+ const automaticCacheControl = params.promptCaching === true || params.cacheSystemPrompt === true ? { type: "ephemeral" } : undefined;
2486
+ const cacheControl = options.cacheControl ?? automaticCacheControl;
2487
+ if (cacheControl)
2488
+ transformed.cache_control = cacheControl;
2489
+ if (options.promptCacheKey)
2490
+ transformed.prompt_cache_key = options.promptCacheKey;
2491
+ if (options.promptCacheOptions)
2492
+ transformed.prompt_cache_options = options.promptCacheOptions;
1670
2493
  const routing = mapRouting({ ...config2.routing, ...options.routing }, config2.allowedProviders);
1671
2494
  if (Object.keys(routing).length > 0)
1672
2495
  transformed.provider = routing;
@@ -1686,12 +2509,22 @@ var transformOpenRouterRequest = (config2, allowedModels, allowedPresets, body,
1686
2509
  ...options.serverTools
1687
2510
  ];
1688
2511
  }
2512
+ if (options.messagesTools) {
2513
+ if (skin !== "messages")
2514
+ throw new Error("OpenRouter messagesTools requires openrouterMessages()");
2515
+ transformed.tools = [
2516
+ ...Array.isArray(transformed.tools) ? transformed.tools : [],
2517
+ ...options.messagesTools
2518
+ ];
2519
+ }
1689
2520
  if (options.serviceTier)
1690
2521
  transformed.service_tier = options.serviceTier;
1691
2522
  if (options.sessionId)
1692
2523
  transformed.session_id = options.sessionId;
1693
2524
  if (options.stopServerToolsWhen)
1694
2525
  transformed.stop_server_tools_when = options.stopServerToolsWhen;
2526
+ if (options.trace)
2527
+ transformed.trace = options.trace;
1695
2528
  if (options.transforms)
1696
2529
  transformed.transforms = [...options.transforms];
1697
2530
  if (options.user)
@@ -1721,7 +2554,7 @@ var openrouter = (config2) => {
1721
2554
  const { allowedModels, allowedPresets } = snapshotPolicy(config2);
1722
2555
  const provider = openai({
1723
2556
  apiKey: config2.apiKey,
1724
- baseUrl: config2.baseUrl ?? DEFAULT_BASE_URL4,
2557
+ baseUrl: config2.baseUrl ?? DEFAULT_BASE_URL5,
1725
2558
  fetch: config2.fetch,
1726
2559
  headers: (params) => resolveAttributionHeaders(config2, params),
1727
2560
  modelForCapabilities: modelForOpenAICapabilities,
@@ -1736,7 +2569,7 @@ var openrouterResponses = (config2) => {
1736
2569
  const { allowedModels, allowedPresets } = snapshotPolicy(config2);
1737
2570
  const provider = openaiResponses({
1738
2571
  apiKey: config2.apiKey,
1739
- baseUrl: config2.baseUrl ?? DEFAULT_BASE_URL4,
2572
+ baseUrl: config2.baseUrl ?? DEFAULT_BASE_URL5,
1740
2573
  fetch: config2.fetch,
1741
2574
  headers: (params) => resolveAttributionHeaders(config2, params),
1742
2575
  modelForCapabilities: modelForOpenAICapabilities,
@@ -1746,12 +2579,29 @@ var openrouterResponses = (config2) => {
1746
2579
  });
1747
2580
  return withOpenRouterPolicy(provider, allowedModels, allowedPresets);
1748
2581
  };
2582
+ var openrouterMessages = (config2) => {
2583
+ assertRoutingPolicy(config2);
2584
+ const { allowedModels, allowedPresets } = snapshotPolicy(config2);
2585
+ const provider = anthropic({
2586
+ apiKey: config2.apiKey,
2587
+ authStyle: "bearer",
2588
+ baseUrl: config2.baseUrl ?? DEFAULT_BASE_URL5,
2589
+ fetch: config2.fetch,
2590
+ headers: (params) => resolveAttributionHeaders(config2, params),
2591
+ providerName: "openrouter",
2592
+ tokenSource: config2.tokenSource,
2593
+ transformRequestBody: (body, params) => transformOpenRouterRequest(config2, allowedModels, allowedPresets, body, params, "messages")
2594
+ });
2595
+ return withOpenRouterPolicy(provider, allowedModels, allowedPresets);
2596
+ };
1749
2597
  export {
2598
+ verifyOpenRouterWebhookSignature,
1750
2599
  openrouterResponses,
2600
+ openrouterMessages,
1751
2601
  openrouter,
1752
2602
  openRouterModelMatchesRule,
1753
2603
  createOpenRouterClient
1754
2604
  };
1755
2605
 
1756
- //# debugId=BFDE217D89D0CFF064756E2164756E21
2606
+ //# debugId=29E9DB40EA495C0E64756E2164756E21
1757
2607
  //# sourceMappingURL=openrouter.js.map