@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.
- package/README.md +22 -12
- package/dist/ai/client/index.js +30 -1
- package/dist/ai/client/index.js.map +5 -5
- package/dist/ai/index.js +531 -56
- package/dist/ai/index.js.map +15 -15
- package/dist/ai/providers/anthropic.js +100 -25
- package/dist/ai/providers/anthropic.js.map +5 -5
- package/dist/ai/providers/gemini.js +5 -2
- package/dist/ai/providers/gemini.js.map +4 -4
- package/dist/ai/providers/ollama.js +5 -2
- package/dist/ai/providers/ollama.js.map +4 -4
- package/dist/ai/providers/openai.js +56 -10
- package/dist/ai/providers/openai.js.map +5 -5
- package/dist/ai/providers/openaiCompatible.js +56 -10
- package/dist/ai/providers/openaiCompatible.js.map +5 -5
- package/dist/ai/providers/openaiResponses.js +100 -16
- package/dist/ai/providers/openaiResponses.js.map +5 -5
- package/dist/ai/providers/openrouter.js +888 -38
- package/dist/ai/providers/openrouter.js.map +10 -9
- package/dist/angular/ai/index.js +30 -1
- package/dist/angular/ai/index.js.map +5 -5
- package/dist/react/ai/index.js +30 -1
- package/dist/react/ai/index.js.map +5 -5
- package/dist/src/ai/client/actions.d.ts +53 -0
- package/dist/src/ai/errors/providerError.d.ts +3 -0
- package/dist/src/ai/index.d.ts +2 -2
- package/dist/src/ai/providers/openrouter.d.ts +46 -2
- package/dist/src/ai/providers/openrouterClient.d.ts +132 -3
- package/dist/src/ai/streamAIWithTools.d.ts +6 -0
- package/dist/svelte/ai/index.js +30 -1
- package/dist/svelte/ai/index.js.map +5 -5
- package/dist/types/ai.d.ts +54 -3
- package/dist/types/anthropic.d.ts +13 -5
- package/dist/vue/ai/index.js +30 -1
- package/dist/vue/ai/index.js.map +5 -5
- 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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
1239
|
-
|
|
1240
|
-
throw
|
|
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.
|
|
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
|
-
|
|
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
|
|
1992
|
-
const
|
|
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
|
-
|
|
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
|
|
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(
|
|
2325
|
+
throw ProviderError.fromResponse(providerName, response.status, errorText);
|
|
2130
2326
|
}
|
|
2131
2327
|
if (!response.body) {
|
|
2132
2328
|
throw new ProviderError({
|
|
2133
|
-
message:
|
|
2134
|
-
provider:
|
|
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
|
-
},
|
|
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 (
|
|
2724
|
-
transformed.
|
|
2725
|
-
|
|
2726
|
-
}
|
|
2727
|
-
|
|
2728
|
-
|
|
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=
|
|
7367
|
+
//# debugId=3B5ACA11D1118B9564756E2164756E21
|
|
6893
7368
|
//# sourceMappingURL=index.js.map
|