@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
package/dist/ai/index.js CHANGED
@@ -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;
@@ -958,7 +1004,12 @@ var mapContentToResponsesFormat = (content) => {
958
1004
  };
959
1005
  var hasToolBlocks = (content) => content.some((block) => block.type === "tool_use" || block.type === "tool_result");
960
1006
  var convertToolBlock = (block) => {
1007
+ if (block.type === "provider_data" && block.provider === "openrouter") {
1008
+ return { ...block.data };
1009
+ }
961
1010
  if (block.type === "tool_use") {
1011
+ if (block.providerData)
1012
+ return { ...block.providerData };
962
1013
  return {
963
1014
  arguments: typeof block.input === "string" ? block.input : JSON.stringify(block.input),
964
1015
  call_id: block.id,
@@ -1094,11 +1145,32 @@ var extractUsage2 = (response) => {
1094
1145
  const { usage } = response;
1095
1146
  const input = typeof usage.input_tokens === "number" ? usage.input_tokens : 0;
1096
1147
  const cached = isRecord2(usage.input_tokens_details) && typeof usage.input_tokens_details.cached_tokens === "number" ? usage.input_tokens_details.cached_tokens : 0;
1097
- return {
1148
+ const outputDetails = isRecord2(usage.output_tokens_details) ? usage.output_tokens_details : undefined;
1149
+ const inputDetails = isRecord2(usage.input_tokens_details) ? usage.input_tokens_details : undefined;
1150
+ const costDetails = isRecord2(usage.cost_details) ? usage.cost_details : undefined;
1151
+ const normalized = {
1098
1152
  cacheReadInputTokens: cached,
1153
+ cacheWriteInputTokens: inputDetails && typeof inputDetails.cache_write_tokens === "number" ? inputDetails.cache_write_tokens : undefined,
1154
+ costCredits: typeof usage.cost === "number" ? usage.cost : undefined,
1099
1155
  inputTokens: Math.max(0, input - cached),
1100
- outputTokens: typeof usage.output_tokens === "number" ? usage.output_tokens : 0
1156
+ outputTokens: typeof usage.output_tokens === "number" ? usage.output_tokens : 0,
1157
+ reasoningTokens: outputDetails && typeof outputDetails.reasoning_tokens === "number" ? outputDetails.reasoning_tokens : undefined,
1158
+ upstreamInferenceCostCredits: costDetails && typeof costDetails.upstream_inference_cost === "number" ? costDetails.upstream_inference_cost : undefined
1101
1159
  };
1160
+ if (isRecord2(usage.server_tool_use)) {
1161
+ normalized.serverToolUse = Object.fromEntries(Object.entries(usage.server_tool_use).filter((entry) => typeof entry[1] === "number"));
1162
+ }
1163
+ return normalized;
1164
+ };
1165
+ var extractResponseMetadata = (response) => {
1166
+ const providerMetadata = isRecord2(response.openrouter_metadata) ? response.openrouter_metadata : undefined;
1167
+ const generationId = typeof response.id === "string" ? response.id : undefined;
1168
+ const model = typeof response.model === "string" ? response.model : undefined;
1169
+ const provider = typeof response.provider === "string" ? response.provider : undefined;
1170
+ const serviceTier = typeof response.service_tier === "string" ? response.service_tier : undefined;
1171
+ if (!providerMetadata && !generationId && !model && !provider && !serviceTier)
1172
+ return;
1173
+ return { generationId, model, provider, providerMetadata, serviceTier };
1102
1174
  };
1103
1175
  var extractMimeFormat = (mimeType) => {
1104
1176
  if (typeof mimeType !== "string") {
@@ -1155,6 +1227,7 @@ var processFunctionCallArgumentsDone = function* (parsed, pendingCalls) {
1155
1227
  id: callId || pending?.callId || itemId,
1156
1228
  input: parseToolInput2(args),
1157
1229
  name,
1230
+ providerData: pending?.providerData ? { ...pending.providerData, arguments: args } : undefined,
1158
1231
  type: "tool_use"
1159
1232
  };
1160
1233
  };
@@ -1173,9 +1246,21 @@ var processOutputItemAdded = (parsed, pendingCalls) => {
1173
1246
  pendingCalls.set(itemId, {
1174
1247
  arguments: "",
1175
1248
  callId,
1176
- name
1249
+ name,
1250
+ providerData: { ...item }
1177
1251
  });
1178
1252
  };
1253
+ var processOutputItemDone = function* (parsed) {
1254
+ if (!isRecord2(parsed.item) || typeof parsed.item.type !== "string")
1255
+ return;
1256
+ if (!parsed.item.type.startsWith("openrouter:"))
1257
+ return;
1258
+ yield {
1259
+ data: { ...parsed.item },
1260
+ provider: "openrouter",
1261
+ type: "provider_event"
1262
+ };
1263
+ };
1179
1264
  var isCompletedImageGeneration = (item) => item.type === "image_generation_call" && item.status === "completed" && typeof item.result === "string" && item.result !== "";
1180
1265
  var buildImageChunk = (item) => ({
1181
1266
  data: typeof item.result === "string" ? item.result : "",
@@ -1191,6 +1276,30 @@ var extractImageFromOutput = function* (output) {
1191
1276
  yield buildImageChunk(item);
1192
1277
  }
1193
1278
  };
1279
+ var extractCitationsFromOutput = function* (output) {
1280
+ for (const item of output) {
1281
+ if (!isRecordArray2(item.content))
1282
+ continue;
1283
+ for (const content of item.content) {
1284
+ if (!Array.isArray(content.annotations))
1285
+ continue;
1286
+ for (const annotation of content.annotations) {
1287
+ if (!isRecord2(annotation) || annotation.type !== "url_citation")
1288
+ continue;
1289
+ if (typeof annotation.url !== "string")
1290
+ continue;
1291
+ yield {
1292
+ content: typeof annotation.content === "string" ? annotation.content : undefined,
1293
+ endIndex: typeof annotation.end_index === "number" ? annotation.end_index : undefined,
1294
+ startIndex: typeof annotation.start_index === "number" ? annotation.start_index : undefined,
1295
+ title: typeof annotation.title === "string" ? annotation.title : undefined,
1296
+ type: "citation",
1297
+ url: annotation.url
1298
+ };
1299
+ }
1300
+ }
1301
+ }
1302
+ };
1194
1303
  var processCompleted = function* (parsed) {
1195
1304
  if (!isRecord2(parsed.response)) {
1196
1305
  yield { type: "done", usage: undefined };
@@ -1198,12 +1307,26 @@ var processCompleted = function* (parsed) {
1198
1307
  }
1199
1308
  const { response } = parsed;
1200
1309
  const usage = extractUsage2(response);
1310
+ const metadata = extractResponseMetadata(response);
1201
1311
  if (isRecordArray2(response.output)) {
1312
+ yield* extractCitationsFromOutput(response.output);
1202
1313
  yield* extractImageFromOutput(response.output);
1203
1314
  }
1204
- yield { type: "done", usage };
1315
+ yield { metadata, type: "done", usage };
1205
1316
  };
1206
- var processSSEEvent = function* (eventType, parsed, pendingCalls) {
1317
+ var responseFailure = (eventType, parsed, providerName) => {
1318
+ const response = isRecord2(parsed.response) ? parsed.response : parsed;
1319
+ const error = isRecord2(response.error) ? response.error : undefined;
1320
+ const type = typeof response.error_type === "string" ? response.error_type : error && typeof error.code === "string" ? error.code : eventType;
1321
+ return new ProviderError({
1322
+ message: error && typeof error.message === "string" ? error.message : `OpenRouter Responses API: ${eventType}`,
1323
+ metadata: response,
1324
+ provider: providerName,
1325
+ retryable: type === "rate_limit_exceeded" || type === "provider_overloaded" || type === "provider_unavailable" || type === "server",
1326
+ type
1327
+ });
1328
+ };
1329
+ var processSSEEvent = function* (eventType, parsed, pendingCalls, providerName) {
1207
1330
  switch (eventType) {
1208
1331
  case "response.reasoning_summary_text.delta": {
1209
1332
  const delta = typeof parsed.delta === "string" ? parsed.delta : "";
@@ -1224,6 +1347,9 @@ var processSSEEvent = function* (eventType, parsed, pendingCalls) {
1224
1347
  case "response.output_item.added":
1225
1348
  processOutputItemAdded(parsed, pendingCalls);
1226
1349
  break;
1350
+ case "response.output_item.done":
1351
+ yield* processOutputItemDone(parsed);
1352
+ break;
1227
1353
  case "response.function_call_arguments.delta":
1228
1354
  processFunctionCallArgumentsDelta(parsed, pendingCalls);
1229
1355
  break;
@@ -1234,11 +1360,10 @@ var processSSEEvent = function* (eventType, parsed, pendingCalls) {
1234
1360
  yield* processCompleted(parsed);
1235
1361
  break;
1236
1362
  case "response.failed":
1237
- case "response.incomplete": {
1238
- const respObj = isRecord2(parsed.response) ? parsed.response : parsed;
1239
- const errMsg = isRecord2(respObj.error) && typeof respObj.error.message === "string" ? respObj.error.message : `OpenAI Responses API: ${eventType}`;
1240
- throw new Error(errMsg);
1241
- }
1363
+ case "response.incomplete":
1364
+ case "response.error":
1365
+ case "error":
1366
+ throw responseFailure(eventType, parsed, providerName);
1242
1367
  }
1243
1368
  };
1244
1369
  var flushSSEBuffer = function* (state) {
@@ -1247,7 +1372,7 @@ var flushSSEBuffer = function* (state) {
1247
1372
  }
1248
1373
  const parsed = parseJSON(state.buffer);
1249
1374
  if (parsed) {
1250
- yield* processSSEEvent(state.currentEvent, parsed, state.pendingCalls);
1375
+ yield* processSSEEvent(state.currentEvent, parsed, state.pendingCalls, state.providerName);
1251
1376
  }
1252
1377
  state.currentEvent = "";
1253
1378
  state.buffer = "";
@@ -1285,17 +1410,19 @@ var drainReader2 = async function* (reader, decoder, state, signal) {
1285
1410
  yield* processSSELines2([textBuffer, ""], state);
1286
1411
  }
1287
1412
  };
1288
- var parseSSEStream2 = async function* (body, signal) {
1413
+ var parseSSEStream2 = async function* (body, providerName, signal) {
1289
1414
  const reader = body.getReader();
1290
1415
  const decoder = new TextDecoder;
1291
1416
  const state = {
1292
1417
  buffer: "",
1293
1418
  currentEvent: "",
1294
1419
  pendingCalls: new Map,
1295
- usage: undefined
1420
+ usage: undefined,
1421
+ providerName
1296
1422
  };
1297
1423
  try {
1298
1424
  yield* drainReader2(reader, decoder, state, signal);
1425
+ yield* flushSSEBuffer(state);
1299
1426
  } finally {
1300
1427
  reader.releaseLock();
1301
1428
  }
@@ -1323,7 +1450,7 @@ var fetchResponsesStream = async function* (baseUrl, apiKey, body, fetchImpl, he
1323
1450
  retryable: true
1324
1451
  });
1325
1452
  }
1326
- yield* parseSSEStream2(response.body, signal);
1453
+ yield* parseSSEStream2(response.body, providerName, signal);
1327
1454
  };
1328
1455
  var resolveImageModels = (imageModels) => {
1329
1456
  if (!imageModels) {
@@ -1717,6 +1844,8 @@ var mapContentBlock2 = (block) => {
1717
1844
  };
1718
1845
  }
1719
1846
  if (block.type === "tool_use") {
1847
+ if (block.providerData)
1848
+ return { ...block.providerData };
1720
1849
  return {
1721
1850
  id: block.id,
1722
1851
  input: block.input,
@@ -1727,6 +1856,9 @@ var mapContentBlock2 = (block) => {
1727
1856
  if (block.type === "audio" || block.type === "video") {
1728
1857
  throw new Error(`Anthropic does not support ${block.type} content blocks`);
1729
1858
  }
1859
+ if (block.type === "provider_data") {
1860
+ return { ...block.data };
1861
+ }
1730
1862
  return { text: block.content, type: "text" };
1731
1863
  };
1732
1864
  var mapMessage = (msg) => ({
@@ -1900,11 +2032,15 @@ var handleContentBlockStart = (parsed, state) => {
1900
2032
  state.currentToolName = getString(block, "name");
1901
2033
  state.toolInputJson = "";
1902
2034
  state.isThinkingBlock = false;
2035
+ state.currentProviderBlock = undefined;
1903
2036
  } else if (block && block.type === "thinking") {
1904
2037
  state.isThinkingBlock = true;
1905
2038
  state.thinkingSignature = "";
2039
+ state.currentProviderBlock = undefined;
1906
2040
  } else {
1907
2041
  state.isThinkingBlock = false;
2042
+ state.currentProviderBlock = block && block.type !== "text" ? { ...block } : undefined;
2043
+ state.providerBlockInputJson = "";
1908
2044
  }
1909
2045
  };
1910
2046
  var handleContentBlockDelta = (parsed, state) => {
@@ -1925,7 +2061,11 @@ var handleContentBlockDelta = (parsed, state) => {
1925
2061
  };
1926
2062
  }
1927
2063
  if (delta.type === "input_json_delta") {
1928
- state.toolInputJson += getString(delta, "partial_json");
2064
+ if (state.currentProviderBlock) {
2065
+ state.providerBlockInputJson += getString(delta, "partial_json");
2066
+ } else {
2067
+ state.toolInputJson += getString(delta, "partial_json");
2068
+ }
1929
2069
  }
1930
2070
  if (delta.type === "signature_delta") {
1931
2071
  state.thinkingSignature += getString(delta, "signature");
@@ -1943,6 +2083,19 @@ var handleContentBlockStop = (state) => {
1943
2083
  type: "thinking"
1944
2084
  };
1945
2085
  }
2086
+ if (state.currentProviderBlock) {
2087
+ const data = { ...state.currentProviderBlock };
2088
+ if (state.providerBlockInputJson) {
2089
+ data.input = tryParseJson(state.providerBlockInputJson) ?? state.providerBlockInputJson;
2090
+ }
2091
+ state.currentProviderBlock = undefined;
2092
+ state.providerBlockInputJson = "";
2093
+ return {
2094
+ data,
2095
+ provider: state.providerName,
2096
+ type: "provider_event"
2097
+ };
2098
+ }
1946
2099
  if (!state.currentToolId) {
1947
2100
  return;
1948
2101
  }
@@ -1962,11 +2115,39 @@ var extractUsage4 = (usageRecord, existingUsage) => {
1962
2115
  if (!usageRecord) {
1963
2116
  return existingUsage;
1964
2117
  }
1965
- return {
2118
+ const normalized = {
1966
2119
  cacheReadInputTokens: getNumber(usageRecord, "cache_read_input_tokens") || existingUsage?.cacheReadInputTokens || 0,
1967
2120
  cacheWriteInputTokens: getNumber(usageRecord, "cache_creation_input_tokens") || existingUsage?.cacheWriteInputTokens || 0,
1968
2121
  inputTokens: getNumber(usageRecord, "input_tokens") || existingUsage?.inputTokens || 0,
1969
- outputTokens: getNumber(usageRecord, "output_tokens") || existingUsage?.outputTokens || 0
2122
+ outputTokens: getNumber(usageRecord, "output_tokens") || existingUsage?.outputTokens || 0,
2123
+ costCredits: getNumber(usageRecord, "cost") || existingUsage?.costCredits,
2124
+ reasoningTokens: getNumber(usageRecord, "reasoning_tokens") || existingUsage?.reasoningTokens,
2125
+ upstreamInferenceCostCredits: getNumber(getRecord(usageRecord, "cost_details") ?? {}, "upstream_inference_cost") || existingUsage?.upstreamInferenceCostCredits
2126
+ };
2127
+ const serverToolUse = getRecord(usageRecord, "server_tool_use");
2128
+ if (serverToolUse) {
2129
+ normalized.serverToolUse = Object.fromEntries(Object.entries(serverToolUse).filter((entry) => typeof entry[1] === "number"));
2130
+ }
2131
+ return normalized;
2132
+ };
2133
+ var mergeMetadata = (source, state) => {
2134
+ const providerMetadata = getRecord(source, "openrouter_metadata");
2135
+ const generationId = getString(source, "id") || undefined;
2136
+ const model = getString(source, "model") || undefined;
2137
+ const provider = getString(source, "provider") || undefined;
2138
+ const serviceTier = getString(source, "service_tier") || undefined;
2139
+ if (!providerMetadata && !generationId && !model && !provider && !serviceTier)
2140
+ return;
2141
+ state.metadata = {
2142
+ ...state.metadata,
2143
+ generationId: generationId ?? state.metadata?.generationId,
2144
+ model: model ?? state.metadata?.model,
2145
+ provider: provider ?? state.metadata?.provider,
2146
+ providerMetadata: {
2147
+ ...state.metadata?.providerMetadata,
2148
+ ...providerMetadata
2149
+ },
2150
+ serviceTier: serviceTier ?? state.metadata?.serviceTier
1970
2151
  };
1971
2152
  };
1972
2153
  var handleMessageDelta = (parsed, state) => {
@@ -1984,15 +2165,18 @@ var handleMessageStart = (parsed, state) => {
1984
2165
  }
1985
2166
  const startUsage = getRecord(message, "usage");
1986
2167
  state.usage = extractUsage4(startUsage, state.usage);
2168
+ mergeMetadata(message, state);
1987
2169
  };
1988
- var handleError = (parsed) => {
2170
+ var handleError = (parsed, state) => {
1989
2171
  const error = getRecord(parsed, "error");
1990
2172
  const errorMessage = error ? getString(error, "message") : "";
1991
- const errorType = error ? getString(error, "type") : "";
1992
- const retryable = errorType === "overloaded_error" || errorType === "rate_limit_error" || errorType === "api_error";
2173
+ const nativeErrorType = error ? getString(error, "type") : "";
2174
+ const errorType = error ? getString(error, "error_type") || nativeErrorType : "";
2175
+ const retryable = errorType === "provider_overloaded" || errorType === "rate_limit_exceeded" || errorType === "provider_unavailable" || errorType === "server" || nativeErrorType === "overloaded_error" || nativeErrorType === "rate_limit_error" || nativeErrorType === "api_error";
1993
2176
  throw new ProviderError({
1994
2177
  message: errorMessage || "Anthropic API error",
1995
- provider: "anthropic",
2178
+ metadata: error,
2179
+ provider: state.providerName,
1996
2180
  retryable,
1997
2181
  type: errorType || null
1998
2182
  });
@@ -2011,6 +2195,7 @@ var processEvent = (eventType, parsed, state) => {
2011
2195
  }
2012
2196
  case "message_delta": {
2013
2197
  handleMessageDelta(parsed, state);
2198
+ mergeMetadata(parsed, state);
2014
2199
  return;
2015
2200
  }
2016
2201
  case "message_start": {
@@ -2018,14 +2203,16 @@ var processEvent = (eventType, parsed, state) => {
2018
2203
  return;
2019
2204
  }
2020
2205
  case "message_stop": {
2206
+ mergeMetadata(parsed, state);
2021
2207
  return {
2022
2208
  stopReason: state.stopReason,
2209
+ metadata: state.metadata,
2023
2210
  type: "done",
2024
2211
  usage: state.usage
2025
2212
  };
2026
2213
  }
2027
2214
  case "error": {
2028
- handleError(parsed);
2215
+ handleError(parsed, state);
2029
2216
  return;
2030
2217
  }
2031
2218
  default: {
@@ -2090,7 +2277,7 @@ async function* streamChunks(reader, decoder, state, signal) {
2090
2277
  yield* result.chunks;
2091
2278
  }
2092
2279
  }
2093
- async function* parseSSEStream4(body, signal) {
2280
+ async function* parseSSEStream4(body, providerName, signal) {
2094
2281
  const reader = body.getReader();
2095
2282
  const decoder = new TextDecoder;
2096
2283
  const state = {
@@ -2101,7 +2288,9 @@ async function* parseSSEStream4(body, signal) {
2101
2288
  stopReason: "",
2102
2289
  thinkingSignature: "",
2103
2290
  toolInputJson: "",
2104
- usage: undefined
2291
+ usage: undefined,
2292
+ providerName,
2293
+ providerBlockInputJson: ""
2105
2294
  };
2106
2295
  try {
2107
2296
  yield* streamChunks(reader, decoder, state, signal);
@@ -2109,41 +2298,51 @@ async function* parseSSEStream4(body, signal) {
2109
2298
  reader.releaseLock();
2110
2299
  }
2111
2300
  }
2112
- var fetchAndStream = async function* (baseUrl, config2, params, configuredMax, promptCaching) {
2113
- const body = buildRequestBody4(params, configuredMax, promptCaching);
2301
+ var fetchAndStream = async function* (baseUrl, config2, params, configuredMax, promptCaching, providerName) {
2302
+ const builtBody = buildRequestBody4(params, configuredMax, promptCaching);
2303
+ const body = config2.transformRequestBody ? config2.transformRequestBody(builtBody, params) : builtBody;
2114
2304
  const target = `${baseUrl}/v1/messages`;
2115
2305
  const fetchImpl = config2.fetch ?? fetch;
2306
+ const token = config2.tokenSource ? await Promise.resolve(config2.tokenSource()) : config2.apiKey;
2307
+ const suppliedHeaders = typeof config2.headers === "function" ? await config2.headers(params) : config2.headers ?? {};
2308
+ const requestHeaders = new Headers(suppliedHeaders);
2309
+ requestHeaders.set("Content-Type", "application/json");
2310
+ if (config2.authStyle === "bearer") {
2311
+ requestHeaders.set("Authorization", `Bearer ${token}`);
2312
+ } else {
2313
+ requestHeaders.set("anthropic-version", API_VERSION);
2314
+ requestHeaders.set("x-api-key", token);
2315
+ }
2116
2316
  const response = await fetchImpl(target, {
2117
2317
  ...h2IfHttps4(target),
2118
2318
  body: JSON.stringify(body),
2119
- headers: {
2120
- "anthropic-version": API_VERSION,
2121
- "Content-Type": "application/json",
2122
- "x-api-key": config2.apiKey
2123
- },
2319
+ headers: requestHeaders,
2124
2320
  method: "POST",
2125
2321
  signal: params.signal
2126
2322
  });
2127
2323
  if (!response.ok) {
2128
2324
  const errorText = await response.text();
2129
- throw ProviderError.fromResponse("anthropic", response.status, errorText);
2325
+ throw ProviderError.fromResponse(providerName, response.status, errorText);
2130
2326
  }
2131
2327
  if (!response.body) {
2132
2328
  throw new ProviderError({
2133
- message: "Anthropic API returned no response body",
2134
- provider: "anthropic",
2329
+ message: `${providerName} Messages API returned no response body`,
2330
+ provider: providerName,
2135
2331
  retryable: true
2136
2332
  });
2137
2333
  }
2138
- yield* parseSSEStream4(response.body, params.signal);
2334
+ yield* parseSSEStream4(response.body, providerName, params.signal);
2139
2335
  };
2140
2336
  var anthropic = (config2) => {
2337
+ if (!config2.apiKey && !config2.tokenSource)
2338
+ throw new Error("anthropic() requires either apiKey or tokenSource");
2141
2339
  const baseUrl = config2.baseUrl ?? DEFAULT_BASE_URL4;
2142
2340
  const configuredMax = config2.maxTokens ?? DEFAULT_MAX_TOKENS;
2143
2341
  const promptCaching = config2.promptCaching ?? true;
2342
+ const providerName = config2.providerName ?? "anthropic";
2144
2343
  return instrumentAIProvider({
2145
- stream: (params) => fetchAndStream(baseUrl, config2, params, configuredMax, promptCaching)
2146
- }, "anthropic");
2344
+ stream: (params) => fetchAndStream(baseUrl, config2, params, configuredMax, promptCaching, providerName)
2345
+ }, providerName);
2147
2346
  };
2148
2347
 
2149
2348
  // src/ai/providers/ollama.ts
@@ -2411,6 +2610,85 @@ var withQuery = (url, query) => {
2411
2610
  }
2412
2611
  return result.toString();
2413
2612
  };
2613
+ var parseImageSSE = async function* (response) {
2614
+ if (!response.body)
2615
+ throw new Error("OpenRouter image stream has no body");
2616
+ const reader = response.body.getReader();
2617
+ const decoder = new TextDecoder;
2618
+ let buffer = "";
2619
+ try {
2620
+ for (;; ) {
2621
+ const result = await reader.read();
2622
+ buffer += decoder.decode(result.value, { stream: !result.done });
2623
+ const lines = buffer.split(`
2624
+ `);
2625
+ buffer = lines.pop() ?? "";
2626
+ for (const line of lines) {
2627
+ if (!line.startsWith("data: "))
2628
+ continue;
2629
+ const data = line.slice(6);
2630
+ if (data === "[DONE]")
2631
+ return;
2632
+ try {
2633
+ const parsed = JSON.parse(data);
2634
+ if (parsed && typeof parsed === "object" && "type" in parsed)
2635
+ yield parsed;
2636
+ } catch {}
2637
+ }
2638
+ if (result.done)
2639
+ break;
2640
+ }
2641
+ if (buffer.startsWith("data: ")) {
2642
+ const parsed = JSON.parse(buffer.slice(6));
2643
+ if (parsed && typeof parsed === "object" && "type" in parsed)
2644
+ yield parsed;
2645
+ }
2646
+ } finally {
2647
+ reader.releaseLock();
2648
+ }
2649
+ };
2650
+ var toBytes = (value) => typeof value === "string" ? new TextEncoder().encode(value) : value;
2651
+ var hexToBytes = (hex) => {
2652
+ if (!/^[0-9a-f]+$/iu.test(hex) || hex.length % 2 !== 0)
2653
+ return;
2654
+ const bytes = new Uint8Array(hex.length / 2);
2655
+ for (let index = 0;index < bytes.length; index += 1) {
2656
+ bytes[index] = Number.parseInt(hex.slice(index * 2, index * 2 + 2), 16);
2657
+ }
2658
+ return bytes;
2659
+ };
2660
+ var constantTimeEqual = (left, right) => {
2661
+ if (left.length !== right.length)
2662
+ return false;
2663
+ let mismatch = 0;
2664
+ for (let index = 0;index < left.length; index += 1)
2665
+ mismatch |= (left[index] ?? 0) ^ (right[index] ?? 0);
2666
+ return mismatch === 0;
2667
+ };
2668
+ var verifyOpenRouterWebhookSignature = async (options) => {
2669
+ const fields = new Map(options.header.split(",").map((part) => {
2670
+ const [key2, ...rest] = part.trim().split("=");
2671
+ return [key2, rest.join("=")];
2672
+ }));
2673
+ const timestamp = fields.get("t");
2674
+ const supplied = fields.get("v1");
2675
+ if (!timestamp || !supplied)
2676
+ return false;
2677
+ const timestampNumber = Number(timestamp);
2678
+ const now = options.nowSeconds ?? Math.floor(Date.now() / 1000);
2679
+ const tolerance = options.toleranceSeconds ?? 300;
2680
+ if (!Number.isFinite(timestampNumber) || Math.abs(now - timestampNumber) > tolerance)
2681
+ return false;
2682
+ const key = await crypto.subtle.importKey("raw", Uint8Array.from(toBytes(options.secret)).buffer, { hash: "SHA-256", name: "HMAC" }, false, ["sign"]);
2683
+ const prefix = new TextEncoder().encode(`${timestamp},`);
2684
+ const body = toBytes(options.body);
2685
+ const payload = new Uint8Array(prefix.length + body.length);
2686
+ payload.set(prefix);
2687
+ payload.set(body, prefix.length);
2688
+ const expected = new Uint8Array(await crypto.subtle.sign("HMAC", key, payload.buffer));
2689
+ const suppliedBytes = hexToBytes(supplied);
2690
+ return suppliedBytes ? constantTimeEqual(expected, suppliedBytes) : false;
2691
+ };
2414
2692
  var createOpenRouterClient = (config2) => {
2415
2693
  if (!config2.apiKey && !config2.tokenSource)
2416
2694
  throw new Error("createOpenRouterClient() requires either apiKey or tokenSource");
@@ -2474,6 +2752,13 @@ var createOpenRouterClient = (config2) => {
2474
2752
  method: "POST"
2475
2753
  });
2476
2754
  },
2755
+ deleteFile: (id, workspaceId) => request(`/files/${encodeURIComponent(id)}`, { method: "DELETE", query: { workspace_id: workspaceId } }),
2756
+ downloadFile: (id, workspaceId) => requestRaw(`/files/${encodeURIComponent(id)}/content`, {
2757
+ query: { workspace_id: workspaceId }
2758
+ }),
2759
+ downloadVideo: (id, index = 0) => requestRaw(`/videos/${encodeURIComponent(id)}/content`, {
2760
+ query: { index }
2761
+ }),
2477
2762
  generateVideo: (body) => {
2478
2763
  assertAllowedModel(body.model, allowedModels);
2479
2764
  return request("/videos", {
@@ -2484,6 +2769,9 @@ var createOpenRouterClient = (config2) => {
2484
2769
  getBatch: (id) => request(`/batches/${encodeURIComponent(id)}`),
2485
2770
  getCredits: () => request("/credits"),
2486
2771
  getCurrentKey: () => request("/key"),
2772
+ getFile: (id, workspaceId) => request(`/files/${encodeURIComponent(id)}`, {
2773
+ query: { workspace_id: workspaceId }
2774
+ }),
2487
2775
  getGeneration: (id) => request("/generation", {
2488
2776
  query: { id }
2489
2777
  }),
@@ -2491,15 +2779,47 @@ var createOpenRouterClient = (config2) => {
2491
2779
  assertAllowedModel(model, allowedModels);
2492
2780
  return request(`/models/${encodeModelPath(model)}/endpoints`);
2493
2781
  },
2782
+ getModel: (model) => {
2783
+ assertAllowedModel(model, allowedModels);
2784
+ return request(`/model/${encodeModelPath(model)}`);
2785
+ },
2786
+ getImageModelEndpoints: (model) => {
2787
+ assertAllowedModel(model, allowedModels);
2788
+ return request(`/images/models/${encodeModelPath(model)}/endpoints`);
2789
+ },
2494
2790
  getVideo: (id) => request(`/videos/${encodeURIComponent(id)}`),
2495
2791
  listImageModels: async () => filterModelList(await request("/images/models")),
2792
+ listFiles: (query) => request("/files", { query }),
2496
2793
  listModels,
2794
+ listUserModels: async () => filterModelList(await request("/models/user")),
2795
+ listZdrEndpoints: async () => {
2796
+ const result = await request("/endpoints/zdr");
2797
+ if (!allowedModels)
2798
+ return result;
2799
+ return {
2800
+ ...result,
2801
+ data: result.data.filter((endpoint) => allowedModels.some((rule) => openRouterModelMatchesRule(endpoint.model_id, rule)))
2802
+ };
2803
+ },
2804
+ countModels: (outputModalities) => request("/models/count", {
2805
+ query: { output_modalities: outputModalities }
2806
+ }),
2497
2807
  listPresets: (offset = 0, limit = 100) => request("/presets", { query: { limit, offset } }),
2808
+ listPresetVersions: (slug, offset = 0, limit = 100) => request(`/presets/${encodeURIComponent(slug)}/versions`, { query: { limit, offset } }),
2498
2809
  listProviders: () => request("/providers"),
2499
2810
  listRerankModels: async () => filterModelList(await request("/rerank/models")),
2500
2811
  listVideoModels: async () => filterModelList(await request("/videos/models")),
2501
2812
  request,
2502
2813
  requestRaw,
2814
+ streamImage: async function* (body, options = {}) {
2815
+ assertAllowedModel(body.model, allowedModels);
2816
+ const response = await requestRaw("/images", {
2817
+ ...options,
2818
+ body: { ...body, stream: true },
2819
+ method: "POST"
2820
+ });
2821
+ yield* parseImageSSE(response);
2822
+ },
2503
2823
  respond: (body) => {
2504
2824
  assertAllowedModel(body.model, allowedModels);
2505
2825
  return body.stream ? requestRaw("/responses", { body, method: "POST" }) : request("/responses", {
@@ -2531,6 +2851,18 @@ var createOpenRouterClient = (config2) => {
2531
2851
  body,
2532
2852
  method: "POST"
2533
2853
  });
2854
+ },
2855
+ uploadFile: (file, options = {}) => {
2856
+ const body = new FormData;
2857
+ if (options.filename)
2858
+ body.append("file", file, options.filename);
2859
+ else
2860
+ body.append("file", file);
2861
+ return request("/files", {
2862
+ body,
2863
+ method: "POST",
2864
+ query: { workspace_id: options.workspaceId }
2865
+ });
2534
2866
  }
2535
2867
  };
2536
2868
  };
@@ -2667,7 +2999,7 @@ var assertAllowedPreset = (preset, allowedPresets) => {
2667
2999
  var assertIndirectModels = (value, allowedModels, key = "") => {
2668
3000
  if (key === "model" && typeof value === "string")
2669
3001
  assertAllowedModel2(value, allowedModels);
2670
- if (key === "models" && Array.isArray(value)) {
3002
+ if ((key === "models" || key === "analysis_models") && Array.isArray(value)) {
2671
3003
  for (const model of value) {
2672
3004
  if (typeof model === "string")
2673
3005
  assertAllowedModel2(model, allowedModels);
@@ -2698,6 +3030,7 @@ var assertRequestOptions = (options, allowedModels, allowedPresets, allowedProvi
2698
3030
  assertAllowedModel2(model, allowedModels);
2699
3031
  }
2700
3032
  assertIndirectModels(options.serverTools, allowedModels);
3033
+ assertIndirectModels(options.messagesTools, allowedModels);
2701
3034
  assertIndirectModels(options.plugins, allowedModels);
2702
3035
  if (options.extraBody) {
2703
3036
  const unsafe = Object.keys(options.extraBody).find((key) => SECURITY_SENSITIVE_EXTRA_BODY_FIELDS.has(key));
@@ -2716,17 +3049,41 @@ var snapshotPolicy = (config2) => ({
2716
3049
  allowedModels: config2.allowedModels ? [...config2.allowedModels] : undefined,
2717
3050
  allowedPresets: config2.allowedPresets ? [...config2.allowedPresets] : undefined
2718
3051
  });
2719
- var transformOpenRouterRequest = (config2, allowedModels, allowedPresets, body, params) => {
3052
+ var transformOpenRouterRequest = (config2, allowedModels, allowedPresets, body, params, skin = "openai") => {
2720
3053
  const options = requestOptionsFor(params, config2.requestOptions);
2721
3054
  assertRequestOptions(options, allowedModels, allowedPresets, config2.allowedProviders);
2722
3055
  const transformed = { ...body, ...options.extraBody };
2723
- if (params.reasoning?.budgetTokens !== undefined) {
2724
- transformed.reasoning = { max_tokens: params.reasoning.budgetTokens };
2725
- delete transformed.reasoning_effort;
2726
- } else if (params.reasoning?.effort) {
2727
- transformed.reasoning = { effort: params.reasoning.effort };
2728
- delete transformed.reasoning_effort;
2729
- }
3056
+ if (options.audioOutput) {
3057
+ transformed.audio = options.audioOutput;
3058
+ transformed.modalities = ["text", "audio"];
3059
+ }
3060
+ if (skin === "openai") {
3061
+ const requestedReasoning = {};
3062
+ if (params.reasoning?.budgetTokens !== undefined) {
3063
+ requestedReasoning.max_tokens = params.reasoning.budgetTokens;
3064
+ delete transformed.reasoning_effort;
3065
+ } else if (params.reasoning?.effort) {
3066
+ requestedReasoning.effort = params.reasoning.effort;
3067
+ delete transformed.reasoning_effort;
3068
+ }
3069
+ if (options.reasoning) {
3070
+ Object.assign(requestedReasoning, options.reasoning);
3071
+ if (options.reasoning.maxTokens !== undefined) {
3072
+ requestedReasoning.max_tokens = options.reasoning.maxTokens;
3073
+ delete requestedReasoning.maxTokens;
3074
+ }
3075
+ }
3076
+ if (Object.keys(requestedReasoning).length > 0)
3077
+ transformed.reasoning = requestedReasoning;
3078
+ }
3079
+ const automaticCacheControl = params.promptCaching === true || params.cacheSystemPrompt === true ? { type: "ephemeral" } : undefined;
3080
+ const cacheControl = options.cacheControl ?? automaticCacheControl;
3081
+ if (cacheControl)
3082
+ transformed.cache_control = cacheControl;
3083
+ if (options.promptCacheKey)
3084
+ transformed.prompt_cache_key = options.promptCacheKey;
3085
+ if (options.promptCacheOptions)
3086
+ transformed.prompt_cache_options = options.promptCacheOptions;
2730
3087
  const routing = mapRouting({ ...config2.routing, ...options.routing }, config2.allowedProviders);
2731
3088
  if (Object.keys(routing).length > 0)
2732
3089
  transformed.provider = routing;
@@ -2746,12 +3103,22 @@ var transformOpenRouterRequest = (config2, allowedModels, allowedPresets, body,
2746
3103
  ...options.serverTools
2747
3104
  ];
2748
3105
  }
3106
+ if (options.messagesTools) {
3107
+ if (skin !== "messages")
3108
+ throw new Error("OpenRouter messagesTools requires openrouterMessages()");
3109
+ transformed.tools = [
3110
+ ...Array.isArray(transformed.tools) ? transformed.tools : [],
3111
+ ...options.messagesTools
3112
+ ];
3113
+ }
2749
3114
  if (options.serviceTier)
2750
3115
  transformed.service_tier = options.serviceTier;
2751
3116
  if (options.sessionId)
2752
3117
  transformed.session_id = options.sessionId;
2753
3118
  if (options.stopServerToolsWhen)
2754
3119
  transformed.stop_server_tools_when = options.stopServerToolsWhen;
3120
+ if (options.trace)
3121
+ transformed.trace = options.trace;
2755
3122
  if (options.transforms)
2756
3123
  transformed.transforms = [...options.transforms];
2757
3124
  if (options.user)
@@ -2806,6 +3173,21 @@ var openrouterResponses = (config2) => {
2806
3173
  });
2807
3174
  return withOpenRouterPolicy(provider, allowedModels, allowedPresets);
2808
3175
  };
3176
+ var openrouterMessages = (config2) => {
3177
+ assertRoutingPolicy(config2);
3178
+ const { allowedModels, allowedPresets } = snapshotPolicy(config2);
3179
+ const provider = anthropic({
3180
+ apiKey: config2.apiKey,
3181
+ authStyle: "bearer",
3182
+ baseUrl: config2.baseUrl ?? DEFAULT_BASE_URL7,
3183
+ fetch: config2.fetch,
3184
+ headers: (params) => resolveAttributionHeaders(config2, params),
3185
+ providerName: "openrouter",
3186
+ tokenSource: config2.tokenSource,
3187
+ transformRequestBody: (body, params) => transformOpenRouterRequest(config2, allowedModels, allowedPresets, body, params, "messages")
3188
+ });
3189
+ return withOpenRouterPolicy(provider, allowedModels, allowedPresets);
3190
+ };
2809
3191
 
2810
3192
  // src/plugins/aiChat.ts
2811
3193
  import { Elysia } from "elysia";
@@ -2982,6 +3364,8 @@ var isValidAIServerMessage = (data) => {
2982
3364
  return "name" in data && "status" in data && "messageId" in data && "conversationId" in data;
2983
3365
  case "image":
2984
3366
  return "data" in data && typeof data.data === "string" && "format" in data && typeof data.format === "string" && "isPartial" in data && typeof data.isPartial === "boolean" && "messageId" in data && "conversationId" in data;
3367
+ case "audio":
3368
+ return "data" in data && typeof data.data === "string" && "format" in data && typeof data.format === "string" && "messageId" in data && "conversationId" in data;
2985
3369
  case "complete":
2986
3370
  return "messageId" in data && "conversationId" in data;
2987
3371
  case "turn_queued":
@@ -3081,6 +3465,15 @@ var sendImageMessage = async (socket, chunk, messageId, conversationId) => sendM
3081
3465
  revisedPrompt: chunk.revisedPrompt,
3082
3466
  type: "image"
3083
3467
  });
3468
+ var sendAudioMessage = async (socket, chunk, messageId, conversationId) => sendMessage(socket, {
3469
+ audioId: chunk.audioId,
3470
+ conversationId,
3471
+ data: chunk.data,
3472
+ format: chunk.format,
3473
+ messageId,
3474
+ transcript: chunk.transcript,
3475
+ type: "audio"
3476
+ });
3084
3477
  var sendToolRunning = async (socket, toolName, toolInput, messageId, conversationId) => sendMessage(socket, {
3085
3478
  conversationId,
3086
3479
  input: toolInput,
@@ -3174,6 +3567,15 @@ var processToolChunk = (chunk, state, options, socket, messageId, conversationId
3174
3567
  revisedPrompt: chunk.revisedPrompt
3175
3568
  });
3176
3569
  break;
3570
+ case "audio":
3571
+ sendAudioMessage(socket, chunk, messageId, conversationId);
3572
+ options.onAudio?.({
3573
+ audioId: chunk.audioId,
3574
+ data: chunk.data,
3575
+ format: chunk.format,
3576
+ transcript: chunk.transcript
3577
+ });
3578
+ break;
3177
3579
  case "tool_use":
3178
3580
  flushThinking(state);
3179
3581
  handleToolChunkToolUse(chunk, state);
@@ -3181,6 +3583,7 @@ var processToolChunk = (chunk, state, options, socket, messageId, conversationId
3181
3583
  id: chunk.id,
3182
3584
  input: typeof chunk.input === "object" && chunk.input !== null ? chunk.input : {},
3183
3585
  name: chunk.name,
3586
+ providerData: chunk.providerData,
3184
3587
  type: "tool_use"
3185
3588
  });
3186
3589
  hitAnotherTool = true;
@@ -3368,6 +3771,15 @@ var consumeStreamChunk = async (chunk, options, socket, state, messageId, conver
3368
3771
  revisedPrompt: chunk.revisedPrompt
3369
3772
  });
3370
3773
  break;
3774
+ case "audio":
3775
+ await sendAudioMessage(socket, chunk, messageId, conversationId);
3776
+ options.onAudio?.({
3777
+ audioId: chunk.audioId,
3778
+ data: chunk.data,
3779
+ format: chunk.format,
3780
+ transcript: chunk.transcript
3781
+ });
3782
+ break;
3371
3783
  case "tool_use":
3372
3784
  flushStreamThinking(state);
3373
3785
  state.pendingToolCalls.push({
@@ -3379,6 +3791,7 @@ var consumeStreamChunk = async (chunk, options, socket, state, messageId, conver
3379
3791
  id: chunk.id,
3380
3792
  input: typeof chunk.input === "object" && chunk.input !== null ? chunk.input : {},
3381
3793
  name: chunk.name,
3794
+ providerData: chunk.providerData,
3382
3795
  type: "tool_use"
3383
3796
  });
3384
3797
  break;
@@ -3479,6 +3892,10 @@ var serializeToolCall2 = (name, input) => `${name}:${JSON.stringify(input)}`;
3479
3892
  var contentEvent = (options, renderers, delta, full) => options.structuredEvents ? { data: JSON.stringify({ delta, full }), event: "content" } : { data: renderers.chunk(delta, full), event: "content" };
3480
3893
  var thinkingEvent = (options, renderers, text) => options.structuredEvents ? { data: JSON.stringify({ text }), event: "thinking" } : { data: renderers.thinking(text), event: "thinking" };
3481
3894
  var imageEvent = (options, renderers, data, format, revisedPrompt) => options.structuredEvents ? { data: JSON.stringify({ data, format, revisedPrompt }), event: "images" } : { data: renderers.image(data, format, revisedPrompt), event: "images" };
3895
+ var audioEvent = (data, format, transcript, audioId) => ({
3896
+ data: JSON.stringify({ audioId, data, format, transcript }),
3897
+ event: "audio"
3898
+ });
3482
3899
  var completeEvent = (options, renderers, fullResponse, usage, durationMs) => {
3483
3900
  options.onComplete?.(fullResponse, usage);
3484
3901
  return options.structuredEvents ? {
@@ -3548,6 +3965,15 @@ var processImageChunk = function* (chunk, renderers, options) {
3548
3965
  revisedPrompt: chunk.revisedPrompt
3549
3966
  });
3550
3967
  };
3968
+ var processAudioChunk = function* (chunk, options) {
3969
+ yield audioEvent(chunk.data, chunk.format, chunk.transcript, chunk.audioId);
3970
+ options.onAudio?.({
3971
+ audioId: chunk.audioId,
3972
+ data: chunk.data,
3973
+ format: chunk.format,
3974
+ transcript: chunk.transcript
3975
+ });
3976
+ };
3551
3977
  var processToolUseChunk = (chunk, chunkState) => {
3552
3978
  maybeFlushThinking(chunkState);
3553
3979
  chunkState.pendingToolCalls.push({
@@ -3559,6 +3985,7 @@ var processToolUseChunk = (chunk, chunkState) => {
3559
3985
  id: chunk.id,
3560
3986
  input: typeof chunk.input === "object" && chunk.input !== null ? chunk.input : {},
3561
3987
  name: chunk.name,
3988
+ providerData: chunk.providerData,
3562
3989
  type: "tool_use"
3563
3990
  });
3564
3991
  };
@@ -3573,6 +4000,9 @@ var processChunk2 = function* (chunk, chunkState, renderers, options, fullRespon
3573
4000
  case "image":
3574
4001
  yield* processImageChunk(chunk, renderers, options);
3575
4002
  break;
4003
+ case "audio":
4004
+ yield* processAudioChunk(chunk, options);
4005
+ break;
3576
4006
  case "tool_use":
3577
4007
  processToolUseChunk(chunk, chunkState);
3578
4008
  break;
@@ -4456,6 +4886,14 @@ var streamAIWithTools = async function* (options) {
4456
4886
  fullText += chunk.content;
4457
4887
  pushText(blocks, chunk.content);
4458
4888
  yield { content: chunk.content, type: "text" };
4889
+ } else if (chunk.type === "audio") {
4890
+ yield {
4891
+ audioId: chunk.audioId,
4892
+ data: chunk.data,
4893
+ format: chunk.format,
4894
+ transcript: chunk.transcript,
4895
+ type: "audio"
4896
+ };
4459
4897
  } else if (chunk.type === "tool_use") {
4460
4898
  thinking = flushThinking3(blocks, thinking);
4461
4899
  pending.push({ id: chunk.id, input: chunk.input, name: chunk.name });
@@ -4463,8 +4901,16 @@ var streamAIWithTools = async function* (options) {
4463
4901
  id: chunk.id,
4464
4902
  input: chunk.input && typeof chunk.input === "object" ? chunk.input : {},
4465
4903
  name: chunk.name,
4904
+ providerData: chunk.providerData,
4466
4905
  type: "tool_use"
4467
4906
  });
4907
+ } else if (chunk.type === "provider_event") {
4908
+ thinking = flushThinking3(blocks, thinking);
4909
+ blocks.push({
4910
+ data: chunk.data,
4911
+ provider: chunk.provider,
4912
+ type: "provider_data"
4913
+ });
4468
4914
  } else if (chunk.type === "done") {
4469
4915
  thinking = flushThinking3(blocks, thinking);
4470
4916
  turnUsage = chunk.usage;
@@ -6009,6 +6455,16 @@ var serverMessageToAction = (message) => {
6009
6455
  revisedPrompt: message.revisedPrompt,
6010
6456
  type: "image"
6011
6457
  };
6458
+ case "audio":
6459
+ return {
6460
+ audioId: message.audioId,
6461
+ conversationId: message.conversationId,
6462
+ data: message.data,
6463
+ format: message.format,
6464
+ messageId: message.messageId,
6465
+ transcript: message.transcript,
6466
+ type: "audio"
6467
+ };
6012
6468
  case "complete":
6013
6469
  return {
6014
6470
  conversationId: message.conversationId,
@@ -6411,6 +6867,20 @@ var handleImage = (state, action) => {
6411
6867
  });
6412
6868
  conversation.messages = [...conversation.messages];
6413
6869
  };
6870
+ var handleAudio = (state, action) => {
6871
+ const conversation = getOrCreate(state, action.conversationId);
6872
+ const message = getOrCreateAssistantMessage(conversation, action.messageId, action.conversationId);
6873
+ message.audio = [
6874
+ ...message.audio ?? [],
6875
+ {
6876
+ audioId: action.audioId,
6877
+ data: action.data,
6878
+ format: action.format,
6879
+ transcript: action.transcript
6880
+ }
6881
+ ];
6882
+ conversation.messages = [...conversation.messages];
6883
+ };
6414
6884
  var handleComplete = (state, action) => {
6415
6885
  const conversation = state.conversations.get(action.conversationId);
6416
6886
  if (conversation) {
@@ -6481,6 +6951,9 @@ var applyAction = (state, action) => {
6481
6951
  case "image":
6482
6952
  handleImage(state, action);
6483
6953
  break;
6954
+ case "audio":
6955
+ handleAudio(state, action);
6956
+ break;
6484
6957
  case "complete":
6485
6958
  handleComplete(state, action);
6486
6959
  break;
@@ -6800,6 +7273,7 @@ var startProviderStatusMonitor = (options) => {
6800
7273
  export {
6801
7274
  xai,
6802
7275
  withResilience,
7276
+ verifyOpenRouterWebhookSignature,
6803
7277
  tableCard,
6804
7278
  streamAIWithTools,
6805
7279
  streamAIToSSE,
@@ -6827,6 +7301,7 @@ export {
6827
7301
  parseChartSpec,
6828
7302
  parseAIMessage,
6829
7303
  openrouterResponses,
7304
+ openrouterMessages,
6830
7305
  openrouter,
6831
7306
  openaiResponses,
6832
7307
  openaiCompatible,
@@ -6889,5 +7364,5 @@ export {
6889
7364
  BUILTIN_UI_CARDS
6890
7365
  };
6891
7366
 
6892
- //# debugId=A06FECA819C53C2464756E2164756E21
7367
+ //# debugId=3B5ACA11D1118B9564756E2164756E21
6893
7368
  //# sourceMappingURL=index.js.map