@absolutejs/ai 0.0.50 → 0.0.52
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 +84 -15
- package/dist/ai/client/index.js +30 -1
- package/dist/ai/client/index.js.map +5 -5
- package/dist/ai/index.js +753 -62
- 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 +1111 -45
- 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 +56 -3
- package/dist/src/ai/providers/openrouterClient.d.ts +495 -9
- 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
|
|
@@ -2385,7 +2584,46 @@ var ollama = (config2 = {}) => {
|
|
|
2385
2584
|
};
|
|
2386
2585
|
|
|
2387
2586
|
// src/ai/providers/openrouterClient.ts
|
|
2587
|
+
var OPENROUTER_PRICING_KEYS = [
|
|
2588
|
+
"prompt",
|
|
2589
|
+
"completion",
|
|
2590
|
+
"request",
|
|
2591
|
+
"image",
|
|
2592
|
+
"web_search",
|
|
2593
|
+
"internal_reasoning",
|
|
2594
|
+
"input_cache_read",
|
|
2595
|
+
"input_cache_write"
|
|
2596
|
+
];
|
|
2597
|
+
var estimateOpenRouterCost = (pricing, units) => {
|
|
2598
|
+
const components = {};
|
|
2599
|
+
let total = 0;
|
|
2600
|
+
for (const key of OPENROUTER_PRICING_KEYS) {
|
|
2601
|
+
const quantity = units[key];
|
|
2602
|
+
if (quantity === undefined)
|
|
2603
|
+
continue;
|
|
2604
|
+
if (!Number.isFinite(quantity) || quantity < 0)
|
|
2605
|
+
throw new Error(`OpenRouter ${key} units must be non-negative`);
|
|
2606
|
+
const rawPrice = pricing[key];
|
|
2607
|
+
if (rawPrice === undefined)
|
|
2608
|
+
continue;
|
|
2609
|
+
const price = Number(rawPrice);
|
|
2610
|
+
if (!Number.isFinite(price) || price < 0)
|
|
2611
|
+
throw new Error(`OpenRouter ${key} price must be non-negative`);
|
|
2612
|
+
components[key] = price * quantity;
|
|
2613
|
+
total += components[key];
|
|
2614
|
+
}
|
|
2615
|
+
return { components, total };
|
|
2616
|
+
};
|
|
2617
|
+
var estimateOpenRouterModelCost = (model, units) => estimateOpenRouterCost(model.pricing ?? {}, units);
|
|
2388
2618
|
var DEFAULT_BASE_URL6 = "https://openrouter.ai/api/v1";
|
|
2619
|
+
var DEFAULT_BATCH_BASE_URL = "https://openrouter.ai/api/beta";
|
|
2620
|
+
var DEFAULT_SITE_URL = "https://openrouter.ai";
|
|
2621
|
+
var TERMINAL_BATCH_STATUSES = new Set([
|
|
2622
|
+
"completed",
|
|
2623
|
+
"failed",
|
|
2624
|
+
"expired",
|
|
2625
|
+
"cancelled"
|
|
2626
|
+
]);
|
|
2389
2627
|
var withoutLatestPrefix = (model) => model.startsWith("~") ? model.slice(1) : model;
|
|
2390
2628
|
var openRouterModelMatchesRule = (model, rule) => {
|
|
2391
2629
|
const normalizedModel = withoutLatestPrefix(model);
|
|
@@ -2399,6 +2637,22 @@ var assertAllowedModel = (model, allowedModels) => {
|
|
|
2399
2637
|
return;
|
|
2400
2638
|
throw new Error(`OpenRouter model "${model}" is not allowed`);
|
|
2401
2639
|
};
|
|
2640
|
+
var assertAllowedModelsInValue = (value, allowedModels, key = "") => {
|
|
2641
|
+
if (key === "model" && typeof value === "string")
|
|
2642
|
+
assertAllowedModel(value, allowedModels);
|
|
2643
|
+
if ((key === "models" || key === "analysis_models" || key === "allowed_models") && Array.isArray(value)) {
|
|
2644
|
+
for (const model of value)
|
|
2645
|
+
if (typeof model === "string")
|
|
2646
|
+
assertAllowedModel(model, allowedModels);
|
|
2647
|
+
}
|
|
2648
|
+
if (Array.isArray(value)) {
|
|
2649
|
+
for (const item of value)
|
|
2650
|
+
assertAllowedModelsInValue(item, allowedModels);
|
|
2651
|
+
} else if (value && typeof value === "object") {
|
|
2652
|
+
for (const [childKey, child] of Object.entries(value))
|
|
2653
|
+
assertAllowedModelsInValue(child, allowedModels, childKey);
|
|
2654
|
+
}
|
|
2655
|
+
};
|
|
2402
2656
|
var normalizePath = (path) => path.startsWith("/") ? path : `/${path}`;
|
|
2403
2657
|
var encodeModelPath = (model) => model.split("/").map(encodeURIComponent).join("/");
|
|
2404
2658
|
var withQuery = (url, query) => {
|
|
@@ -2411,13 +2665,142 @@ var withQuery = (url, query) => {
|
|
|
2411
2665
|
}
|
|
2412
2666
|
return result.toString();
|
|
2413
2667
|
};
|
|
2668
|
+
var parseImageSSE = async function* (response) {
|
|
2669
|
+
if (!response.body)
|
|
2670
|
+
throw new Error("OpenRouter image stream has no body");
|
|
2671
|
+
const reader = response.body.getReader();
|
|
2672
|
+
const decoder = new TextDecoder;
|
|
2673
|
+
let buffer = "";
|
|
2674
|
+
try {
|
|
2675
|
+
for (;; ) {
|
|
2676
|
+
const result = await reader.read();
|
|
2677
|
+
buffer += decoder.decode(result.value, { stream: !result.done });
|
|
2678
|
+
const lines = buffer.split(`
|
|
2679
|
+
`);
|
|
2680
|
+
buffer = lines.pop() ?? "";
|
|
2681
|
+
for (const line of lines) {
|
|
2682
|
+
if (!line.startsWith("data: "))
|
|
2683
|
+
continue;
|
|
2684
|
+
const data = line.slice(6);
|
|
2685
|
+
if (data === "[DONE]")
|
|
2686
|
+
return;
|
|
2687
|
+
try {
|
|
2688
|
+
const parsed = JSON.parse(data);
|
|
2689
|
+
if (parsed && typeof parsed === "object" && "type" in parsed)
|
|
2690
|
+
yield parsed;
|
|
2691
|
+
} catch {}
|
|
2692
|
+
}
|
|
2693
|
+
if (result.done)
|
|
2694
|
+
break;
|
|
2695
|
+
}
|
|
2696
|
+
if (buffer.startsWith("data: ")) {
|
|
2697
|
+
const parsed = JSON.parse(buffer.slice(6));
|
|
2698
|
+
if (parsed && typeof parsed === "object" && "type" in parsed)
|
|
2699
|
+
yield parsed;
|
|
2700
|
+
}
|
|
2701
|
+
} finally {
|
|
2702
|
+
reader.releaseLock();
|
|
2703
|
+
}
|
|
2704
|
+
};
|
|
2705
|
+
var toBytes = (value) => typeof value === "string" ? new TextEncoder().encode(value) : value;
|
|
2706
|
+
var toBase64Url = (bytes) => {
|
|
2707
|
+
let binary = "";
|
|
2708
|
+
for (const byte of bytes)
|
|
2709
|
+
binary += String.fromCharCode(byte);
|
|
2710
|
+
return btoa(binary).replaceAll("+", "-").replaceAll("/", "_").replace(/=+$/u, "");
|
|
2711
|
+
};
|
|
2712
|
+
var generateOpenRouterPKCE = async () => {
|
|
2713
|
+
const random = crypto.getRandomValues(new Uint8Array(32));
|
|
2714
|
+
const codeVerifier = toBase64Url(random);
|
|
2715
|
+
const digest = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(codeVerifier));
|
|
2716
|
+
return {
|
|
2717
|
+
codeChallenge: toBase64Url(new Uint8Array(digest)),
|
|
2718
|
+
codeChallengeMethod: "S256",
|
|
2719
|
+
codeVerifier
|
|
2720
|
+
};
|
|
2721
|
+
};
|
|
2722
|
+
var createOpenRouterAuthorizationUrl = (options = {}) => {
|
|
2723
|
+
const url = new URL("/auth", options.baseUrl ?? DEFAULT_SITE_URL);
|
|
2724
|
+
if (options.callbackUrl)
|
|
2725
|
+
url.searchParams.set("callback_url", options.callbackUrl);
|
|
2726
|
+
if (options.codeChallenge)
|
|
2727
|
+
url.searchParams.set("code_challenge", options.codeChallenge);
|
|
2728
|
+
if (options.codeChallengeMethod)
|
|
2729
|
+
url.searchParams.set("code_challenge_method", options.codeChallengeMethod);
|
|
2730
|
+
if (options.keyLabel)
|
|
2731
|
+
url.searchParams.set("key_label", options.keyLabel);
|
|
2732
|
+
return url.toString();
|
|
2733
|
+
};
|
|
2734
|
+
var exchangeOpenRouterAuthCode = async (body, options = {}) => {
|
|
2735
|
+
const response = await (options.fetch ?? globalThis.fetch)(`${(options.baseUrl ?? DEFAULT_BASE_URL6).replace(/\/$/, "")}/auth/keys`, {
|
|
2736
|
+
body: JSON.stringify(body),
|
|
2737
|
+
headers: { "Content-Type": "application/json" },
|
|
2738
|
+
method: "POST"
|
|
2739
|
+
});
|
|
2740
|
+
if (!response.ok)
|
|
2741
|
+
throw ProviderError.fromResponse("openrouter", response.status, await response.text());
|
|
2742
|
+
return response.json();
|
|
2743
|
+
};
|
|
2744
|
+
var createOpenRouterKeyLinks = async (key, siteUrl = DEFAULT_SITE_URL) => {
|
|
2745
|
+
const digest = new Uint8Array(await crypto.subtle.digest("SHA-256", new TextEncoder().encode(key)));
|
|
2746
|
+
const hash = Array.from(digest, (byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
2747
|
+
const root = siteUrl.replace(/\/$/, "");
|
|
2748
|
+
return {
|
|
2749
|
+
hash,
|
|
2750
|
+
logsUrl: `${root}/logs?api_key_hash=${hash}`,
|
|
2751
|
+
settingsUrl: `${root}/keys/${hash}`
|
|
2752
|
+
};
|
|
2753
|
+
};
|
|
2754
|
+
var hexToBytes = (hex) => {
|
|
2755
|
+
if (!/^[0-9a-f]+$/iu.test(hex) || hex.length % 2 !== 0)
|
|
2756
|
+
return;
|
|
2757
|
+
const bytes = new Uint8Array(hex.length / 2);
|
|
2758
|
+
for (let index = 0;index < bytes.length; index += 1) {
|
|
2759
|
+
bytes[index] = Number.parseInt(hex.slice(index * 2, index * 2 + 2), 16);
|
|
2760
|
+
}
|
|
2761
|
+
return bytes;
|
|
2762
|
+
};
|
|
2763
|
+
var constantTimeEqual = (left, right) => {
|
|
2764
|
+
if (left.length !== right.length)
|
|
2765
|
+
return false;
|
|
2766
|
+
let mismatch = 0;
|
|
2767
|
+
for (let index = 0;index < left.length; index += 1)
|
|
2768
|
+
mismatch |= (left[index] ?? 0) ^ (right[index] ?? 0);
|
|
2769
|
+
return mismatch === 0;
|
|
2770
|
+
};
|
|
2771
|
+
var verifyOpenRouterWebhookSignature = async (options) => {
|
|
2772
|
+
const fields = new Map(options.header.split(",").map((part) => {
|
|
2773
|
+
const [key2, ...rest] = part.trim().split("=");
|
|
2774
|
+
return [key2, rest.join("=")];
|
|
2775
|
+
}));
|
|
2776
|
+
const timestamp = fields.get("t");
|
|
2777
|
+
const supplied = fields.get("v1");
|
|
2778
|
+
if (!timestamp || !supplied)
|
|
2779
|
+
return false;
|
|
2780
|
+
const timestampNumber = Number(timestamp);
|
|
2781
|
+
const now = options.nowSeconds ?? Math.floor(Date.now() / 1000);
|
|
2782
|
+
const tolerance = options.toleranceSeconds ?? 300;
|
|
2783
|
+
if (!Number.isFinite(timestampNumber) || Math.abs(now - timestampNumber) > tolerance)
|
|
2784
|
+
return false;
|
|
2785
|
+
const key = await crypto.subtle.importKey("raw", Uint8Array.from(toBytes(options.secret)).buffer, { hash: "SHA-256", name: "HMAC" }, false, ["sign"]);
|
|
2786
|
+
const prefix = new TextEncoder().encode(`${timestamp},`);
|
|
2787
|
+
const body = toBytes(options.body);
|
|
2788
|
+
const payload = new Uint8Array(prefix.length + body.length);
|
|
2789
|
+
payload.set(prefix);
|
|
2790
|
+
payload.set(body, prefix.length);
|
|
2791
|
+
const expected = new Uint8Array(await crypto.subtle.sign("HMAC", key, payload.buffer));
|
|
2792
|
+
const suppliedBytes = hexToBytes(supplied);
|
|
2793
|
+
return suppliedBytes ? constantTimeEqual(expected, suppliedBytes) : false;
|
|
2794
|
+
};
|
|
2414
2795
|
var createOpenRouterClient = (config2) => {
|
|
2415
2796
|
if (!config2.apiKey && !config2.tokenSource)
|
|
2416
2797
|
throw new Error("createOpenRouterClient() requires either apiKey or tokenSource");
|
|
2417
2798
|
const baseUrl = (config2.baseUrl ?? DEFAULT_BASE_URL6).replace(/\/$/, "");
|
|
2799
|
+
const batchBaseUrl = (config2.batchBaseUrl ?? (config2.baseUrl ? new URL("../beta", `${baseUrl}/`).toString() : DEFAULT_BATCH_BASE_URL)).replace(/\/$/, "");
|
|
2418
2800
|
const fetchImpl = config2.fetch ?? globalThis.fetch;
|
|
2419
2801
|
const allowedModels = config2.allowedModels ? [...config2.allowedModels] : undefined;
|
|
2420
|
-
const
|
|
2802
|
+
const defaultWorkspaceId = config2.workspaceId;
|
|
2803
|
+
const requestRawAt = async (rootUrl, path, options = {}) => {
|
|
2421
2804
|
const token = config2.tokenSource ? await Promise.resolve(config2.tokenSource()) : config2.apiKey;
|
|
2422
2805
|
const suppliedHeaders = typeof config2.headers === "function" ? await config2.headers() : config2.headers ?? {};
|
|
2423
2806
|
const headers = new Headers(suppliedHeaders);
|
|
@@ -2431,13 +2814,16 @@ var createOpenRouterClient = (config2) => {
|
|
|
2431
2814
|
body = JSON.stringify(options.body);
|
|
2432
2815
|
}
|
|
2433
2816
|
const { query, ...requestInit } = options;
|
|
2434
|
-
const response = await fetchImpl(withQuery(`${
|
|
2817
|
+
const response = await fetchImpl(withQuery(`${rootUrl}${normalizePath(path)}`, options.query), { ...requestInit, body, headers });
|
|
2435
2818
|
if (!response.ok) {
|
|
2436
2819
|
throw ProviderError.fromResponse("openrouter", response.status, await response.text());
|
|
2437
2820
|
}
|
|
2438
2821
|
return response;
|
|
2439
2822
|
};
|
|
2823
|
+
const requestRaw = (path, options = {}) => requestRawAt(baseUrl, path, options);
|
|
2440
2824
|
const request = async (path, options = {}) => (await requestRaw(path, options)).json();
|
|
2825
|
+
const requestBatch = async (path, options = {}) => (await requestRawAt(batchBaseUrl, path, options)).json();
|
|
2826
|
+
const getBatch = (id) => requestBatch(`/batches/${encodeURIComponent(id)}`);
|
|
2441
2827
|
const listModels = async (query) => {
|
|
2442
2828
|
const result = await request("/models", { query });
|
|
2443
2829
|
if (!allowedModels)
|
|
@@ -2456,10 +2842,45 @@ var createOpenRouterClient = (config2) => {
|
|
|
2456
2842
|
};
|
|
2457
2843
|
};
|
|
2458
2844
|
return {
|
|
2459
|
-
|
|
2845
|
+
addWorkspaceMembers: (id, userIds) => request(`/workspaces/${encodeURIComponent(id)}/members/add`, { body: { user_ids: [...userIds] }, method: "POST" }),
|
|
2846
|
+
createAuthCode: (body) => request("/auth/keys/code", {
|
|
2847
|
+
body: {
|
|
2848
|
+
...body,
|
|
2849
|
+
workspace_id: body.workspace_id ?? defaultWorkspaceId
|
|
2850
|
+
},
|
|
2460
2851
|
method: "POST"
|
|
2461
2852
|
}),
|
|
2462
|
-
|
|
2853
|
+
createPresetFromChatCompletions: (slug, body) => {
|
|
2854
|
+
assertAllowedModelsInValue(body, allowedModels);
|
|
2855
|
+
return request(`/presets/${encodeURIComponent(slug)}/chat/completions`, { body, method: "POST" });
|
|
2856
|
+
},
|
|
2857
|
+
createPresetFromMessages: (slug, body) => {
|
|
2858
|
+
assertAllowedModelsInValue(body, allowedModels);
|
|
2859
|
+
return request(`/presets/${encodeURIComponent(slug)}/messages`, { body, method: "POST" });
|
|
2860
|
+
},
|
|
2861
|
+
createPresetFromResponses: (slug, body) => {
|
|
2862
|
+
assertAllowedModelsInValue(body, allowedModels);
|
|
2863
|
+
return request(`/presets/${encodeURIComponent(slug)}/responses`, { body, method: "POST" });
|
|
2864
|
+
},
|
|
2865
|
+
createWorkspace: (body) => {
|
|
2866
|
+
assertAllowedModelsInValue(body, allowedModels);
|
|
2867
|
+
return request("/workspaces", {
|
|
2868
|
+
body,
|
|
2869
|
+
method: "POST"
|
|
2870
|
+
});
|
|
2871
|
+
},
|
|
2872
|
+
createBatch: (body) => {
|
|
2873
|
+
assertAllowedModel(body.model, allowedModels);
|
|
2874
|
+
return requestBatch("/batches", {
|
|
2875
|
+
body: {
|
|
2876
|
+
endpoint: body.endpoint,
|
|
2877
|
+
model: body.model,
|
|
2878
|
+
requests: body.requests,
|
|
2879
|
+
...body.completion_window ? { completion_window: body.completion_window } : {}
|
|
2880
|
+
},
|
|
2881
|
+
method: "POST"
|
|
2882
|
+
});
|
|
2883
|
+
},
|
|
2463
2884
|
createEmbedding: (body) => {
|
|
2464
2885
|
assertAllowedModel(body.model, allowedModels);
|
|
2465
2886
|
return request("/embeddings", {
|
|
@@ -2474,6 +2895,17 @@ var createOpenRouterClient = (config2) => {
|
|
|
2474
2895
|
method: "POST"
|
|
2475
2896
|
});
|
|
2476
2897
|
},
|
|
2898
|
+
deleteFile: (id, workspaceId = defaultWorkspaceId) => request(`/files/${encodeURIComponent(id)}`, { method: "DELETE", query: { workspace_id: workspaceId } }),
|
|
2899
|
+
deleteWorkspace: (id) => request(`/workspaces/${encodeURIComponent(id)}`, {
|
|
2900
|
+
method: "DELETE"
|
|
2901
|
+
}),
|
|
2902
|
+
deleteWorkspaceBudget: (id, interval) => request(`/workspaces/${encodeURIComponent(id)}/budgets/${interval}`, { method: "DELETE" }),
|
|
2903
|
+
downloadFile: (id, workspaceId = defaultWorkspaceId) => requestRaw(`/files/${encodeURIComponent(id)}/content`, {
|
|
2904
|
+
query: { workspace_id: workspaceId }
|
|
2905
|
+
}),
|
|
2906
|
+
downloadVideo: (id, index = 0) => requestRaw(`/videos/${encodeURIComponent(id)}/content`, {
|
|
2907
|
+
query: { index }
|
|
2908
|
+
}),
|
|
2477
2909
|
generateVideo: (body) => {
|
|
2478
2910
|
assertAllowedModel(body.model, allowedModels);
|
|
2479
2911
|
return request("/videos", {
|
|
@@ -2481,25 +2913,90 @@ var createOpenRouterClient = (config2) => {
|
|
|
2481
2913
|
method: "POST"
|
|
2482
2914
|
});
|
|
2483
2915
|
},
|
|
2484
|
-
getBatch
|
|
2916
|
+
getBatch,
|
|
2917
|
+
getActivity: (query) => request("/activity", {
|
|
2918
|
+
query: {
|
|
2919
|
+
...query,
|
|
2920
|
+
workspace_id: query?.workspace_id ?? defaultWorkspaceId
|
|
2921
|
+
}
|
|
2922
|
+
}),
|
|
2923
|
+
getAnalyticsMeta: () => request("/analytics/meta"),
|
|
2485
2924
|
getCredits: () => request("/credits"),
|
|
2486
2925
|
getCurrentKey: () => request("/key"),
|
|
2926
|
+
getFile: (id, workspaceId = defaultWorkspaceId) => request(`/files/${encodeURIComponent(id)}`, {
|
|
2927
|
+
query: { workspace_id: workspaceId }
|
|
2928
|
+
}),
|
|
2487
2929
|
getGeneration: (id) => request("/generation", {
|
|
2488
2930
|
query: { id }
|
|
2489
2931
|
}),
|
|
2932
|
+
getGenerationContent: (id) => request("/generation/content", {
|
|
2933
|
+
query: { id }
|
|
2934
|
+
}),
|
|
2490
2935
|
getModelEndpoints: (model) => {
|
|
2491
2936
|
assertAllowedModel(model, allowedModels);
|
|
2492
2937
|
return request(`/models/${encodeModelPath(model)}/endpoints`);
|
|
2493
2938
|
},
|
|
2939
|
+
getModel: (model) => {
|
|
2940
|
+
assertAllowedModel(model, allowedModels);
|
|
2941
|
+
return request(`/model/${encodeModelPath(model)}`);
|
|
2942
|
+
},
|
|
2943
|
+
getImageModelEndpoints: (model) => {
|
|
2944
|
+
assertAllowedModel(model, allowedModels);
|
|
2945
|
+
return request(`/images/models/${encodeModelPath(model)}/endpoints`);
|
|
2946
|
+
},
|
|
2947
|
+
getPreset: (slug) => request(`/presets/${encodeURIComponent(slug)}`),
|
|
2948
|
+
getPresetVersion: (slug, version) => request(`/presets/${encodeURIComponent(slug)}/versions/${encodeURIComponent(String(version))}`),
|
|
2949
|
+
getTaskClassifications: (window2 = "7d") => request("/classifications/task", {
|
|
2950
|
+
query: { window: window2 }
|
|
2951
|
+
}),
|
|
2494
2952
|
getVideo: (id) => request(`/videos/${encodeURIComponent(id)}`),
|
|
2953
|
+
getWorkspace: (id) => request(`/workspaces/${encodeURIComponent(id)}`),
|
|
2495
2954
|
listImageModels: async () => filterModelList(await request("/images/models")),
|
|
2955
|
+
listFiles: (query) => request("/files", {
|
|
2956
|
+
query: {
|
|
2957
|
+
...query,
|
|
2958
|
+
workspace_id: query?.workspace_id ?? defaultWorkspaceId
|
|
2959
|
+
}
|
|
2960
|
+
}),
|
|
2496
2961
|
listModels,
|
|
2497
|
-
|
|
2962
|
+
listUserModels: async () => filterModelList(await request("/models/user")),
|
|
2963
|
+
listZdrEndpoints: async () => {
|
|
2964
|
+
const result = await request("/endpoints/zdr");
|
|
2965
|
+
if (!allowedModels)
|
|
2966
|
+
return result;
|
|
2967
|
+
return {
|
|
2968
|
+
...result,
|
|
2969
|
+
data: result.data.filter((endpoint) => allowedModels.some((rule) => openRouterModelMatchesRule(endpoint.model_id, rule)))
|
|
2970
|
+
};
|
|
2971
|
+
},
|
|
2972
|
+
countModels: (outputModalities) => request("/models/count", {
|
|
2973
|
+
query: { output_modalities: outputModalities }
|
|
2974
|
+
}),
|
|
2975
|
+
listPresets: (offset = 0, limit = 100) => request("/presets", {
|
|
2976
|
+
query: { limit, offset }
|
|
2977
|
+
}),
|
|
2978
|
+
listPresetVersions: (slug, offset = 0, limit = 100) => request(`/presets/${encodeURIComponent(slug)}/versions`, { query: { limit, offset } }),
|
|
2498
2979
|
listProviders: () => request("/providers"),
|
|
2499
2980
|
listRerankModels: async () => filterModelList(await request("/rerank/models")),
|
|
2500
2981
|
listVideoModels: async () => filterModelList(await request("/videos/models")),
|
|
2982
|
+
listWorkspaceBudgets: (id) => request(`/workspaces/${encodeURIComponent(id)}/budgets`),
|
|
2983
|
+
listWorkspaces: (offset = 0, limit = 100) => request("/workspaces", { query: { limit, offset } }),
|
|
2984
|
+
queryAnalytics: (body) => request("/analytics/query", {
|
|
2985
|
+
body,
|
|
2986
|
+
method: "POST"
|
|
2987
|
+
}),
|
|
2988
|
+
removeWorkspaceMembers: (id, userIds) => request(`/workspaces/${encodeURIComponent(id)}/members/remove`, { body: { user_ids: [...userIds] }, method: "POST" }),
|
|
2501
2989
|
request,
|
|
2502
2990
|
requestRaw,
|
|
2991
|
+
streamImage: async function* (body, options = {}) {
|
|
2992
|
+
assertAllowedModel(body.model, allowedModels);
|
|
2993
|
+
const response = await requestRaw("/images", {
|
|
2994
|
+
...options,
|
|
2995
|
+
body: { ...body, stream: true },
|
|
2996
|
+
method: "POST"
|
|
2997
|
+
});
|
|
2998
|
+
yield* parseImageSSE(response);
|
|
2999
|
+
},
|
|
2503
3000
|
respond: (body) => {
|
|
2504
3001
|
assertAllowedModel(body.model, allowedModels);
|
|
2505
3002
|
return body.stream ? requestRaw("/responses", { body, method: "POST" }) : request("/responses", {
|
|
@@ -2531,6 +3028,51 @@ var createOpenRouterClient = (config2) => {
|
|
|
2531
3028
|
body,
|
|
2532
3029
|
method: "POST"
|
|
2533
3030
|
});
|
|
3031
|
+
},
|
|
3032
|
+
uploadFile: (file, options = {}) => {
|
|
3033
|
+
const body = new FormData;
|
|
3034
|
+
if (options.filename)
|
|
3035
|
+
body.append("file", file, options.filename);
|
|
3036
|
+
else
|
|
3037
|
+
body.append("file", file);
|
|
3038
|
+
return request("/files", {
|
|
3039
|
+
body,
|
|
3040
|
+
method: "POST",
|
|
3041
|
+
query: { workspace_id: options.workspaceId ?? defaultWorkspaceId }
|
|
3042
|
+
});
|
|
3043
|
+
},
|
|
3044
|
+
updateWorkspace: (id, body) => {
|
|
3045
|
+
assertAllowedModelsInValue(body, allowedModels);
|
|
3046
|
+
return request(`/workspaces/${encodeURIComponent(id)}`, { body, method: "PATCH" });
|
|
3047
|
+
},
|
|
3048
|
+
upsertWorkspaceBudget: (id, interval, limitUsd) => request(`/workspaces/${encodeURIComponent(id)}/budgets/${interval}`, { body: { limit_usd: limitUsd }, method: "PUT" }),
|
|
3049
|
+
waitForBatch: async (id, options = {}) => {
|
|
3050
|
+
const intervalMs = options.intervalMs ?? 1000;
|
|
3051
|
+
const timeoutMs = options.timeoutMs;
|
|
3052
|
+
if (!Number.isFinite(intervalMs) || intervalMs < 0)
|
|
3053
|
+
throw new Error("OpenRouter batch intervalMs must be non-negative");
|
|
3054
|
+
if (timeoutMs !== undefined && (!Number.isFinite(timeoutMs) || timeoutMs < 0))
|
|
3055
|
+
throw new Error("OpenRouter batch timeoutMs must be non-negative");
|
|
3056
|
+
const startedAt = Date.now();
|
|
3057
|
+
for (;; ) {
|
|
3058
|
+
options.signal?.throwIfAborted();
|
|
3059
|
+
const batch = await getBatch(id);
|
|
3060
|
+
if (TERMINAL_BATCH_STATUSES.has(batch.status))
|
|
3061
|
+
return batch;
|
|
3062
|
+
if (timeoutMs !== undefined && Date.now() - startedAt + intervalMs > timeoutMs)
|
|
3063
|
+
throw new Error(`Timed out waiting for OpenRouter batch "${id}"`);
|
|
3064
|
+
await new Promise((resolve, reject) => {
|
|
3065
|
+
const onAbort = () => {
|
|
3066
|
+
clearTimeout(timeout);
|
|
3067
|
+
reject(options.signal?.reason);
|
|
3068
|
+
};
|
|
3069
|
+
const timeout = setTimeout(() => {
|
|
3070
|
+
options.signal?.removeEventListener("abort", onAbort);
|
|
3071
|
+
resolve();
|
|
3072
|
+
}, intervalMs);
|
|
3073
|
+
options.signal?.addEventListener("abort", onAbort, { once: true });
|
|
3074
|
+
});
|
|
3075
|
+
}
|
|
2534
3076
|
}
|
|
2535
3077
|
};
|
|
2536
3078
|
};
|
|
@@ -2667,7 +3209,7 @@ var assertAllowedPreset = (preset, allowedPresets) => {
|
|
|
2667
3209
|
var assertIndirectModels = (value, allowedModels, key = "") => {
|
|
2668
3210
|
if (key === "model" && typeof value === "string")
|
|
2669
3211
|
assertAllowedModel2(value, allowedModels);
|
|
2670
|
-
if (key === "models" && Array.isArray(value)) {
|
|
3212
|
+
if ((key === "models" || key === "analysis_models" || key === "allowed_models") && Array.isArray(value)) {
|
|
2671
3213
|
for (const model of value) {
|
|
2672
3214
|
if (typeof model === "string")
|
|
2673
3215
|
assertAllowedModel2(model, allowedModels);
|
|
@@ -2698,6 +3240,7 @@ var assertRequestOptions = (options, allowedModels, allowedPresets, allowedProvi
|
|
|
2698
3240
|
assertAllowedModel2(model, allowedModels);
|
|
2699
3241
|
}
|
|
2700
3242
|
assertIndirectModels(options.serverTools, allowedModels);
|
|
3243
|
+
assertIndirectModels(options.messagesTools, allowedModels);
|
|
2701
3244
|
assertIndirectModels(options.plugins, allowedModels);
|
|
2702
3245
|
if (options.extraBody) {
|
|
2703
3246
|
const unsafe = Object.keys(options.extraBody).find((key) => SECURITY_SENSITIVE_EXTRA_BODY_FIELDS.has(key));
|
|
@@ -2716,17 +3259,41 @@ var snapshotPolicy = (config2) => ({
|
|
|
2716
3259
|
allowedModels: config2.allowedModels ? [...config2.allowedModels] : undefined,
|
|
2717
3260
|
allowedPresets: config2.allowedPresets ? [...config2.allowedPresets] : undefined
|
|
2718
3261
|
});
|
|
2719
|
-
var transformOpenRouterRequest = (config2, allowedModels, allowedPresets, body, params) => {
|
|
3262
|
+
var transformOpenRouterRequest = (config2, allowedModels, allowedPresets, body, params, skin = "openai") => {
|
|
2720
3263
|
const options = requestOptionsFor(params, config2.requestOptions);
|
|
2721
3264
|
assertRequestOptions(options, allowedModels, allowedPresets, config2.allowedProviders);
|
|
2722
3265
|
const transformed = { ...body, ...options.extraBody };
|
|
2723
|
-
if (
|
|
2724
|
-
transformed.
|
|
2725
|
-
|
|
2726
|
-
}
|
|
2727
|
-
|
|
2728
|
-
|
|
2729
|
-
|
|
3266
|
+
if (options.audioOutput) {
|
|
3267
|
+
transformed.audio = options.audioOutput;
|
|
3268
|
+
transformed.modalities = ["text", "audio"];
|
|
3269
|
+
}
|
|
3270
|
+
if (skin === "openai") {
|
|
3271
|
+
const requestedReasoning = {};
|
|
3272
|
+
if (params.reasoning?.budgetTokens !== undefined) {
|
|
3273
|
+
requestedReasoning.max_tokens = params.reasoning.budgetTokens;
|
|
3274
|
+
delete transformed.reasoning_effort;
|
|
3275
|
+
} else if (params.reasoning?.effort) {
|
|
3276
|
+
requestedReasoning.effort = params.reasoning.effort;
|
|
3277
|
+
delete transformed.reasoning_effort;
|
|
3278
|
+
}
|
|
3279
|
+
if (options.reasoning) {
|
|
3280
|
+
Object.assign(requestedReasoning, options.reasoning);
|
|
3281
|
+
if (options.reasoning.maxTokens !== undefined) {
|
|
3282
|
+
requestedReasoning.max_tokens = options.reasoning.maxTokens;
|
|
3283
|
+
delete requestedReasoning.maxTokens;
|
|
3284
|
+
}
|
|
3285
|
+
}
|
|
3286
|
+
if (Object.keys(requestedReasoning).length > 0)
|
|
3287
|
+
transformed.reasoning = requestedReasoning;
|
|
3288
|
+
}
|
|
3289
|
+
const automaticCacheControl = params.promptCaching === true || params.cacheSystemPrompt === true ? { type: "ephemeral" } : undefined;
|
|
3290
|
+
const cacheControl = options.cacheControl ?? automaticCacheControl;
|
|
3291
|
+
if (cacheControl)
|
|
3292
|
+
transformed.cache_control = cacheControl;
|
|
3293
|
+
if (options.promptCacheKey)
|
|
3294
|
+
transformed.prompt_cache_key = options.promptCacheKey;
|
|
3295
|
+
if (options.promptCacheOptions)
|
|
3296
|
+
transformed.prompt_cache_options = options.promptCacheOptions;
|
|
2730
3297
|
const routing = mapRouting({ ...config2.routing, ...options.routing }, config2.allowedProviders);
|
|
2731
3298
|
if (Object.keys(routing).length > 0)
|
|
2732
3299
|
transformed.provider = routing;
|
|
@@ -2746,12 +3313,22 @@ var transformOpenRouterRequest = (config2, allowedModels, allowedPresets, body,
|
|
|
2746
3313
|
...options.serverTools
|
|
2747
3314
|
];
|
|
2748
3315
|
}
|
|
3316
|
+
if (options.messagesTools) {
|
|
3317
|
+
if (skin !== "messages")
|
|
3318
|
+
throw new Error("OpenRouter messagesTools requires openrouterMessages()");
|
|
3319
|
+
transformed.tools = [
|
|
3320
|
+
...Array.isArray(transformed.tools) ? transformed.tools : [],
|
|
3321
|
+
...options.messagesTools
|
|
3322
|
+
];
|
|
3323
|
+
}
|
|
2749
3324
|
if (options.serviceTier)
|
|
2750
3325
|
transformed.service_tier = options.serviceTier;
|
|
2751
3326
|
if (options.sessionId)
|
|
2752
3327
|
transformed.session_id = options.sessionId;
|
|
2753
3328
|
if (options.stopServerToolsWhen)
|
|
2754
3329
|
transformed.stop_server_tools_when = options.stopServerToolsWhen;
|
|
3330
|
+
if (options.trace)
|
|
3331
|
+
transformed.trace = options.trace;
|
|
2755
3332
|
if (options.transforms)
|
|
2756
3333
|
transformed.transforms = [...options.transforms];
|
|
2757
3334
|
if (options.user)
|
|
@@ -2806,6 +3383,21 @@ var openrouterResponses = (config2) => {
|
|
|
2806
3383
|
});
|
|
2807
3384
|
return withOpenRouterPolicy(provider, allowedModels, allowedPresets);
|
|
2808
3385
|
};
|
|
3386
|
+
var openrouterMessages = (config2) => {
|
|
3387
|
+
assertRoutingPolicy(config2);
|
|
3388
|
+
const { allowedModels, allowedPresets } = snapshotPolicy(config2);
|
|
3389
|
+
const provider = anthropic({
|
|
3390
|
+
apiKey: config2.apiKey,
|
|
3391
|
+
authStyle: "bearer",
|
|
3392
|
+
baseUrl: config2.baseUrl ?? DEFAULT_BASE_URL7,
|
|
3393
|
+
fetch: config2.fetch,
|
|
3394
|
+
headers: (params) => resolveAttributionHeaders(config2, params),
|
|
3395
|
+
providerName: "openrouter",
|
|
3396
|
+
tokenSource: config2.tokenSource,
|
|
3397
|
+
transformRequestBody: (body, params) => transformOpenRouterRequest(config2, allowedModels, allowedPresets, body, params, "messages")
|
|
3398
|
+
});
|
|
3399
|
+
return withOpenRouterPolicy(provider, allowedModels, allowedPresets);
|
|
3400
|
+
};
|
|
2809
3401
|
|
|
2810
3402
|
// src/plugins/aiChat.ts
|
|
2811
3403
|
import { Elysia } from "elysia";
|
|
@@ -2982,6 +3574,8 @@ var isValidAIServerMessage = (data) => {
|
|
|
2982
3574
|
return "name" in data && "status" in data && "messageId" in data && "conversationId" in data;
|
|
2983
3575
|
case "image":
|
|
2984
3576
|
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;
|
|
3577
|
+
case "audio":
|
|
3578
|
+
return "data" in data && typeof data.data === "string" && "format" in data && typeof data.format === "string" && "messageId" in data && "conversationId" in data;
|
|
2985
3579
|
case "complete":
|
|
2986
3580
|
return "messageId" in data && "conversationId" in data;
|
|
2987
3581
|
case "turn_queued":
|
|
@@ -3081,6 +3675,15 @@ var sendImageMessage = async (socket, chunk, messageId, conversationId) => sendM
|
|
|
3081
3675
|
revisedPrompt: chunk.revisedPrompt,
|
|
3082
3676
|
type: "image"
|
|
3083
3677
|
});
|
|
3678
|
+
var sendAudioMessage = async (socket, chunk, messageId, conversationId) => sendMessage(socket, {
|
|
3679
|
+
audioId: chunk.audioId,
|
|
3680
|
+
conversationId,
|
|
3681
|
+
data: chunk.data,
|
|
3682
|
+
format: chunk.format,
|
|
3683
|
+
messageId,
|
|
3684
|
+
transcript: chunk.transcript,
|
|
3685
|
+
type: "audio"
|
|
3686
|
+
});
|
|
3084
3687
|
var sendToolRunning = async (socket, toolName, toolInput, messageId, conversationId) => sendMessage(socket, {
|
|
3085
3688
|
conversationId,
|
|
3086
3689
|
input: toolInput,
|
|
@@ -3174,6 +3777,15 @@ var processToolChunk = (chunk, state, options, socket, messageId, conversationId
|
|
|
3174
3777
|
revisedPrompt: chunk.revisedPrompt
|
|
3175
3778
|
});
|
|
3176
3779
|
break;
|
|
3780
|
+
case "audio":
|
|
3781
|
+
sendAudioMessage(socket, chunk, messageId, conversationId);
|
|
3782
|
+
options.onAudio?.({
|
|
3783
|
+
audioId: chunk.audioId,
|
|
3784
|
+
data: chunk.data,
|
|
3785
|
+
format: chunk.format,
|
|
3786
|
+
transcript: chunk.transcript
|
|
3787
|
+
});
|
|
3788
|
+
break;
|
|
3177
3789
|
case "tool_use":
|
|
3178
3790
|
flushThinking(state);
|
|
3179
3791
|
handleToolChunkToolUse(chunk, state);
|
|
@@ -3181,6 +3793,7 @@ var processToolChunk = (chunk, state, options, socket, messageId, conversationId
|
|
|
3181
3793
|
id: chunk.id,
|
|
3182
3794
|
input: typeof chunk.input === "object" && chunk.input !== null ? chunk.input : {},
|
|
3183
3795
|
name: chunk.name,
|
|
3796
|
+
providerData: chunk.providerData,
|
|
3184
3797
|
type: "tool_use"
|
|
3185
3798
|
});
|
|
3186
3799
|
hitAnotherTool = true;
|
|
@@ -3368,6 +3981,15 @@ var consumeStreamChunk = async (chunk, options, socket, state, messageId, conver
|
|
|
3368
3981
|
revisedPrompt: chunk.revisedPrompt
|
|
3369
3982
|
});
|
|
3370
3983
|
break;
|
|
3984
|
+
case "audio":
|
|
3985
|
+
await sendAudioMessage(socket, chunk, messageId, conversationId);
|
|
3986
|
+
options.onAudio?.({
|
|
3987
|
+
audioId: chunk.audioId,
|
|
3988
|
+
data: chunk.data,
|
|
3989
|
+
format: chunk.format,
|
|
3990
|
+
transcript: chunk.transcript
|
|
3991
|
+
});
|
|
3992
|
+
break;
|
|
3371
3993
|
case "tool_use":
|
|
3372
3994
|
flushStreamThinking(state);
|
|
3373
3995
|
state.pendingToolCalls.push({
|
|
@@ -3379,6 +4001,7 @@ var consumeStreamChunk = async (chunk, options, socket, state, messageId, conver
|
|
|
3379
4001
|
id: chunk.id,
|
|
3380
4002
|
input: typeof chunk.input === "object" && chunk.input !== null ? chunk.input : {},
|
|
3381
4003
|
name: chunk.name,
|
|
4004
|
+
providerData: chunk.providerData,
|
|
3382
4005
|
type: "tool_use"
|
|
3383
4006
|
});
|
|
3384
4007
|
break;
|
|
@@ -3479,6 +4102,10 @@ var serializeToolCall2 = (name, input) => `${name}:${JSON.stringify(input)}`;
|
|
|
3479
4102
|
var contentEvent = (options, renderers, delta, full) => options.structuredEvents ? { data: JSON.stringify({ delta, full }), event: "content" } : { data: renderers.chunk(delta, full), event: "content" };
|
|
3480
4103
|
var thinkingEvent = (options, renderers, text) => options.structuredEvents ? { data: JSON.stringify({ text }), event: "thinking" } : { data: renderers.thinking(text), event: "thinking" };
|
|
3481
4104
|
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" };
|
|
4105
|
+
var audioEvent = (data, format, transcript, audioId) => ({
|
|
4106
|
+
data: JSON.stringify({ audioId, data, format, transcript }),
|
|
4107
|
+
event: "audio"
|
|
4108
|
+
});
|
|
3482
4109
|
var completeEvent = (options, renderers, fullResponse, usage, durationMs) => {
|
|
3483
4110
|
options.onComplete?.(fullResponse, usage);
|
|
3484
4111
|
return options.structuredEvents ? {
|
|
@@ -3548,6 +4175,15 @@ var processImageChunk = function* (chunk, renderers, options) {
|
|
|
3548
4175
|
revisedPrompt: chunk.revisedPrompt
|
|
3549
4176
|
});
|
|
3550
4177
|
};
|
|
4178
|
+
var processAudioChunk = function* (chunk, options) {
|
|
4179
|
+
yield audioEvent(chunk.data, chunk.format, chunk.transcript, chunk.audioId);
|
|
4180
|
+
options.onAudio?.({
|
|
4181
|
+
audioId: chunk.audioId,
|
|
4182
|
+
data: chunk.data,
|
|
4183
|
+
format: chunk.format,
|
|
4184
|
+
transcript: chunk.transcript
|
|
4185
|
+
});
|
|
4186
|
+
};
|
|
3551
4187
|
var processToolUseChunk = (chunk, chunkState) => {
|
|
3552
4188
|
maybeFlushThinking(chunkState);
|
|
3553
4189
|
chunkState.pendingToolCalls.push({
|
|
@@ -3559,6 +4195,7 @@ var processToolUseChunk = (chunk, chunkState) => {
|
|
|
3559
4195
|
id: chunk.id,
|
|
3560
4196
|
input: typeof chunk.input === "object" && chunk.input !== null ? chunk.input : {},
|
|
3561
4197
|
name: chunk.name,
|
|
4198
|
+
providerData: chunk.providerData,
|
|
3562
4199
|
type: "tool_use"
|
|
3563
4200
|
});
|
|
3564
4201
|
};
|
|
@@ -3573,6 +4210,9 @@ var processChunk2 = function* (chunk, chunkState, renderers, options, fullRespon
|
|
|
3573
4210
|
case "image":
|
|
3574
4211
|
yield* processImageChunk(chunk, renderers, options);
|
|
3575
4212
|
break;
|
|
4213
|
+
case "audio":
|
|
4214
|
+
yield* processAudioChunk(chunk, options);
|
|
4215
|
+
break;
|
|
3576
4216
|
case "tool_use":
|
|
3577
4217
|
processToolUseChunk(chunk, chunkState);
|
|
3578
4218
|
break;
|
|
@@ -4456,6 +5096,14 @@ var streamAIWithTools = async function* (options) {
|
|
|
4456
5096
|
fullText += chunk.content;
|
|
4457
5097
|
pushText(blocks, chunk.content);
|
|
4458
5098
|
yield { content: chunk.content, type: "text" };
|
|
5099
|
+
} else if (chunk.type === "audio") {
|
|
5100
|
+
yield {
|
|
5101
|
+
audioId: chunk.audioId,
|
|
5102
|
+
data: chunk.data,
|
|
5103
|
+
format: chunk.format,
|
|
5104
|
+
transcript: chunk.transcript,
|
|
5105
|
+
type: "audio"
|
|
5106
|
+
};
|
|
4459
5107
|
} else if (chunk.type === "tool_use") {
|
|
4460
5108
|
thinking = flushThinking3(blocks, thinking);
|
|
4461
5109
|
pending.push({ id: chunk.id, input: chunk.input, name: chunk.name });
|
|
@@ -4463,8 +5111,16 @@ var streamAIWithTools = async function* (options) {
|
|
|
4463
5111
|
id: chunk.id,
|
|
4464
5112
|
input: chunk.input && typeof chunk.input === "object" ? chunk.input : {},
|
|
4465
5113
|
name: chunk.name,
|
|
5114
|
+
providerData: chunk.providerData,
|
|
4466
5115
|
type: "tool_use"
|
|
4467
5116
|
});
|
|
5117
|
+
} else if (chunk.type === "provider_event") {
|
|
5118
|
+
thinking = flushThinking3(blocks, thinking);
|
|
5119
|
+
blocks.push({
|
|
5120
|
+
data: chunk.data,
|
|
5121
|
+
provider: chunk.provider,
|
|
5122
|
+
type: "provider_data"
|
|
5123
|
+
});
|
|
4468
5124
|
} else if (chunk.type === "done") {
|
|
4469
5125
|
thinking = flushThinking3(blocks, thinking);
|
|
4470
5126
|
turnUsage = chunk.usage;
|
|
@@ -6009,6 +6665,16 @@ var serverMessageToAction = (message) => {
|
|
|
6009
6665
|
revisedPrompt: message.revisedPrompt,
|
|
6010
6666
|
type: "image"
|
|
6011
6667
|
};
|
|
6668
|
+
case "audio":
|
|
6669
|
+
return {
|
|
6670
|
+
audioId: message.audioId,
|
|
6671
|
+
conversationId: message.conversationId,
|
|
6672
|
+
data: message.data,
|
|
6673
|
+
format: message.format,
|
|
6674
|
+
messageId: message.messageId,
|
|
6675
|
+
transcript: message.transcript,
|
|
6676
|
+
type: "audio"
|
|
6677
|
+
};
|
|
6012
6678
|
case "complete":
|
|
6013
6679
|
return {
|
|
6014
6680
|
conversationId: message.conversationId,
|
|
@@ -6411,6 +7077,20 @@ var handleImage = (state, action) => {
|
|
|
6411
7077
|
});
|
|
6412
7078
|
conversation.messages = [...conversation.messages];
|
|
6413
7079
|
};
|
|
7080
|
+
var handleAudio = (state, action) => {
|
|
7081
|
+
const conversation = getOrCreate(state, action.conversationId);
|
|
7082
|
+
const message = getOrCreateAssistantMessage(conversation, action.messageId, action.conversationId);
|
|
7083
|
+
message.audio = [
|
|
7084
|
+
...message.audio ?? [],
|
|
7085
|
+
{
|
|
7086
|
+
audioId: action.audioId,
|
|
7087
|
+
data: action.data,
|
|
7088
|
+
format: action.format,
|
|
7089
|
+
transcript: action.transcript
|
|
7090
|
+
}
|
|
7091
|
+
];
|
|
7092
|
+
conversation.messages = [...conversation.messages];
|
|
7093
|
+
};
|
|
6414
7094
|
var handleComplete = (state, action) => {
|
|
6415
7095
|
const conversation = state.conversations.get(action.conversationId);
|
|
6416
7096
|
if (conversation) {
|
|
@@ -6481,6 +7161,9 @@ var applyAction = (state, action) => {
|
|
|
6481
7161
|
case "image":
|
|
6482
7162
|
handleImage(state, action);
|
|
6483
7163
|
break;
|
|
7164
|
+
case "audio":
|
|
7165
|
+
handleAudio(state, action);
|
|
7166
|
+
break;
|
|
6484
7167
|
case "complete":
|
|
6485
7168
|
handleComplete(state, action);
|
|
6486
7169
|
break;
|
|
@@ -6800,6 +7483,7 @@ var startProviderStatusMonitor = (options) => {
|
|
|
6800
7483
|
export {
|
|
6801
7484
|
xai,
|
|
6802
7485
|
withResilience,
|
|
7486
|
+
verifyOpenRouterWebhookSignature,
|
|
6803
7487
|
tableCard,
|
|
6804
7488
|
streamAIWithTools,
|
|
6805
7489
|
streamAIToSSE,
|
|
@@ -6827,6 +7511,7 @@ export {
|
|
|
6827
7511
|
parseChartSpec,
|
|
6828
7512
|
parseAIMessage,
|
|
6829
7513
|
openrouterResponses,
|
|
7514
|
+
openrouterMessages,
|
|
6830
7515
|
openrouter,
|
|
6831
7516
|
openaiResponses,
|
|
6832
7517
|
openaiCompatible,
|
|
@@ -6838,6 +7523,7 @@ export {
|
|
|
6838
7523
|
meta,
|
|
6839
7524
|
google,
|
|
6840
7525
|
getProviderHealth,
|
|
7526
|
+
generateOpenRouterPKCE,
|
|
6841
7527
|
generateObjectAI,
|
|
6842
7528
|
generateId,
|
|
6843
7529
|
generateAIWithTools,
|
|
@@ -6845,13 +7531,18 @@ export {
|
|
|
6845
7531
|
gemini,
|
|
6846
7532
|
formCard,
|
|
6847
7533
|
fetchProviderApiStatus,
|
|
7534
|
+
exchangeOpenRouterAuthCode,
|
|
7535
|
+
estimateOpenRouterModelCost,
|
|
7536
|
+
estimateOpenRouterCost,
|
|
6848
7537
|
diffCard,
|
|
6849
7538
|
deepseek,
|
|
6850
7539
|
credentialCard,
|
|
6851
7540
|
createUiCards,
|
|
6852
7541
|
createSyncConversationStore,
|
|
6853
7542
|
createProviderProxyResponse,
|
|
7543
|
+
createOpenRouterKeyLinks,
|
|
6854
7544
|
createOpenRouterClient,
|
|
7545
|
+
createOpenRouterAuthorizationUrl,
|
|
6855
7546
|
createOAuth2ClientCredentialsTokenSource,
|
|
6856
7547
|
createMemoryStore,
|
|
6857
7548
|
createConversationManager,
|
|
@@ -6889,5 +7580,5 @@ export {
|
|
|
6889
7580
|
BUILTIN_UI_CARDS
|
|
6890
7581
|
};
|
|
6891
7582
|
|
|
6892
|
-
//# debugId=
|
|
7583
|
+
//# debugId=25187F8D2370EC5064756E2164756E21
|
|
6893
7584
|
//# sourceMappingURL=index.js.map
|