@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
|
@@ -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;
|
|
@@ -923,7 +969,12 @@ var mapContentToResponsesFormat = (content) => {
|
|
|
923
969
|
};
|
|
924
970
|
var hasToolBlocks = (content) => content.some((block) => block.type === "tool_use" || block.type === "tool_result");
|
|
925
971
|
var convertToolBlock = (block) => {
|
|
972
|
+
if (block.type === "provider_data" && block.provider === "openrouter") {
|
|
973
|
+
return { ...block.data };
|
|
974
|
+
}
|
|
926
975
|
if (block.type === "tool_use") {
|
|
976
|
+
if (block.providerData)
|
|
977
|
+
return { ...block.providerData };
|
|
927
978
|
return {
|
|
928
979
|
arguments: typeof block.input === "string" ? block.input : JSON.stringify(block.input),
|
|
929
980
|
call_id: block.id,
|
|
@@ -1059,11 +1110,32 @@ var extractUsage2 = (response) => {
|
|
|
1059
1110
|
const { usage } = response;
|
|
1060
1111
|
const input = typeof usage.input_tokens === "number" ? usage.input_tokens : 0;
|
|
1061
1112
|
const cached = isRecord2(usage.input_tokens_details) && typeof usage.input_tokens_details.cached_tokens === "number" ? usage.input_tokens_details.cached_tokens : 0;
|
|
1062
|
-
|
|
1113
|
+
const outputDetails = isRecord2(usage.output_tokens_details) ? usage.output_tokens_details : undefined;
|
|
1114
|
+
const inputDetails = isRecord2(usage.input_tokens_details) ? usage.input_tokens_details : undefined;
|
|
1115
|
+
const costDetails = isRecord2(usage.cost_details) ? usage.cost_details : undefined;
|
|
1116
|
+
const normalized = {
|
|
1063
1117
|
cacheReadInputTokens: cached,
|
|
1118
|
+
cacheWriteInputTokens: inputDetails && typeof inputDetails.cache_write_tokens === "number" ? inputDetails.cache_write_tokens : undefined,
|
|
1119
|
+
costCredits: typeof usage.cost === "number" ? usage.cost : undefined,
|
|
1064
1120
|
inputTokens: Math.max(0, input - cached),
|
|
1065
|
-
outputTokens: typeof usage.output_tokens === "number" ? usage.output_tokens : 0
|
|
1121
|
+
outputTokens: typeof usage.output_tokens === "number" ? usage.output_tokens : 0,
|
|
1122
|
+
reasoningTokens: outputDetails && typeof outputDetails.reasoning_tokens === "number" ? outputDetails.reasoning_tokens : undefined,
|
|
1123
|
+
upstreamInferenceCostCredits: costDetails && typeof costDetails.upstream_inference_cost === "number" ? costDetails.upstream_inference_cost : undefined
|
|
1066
1124
|
};
|
|
1125
|
+
if (isRecord2(usage.server_tool_use)) {
|
|
1126
|
+
normalized.serverToolUse = Object.fromEntries(Object.entries(usage.server_tool_use).filter((entry) => typeof entry[1] === "number"));
|
|
1127
|
+
}
|
|
1128
|
+
return normalized;
|
|
1129
|
+
};
|
|
1130
|
+
var extractResponseMetadata = (response) => {
|
|
1131
|
+
const providerMetadata = isRecord2(response.openrouter_metadata) ? response.openrouter_metadata : undefined;
|
|
1132
|
+
const generationId = typeof response.id === "string" ? response.id : undefined;
|
|
1133
|
+
const model = typeof response.model === "string" ? response.model : undefined;
|
|
1134
|
+
const provider = typeof response.provider === "string" ? response.provider : undefined;
|
|
1135
|
+
const serviceTier = typeof response.service_tier === "string" ? response.service_tier : undefined;
|
|
1136
|
+
if (!providerMetadata && !generationId && !model && !provider && !serviceTier)
|
|
1137
|
+
return;
|
|
1138
|
+
return { generationId, model, provider, providerMetadata, serviceTier };
|
|
1067
1139
|
};
|
|
1068
1140
|
var extractMimeFormat = (mimeType) => {
|
|
1069
1141
|
if (typeof mimeType !== "string") {
|
|
@@ -1120,6 +1192,7 @@ var processFunctionCallArgumentsDone = function* (parsed, pendingCalls) {
|
|
|
1120
1192
|
id: callId || pending?.callId || itemId,
|
|
1121
1193
|
input: parseToolInput2(args),
|
|
1122
1194
|
name,
|
|
1195
|
+
providerData: pending?.providerData ? { ...pending.providerData, arguments: args } : undefined,
|
|
1123
1196
|
type: "tool_use"
|
|
1124
1197
|
};
|
|
1125
1198
|
};
|
|
@@ -1138,9 +1211,21 @@ var processOutputItemAdded = (parsed, pendingCalls) => {
|
|
|
1138
1211
|
pendingCalls.set(itemId, {
|
|
1139
1212
|
arguments: "",
|
|
1140
1213
|
callId,
|
|
1141
|
-
name
|
|
1214
|
+
name,
|
|
1215
|
+
providerData: { ...item }
|
|
1142
1216
|
});
|
|
1143
1217
|
};
|
|
1218
|
+
var processOutputItemDone = function* (parsed) {
|
|
1219
|
+
if (!isRecord2(parsed.item) || typeof parsed.item.type !== "string")
|
|
1220
|
+
return;
|
|
1221
|
+
if (!parsed.item.type.startsWith("openrouter:"))
|
|
1222
|
+
return;
|
|
1223
|
+
yield {
|
|
1224
|
+
data: { ...parsed.item },
|
|
1225
|
+
provider: "openrouter",
|
|
1226
|
+
type: "provider_event"
|
|
1227
|
+
};
|
|
1228
|
+
};
|
|
1144
1229
|
var isCompletedImageGeneration = (item) => item.type === "image_generation_call" && item.status === "completed" && typeof item.result === "string" && item.result !== "";
|
|
1145
1230
|
var buildImageChunk = (item) => ({
|
|
1146
1231
|
data: typeof item.result === "string" ? item.result : "",
|
|
@@ -1156,6 +1241,30 @@ var extractImageFromOutput = function* (output) {
|
|
|
1156
1241
|
yield buildImageChunk(item);
|
|
1157
1242
|
}
|
|
1158
1243
|
};
|
|
1244
|
+
var extractCitationsFromOutput = function* (output) {
|
|
1245
|
+
for (const item of output) {
|
|
1246
|
+
if (!isRecordArray2(item.content))
|
|
1247
|
+
continue;
|
|
1248
|
+
for (const content of item.content) {
|
|
1249
|
+
if (!Array.isArray(content.annotations))
|
|
1250
|
+
continue;
|
|
1251
|
+
for (const annotation of content.annotations) {
|
|
1252
|
+
if (!isRecord2(annotation) || annotation.type !== "url_citation")
|
|
1253
|
+
continue;
|
|
1254
|
+
if (typeof annotation.url !== "string")
|
|
1255
|
+
continue;
|
|
1256
|
+
yield {
|
|
1257
|
+
content: typeof annotation.content === "string" ? annotation.content : undefined,
|
|
1258
|
+
endIndex: typeof annotation.end_index === "number" ? annotation.end_index : undefined,
|
|
1259
|
+
startIndex: typeof annotation.start_index === "number" ? annotation.start_index : undefined,
|
|
1260
|
+
title: typeof annotation.title === "string" ? annotation.title : undefined,
|
|
1261
|
+
type: "citation",
|
|
1262
|
+
url: annotation.url
|
|
1263
|
+
};
|
|
1264
|
+
}
|
|
1265
|
+
}
|
|
1266
|
+
}
|
|
1267
|
+
};
|
|
1159
1268
|
var processCompleted = function* (parsed) {
|
|
1160
1269
|
if (!isRecord2(parsed.response)) {
|
|
1161
1270
|
yield { type: "done", usage: undefined };
|
|
@@ -1163,12 +1272,26 @@ var processCompleted = function* (parsed) {
|
|
|
1163
1272
|
}
|
|
1164
1273
|
const { response } = parsed;
|
|
1165
1274
|
const usage = extractUsage2(response);
|
|
1275
|
+
const metadata = extractResponseMetadata(response);
|
|
1166
1276
|
if (isRecordArray2(response.output)) {
|
|
1277
|
+
yield* extractCitationsFromOutput(response.output);
|
|
1167
1278
|
yield* extractImageFromOutput(response.output);
|
|
1168
1279
|
}
|
|
1169
|
-
yield { type: "done", usage };
|
|
1280
|
+
yield { metadata, type: "done", usage };
|
|
1281
|
+
};
|
|
1282
|
+
var responseFailure = (eventType, parsed, providerName) => {
|
|
1283
|
+
const response = isRecord2(parsed.response) ? parsed.response : parsed;
|
|
1284
|
+
const error = isRecord2(response.error) ? response.error : undefined;
|
|
1285
|
+
const type = typeof response.error_type === "string" ? response.error_type : error && typeof error.code === "string" ? error.code : eventType;
|
|
1286
|
+
return new ProviderError({
|
|
1287
|
+
message: error && typeof error.message === "string" ? error.message : `OpenRouter Responses API: ${eventType}`,
|
|
1288
|
+
metadata: response,
|
|
1289
|
+
provider: providerName,
|
|
1290
|
+
retryable: type === "rate_limit_exceeded" || type === "provider_overloaded" || type === "provider_unavailable" || type === "server",
|
|
1291
|
+
type
|
|
1292
|
+
});
|
|
1170
1293
|
};
|
|
1171
|
-
var processSSEEvent = function* (eventType, parsed, pendingCalls) {
|
|
1294
|
+
var processSSEEvent = function* (eventType, parsed, pendingCalls, providerName) {
|
|
1172
1295
|
switch (eventType) {
|
|
1173
1296
|
case "response.reasoning_summary_text.delta": {
|
|
1174
1297
|
const delta = typeof parsed.delta === "string" ? parsed.delta : "";
|
|
@@ -1189,6 +1312,9 @@ var processSSEEvent = function* (eventType, parsed, pendingCalls) {
|
|
|
1189
1312
|
case "response.output_item.added":
|
|
1190
1313
|
processOutputItemAdded(parsed, pendingCalls);
|
|
1191
1314
|
break;
|
|
1315
|
+
case "response.output_item.done":
|
|
1316
|
+
yield* processOutputItemDone(parsed);
|
|
1317
|
+
break;
|
|
1192
1318
|
case "response.function_call_arguments.delta":
|
|
1193
1319
|
processFunctionCallArgumentsDelta(parsed, pendingCalls);
|
|
1194
1320
|
break;
|
|
@@ -1199,11 +1325,10 @@ var processSSEEvent = function* (eventType, parsed, pendingCalls) {
|
|
|
1199
1325
|
yield* processCompleted(parsed);
|
|
1200
1326
|
break;
|
|
1201
1327
|
case "response.failed":
|
|
1202
|
-
case "response.incomplete":
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
throw
|
|
1206
|
-
}
|
|
1328
|
+
case "response.incomplete":
|
|
1329
|
+
case "response.error":
|
|
1330
|
+
case "error":
|
|
1331
|
+
throw responseFailure(eventType, parsed, providerName);
|
|
1207
1332
|
}
|
|
1208
1333
|
};
|
|
1209
1334
|
var flushSSEBuffer = function* (state) {
|
|
@@ -1212,7 +1337,7 @@ var flushSSEBuffer = function* (state) {
|
|
|
1212
1337
|
}
|
|
1213
1338
|
const parsed = parseJSON(state.buffer);
|
|
1214
1339
|
if (parsed) {
|
|
1215
|
-
yield* processSSEEvent(state.currentEvent, parsed, state.pendingCalls);
|
|
1340
|
+
yield* processSSEEvent(state.currentEvent, parsed, state.pendingCalls, state.providerName);
|
|
1216
1341
|
}
|
|
1217
1342
|
state.currentEvent = "";
|
|
1218
1343
|
state.buffer = "";
|
|
@@ -1250,17 +1375,19 @@ var drainReader2 = async function* (reader, decoder, state, signal) {
|
|
|
1250
1375
|
yield* processSSELines2([textBuffer, ""], state);
|
|
1251
1376
|
}
|
|
1252
1377
|
};
|
|
1253
|
-
var parseSSEStream2 = async function* (body, signal) {
|
|
1378
|
+
var parseSSEStream2 = async function* (body, providerName, signal) {
|
|
1254
1379
|
const reader = body.getReader();
|
|
1255
1380
|
const decoder = new TextDecoder;
|
|
1256
1381
|
const state = {
|
|
1257
1382
|
buffer: "",
|
|
1258
1383
|
currentEvent: "",
|
|
1259
1384
|
pendingCalls: new Map,
|
|
1260
|
-
usage: undefined
|
|
1385
|
+
usage: undefined,
|
|
1386
|
+
providerName
|
|
1261
1387
|
};
|
|
1262
1388
|
try {
|
|
1263
1389
|
yield* drainReader2(reader, decoder, state, signal);
|
|
1390
|
+
yield* flushSSEBuffer(state);
|
|
1264
1391
|
} finally {
|
|
1265
1392
|
reader.releaseLock();
|
|
1266
1393
|
}
|
|
@@ -1288,7 +1415,7 @@ var fetchResponsesStream = async function* (baseUrl, apiKey, body, fetchImpl, he
|
|
|
1288
1415
|
retryable: true
|
|
1289
1416
|
});
|
|
1290
1417
|
}
|
|
1291
|
-
yield* parseSSEStream2(response.body, signal);
|
|
1418
|
+
yield* parseSSEStream2(response.body, providerName, signal);
|
|
1292
1419
|
};
|
|
1293
1420
|
var resolveImageModels = (imageModels) => {
|
|
1294
1421
|
if (!imageModels) {
|
|
@@ -1324,8 +1451,585 @@ var openaiResponses = (config2) => {
|
|
|
1324
1451
|
}, providerName);
|
|
1325
1452
|
};
|
|
1326
1453
|
|
|
1454
|
+
// src/ai/providers/anthropic.ts
|
|
1455
|
+
var h2IfHttps3 = (url) => url.startsWith("https://") ? { protocol: "http2" } : {};
|
|
1456
|
+
var DEFAULT_BASE_URL3 = "https://api.anthropic.com";
|
|
1457
|
+
var API_VERSION = "2023-06-01";
|
|
1458
|
+
var DEFAULT_MAX_TOKENS = 32000;
|
|
1459
|
+
var EVENT_PREFIX_LENGTH2 = 7;
|
|
1460
|
+
var DATA_PREFIX_LENGTH2 = 6;
|
|
1461
|
+
var EMPTY_CHUNKS = [];
|
|
1462
|
+
var isRecord3 = (val) => typeof val === "object" && val !== null;
|
|
1463
|
+
var mapContentBlock = (block) => {
|
|
1464
|
+
if (block.type === "thinking") {
|
|
1465
|
+
return {
|
|
1466
|
+
signature: block.signature,
|
|
1467
|
+
thinking: block.thinking,
|
|
1468
|
+
type: "thinking"
|
|
1469
|
+
};
|
|
1470
|
+
}
|
|
1471
|
+
if (block.type === "image") {
|
|
1472
|
+
return {
|
|
1473
|
+
source: block.source,
|
|
1474
|
+
type: "image"
|
|
1475
|
+
};
|
|
1476
|
+
}
|
|
1477
|
+
if (block.type === "document") {
|
|
1478
|
+
return {
|
|
1479
|
+
source: block.source,
|
|
1480
|
+
type: "document"
|
|
1481
|
+
};
|
|
1482
|
+
}
|
|
1483
|
+
if (block.type === "tool_result") {
|
|
1484
|
+
return {
|
|
1485
|
+
content: block.content,
|
|
1486
|
+
tool_use_id: block.tool_use_id,
|
|
1487
|
+
type: "tool_result"
|
|
1488
|
+
};
|
|
1489
|
+
}
|
|
1490
|
+
if (block.type === "tool_use") {
|
|
1491
|
+
if (block.providerData)
|
|
1492
|
+
return { ...block.providerData };
|
|
1493
|
+
return {
|
|
1494
|
+
id: block.id,
|
|
1495
|
+
input: block.input,
|
|
1496
|
+
name: block.name,
|
|
1497
|
+
type: "tool_use"
|
|
1498
|
+
};
|
|
1499
|
+
}
|
|
1500
|
+
if (block.type === "audio" || block.type === "video") {
|
|
1501
|
+
throw new Error(`Anthropic does not support ${block.type} content blocks`);
|
|
1502
|
+
}
|
|
1503
|
+
if (block.type === "provider_data") {
|
|
1504
|
+
return { ...block.data };
|
|
1505
|
+
}
|
|
1506
|
+
return { text: block.content, type: "text" };
|
|
1507
|
+
};
|
|
1508
|
+
var mapMessage = (msg) => ({
|
|
1509
|
+
content: typeof msg.content === "string" ? msg.content : msg.content.map(mapContentBlock),
|
|
1510
|
+
role: msg.role === "system" ? "user" : msg.role
|
|
1511
|
+
});
|
|
1512
|
+
var mapToolDefinition2 = (tool) => ({
|
|
1513
|
+
description: tool.description,
|
|
1514
|
+
input_schema: tool.input_schema,
|
|
1515
|
+
name: tool.name
|
|
1516
|
+
});
|
|
1517
|
+
var cacheLastContentBlock = (msg) => {
|
|
1518
|
+
const cacheControl = { type: "ephemeral" };
|
|
1519
|
+
if (typeof msg.content === "string") {
|
|
1520
|
+
return {
|
|
1521
|
+
content: [
|
|
1522
|
+
{ cache_control: cacheControl, text: msg.content, type: "text" }
|
|
1523
|
+
],
|
|
1524
|
+
role: msg.role
|
|
1525
|
+
};
|
|
1526
|
+
}
|
|
1527
|
+
if (msg.content.length === 0)
|
|
1528
|
+
return msg;
|
|
1529
|
+
const blocks = [...msg.content];
|
|
1530
|
+
blocks[blocks.length - 1] = {
|
|
1531
|
+
...blocks[blocks.length - 1],
|
|
1532
|
+
cache_control: cacheControl
|
|
1533
|
+
};
|
|
1534
|
+
return { content: blocks, role: msg.role };
|
|
1535
|
+
};
|
|
1536
|
+
var buildRequestBody3 = (params, configuredMax, configCaching) => {
|
|
1537
|
+
const caching = params.promptCaching ?? configCaching;
|
|
1538
|
+
const cacheSystem = params.cacheSystemPrompt ?? caching;
|
|
1539
|
+
const messages = params.messages.filter((msg) => msg.role !== "system").map(mapMessage);
|
|
1540
|
+
if (caching && messages.length > 1) {
|
|
1541
|
+
const last = messages[messages.length - 1];
|
|
1542
|
+
if (last) {
|
|
1543
|
+
messages[messages.length - 1] = cacheLastContentBlock(last);
|
|
1544
|
+
}
|
|
1545
|
+
}
|
|
1546
|
+
const max = typeof params.maxTokens === "number" ? params.maxTokens : configuredMax;
|
|
1547
|
+
const body = {
|
|
1548
|
+
max_tokens: max,
|
|
1549
|
+
messages,
|
|
1550
|
+
model: params.model,
|
|
1551
|
+
stream: true
|
|
1552
|
+
};
|
|
1553
|
+
if (params.systemPrompt) {
|
|
1554
|
+
body.system = cacheSystem ? [
|
|
1555
|
+
{
|
|
1556
|
+
cache_control: { type: "ephemeral" },
|
|
1557
|
+
text: params.systemPrompt,
|
|
1558
|
+
type: "text"
|
|
1559
|
+
}
|
|
1560
|
+
] : params.systemPrompt;
|
|
1561
|
+
}
|
|
1562
|
+
if (params.tools && params.tools.length > 0) {
|
|
1563
|
+
const tools = params.tools.map(mapToolDefinition2);
|
|
1564
|
+
if (caching) {
|
|
1565
|
+
tools[tools.length - 1] = {
|
|
1566
|
+
...tools[tools.length - 1],
|
|
1567
|
+
cache_control: { type: "ephemeral" }
|
|
1568
|
+
};
|
|
1569
|
+
}
|
|
1570
|
+
body.tools = tools;
|
|
1571
|
+
if (params.toolChoice === "auto" || params.toolChoice === "none") {
|
|
1572
|
+
body.tool_choice = { type: params.toolChoice };
|
|
1573
|
+
} else if (params.toolChoice === "required") {
|
|
1574
|
+
body.tool_choice = { type: "any" };
|
|
1575
|
+
} else if (params.toolChoice && typeof params.toolChoice === "object") {
|
|
1576
|
+
body.tool_choice = { name: params.toolChoice.name, type: "tool" };
|
|
1577
|
+
}
|
|
1578
|
+
}
|
|
1579
|
+
if (params.stopSequences && params.stopSequences.length > 0) {
|
|
1580
|
+
body.stop_sequences = params.stopSequences;
|
|
1581
|
+
}
|
|
1582
|
+
const mode = params.reasoning ? anthropicReasoningMode(params.model) : "none";
|
|
1583
|
+
const thinkingActive = mode !== "none";
|
|
1584
|
+
if (!thinkingActive && anthropicSupportsSampling(params.model)) {
|
|
1585
|
+
if (typeof params.temperature === "number") {
|
|
1586
|
+
body.temperature = params.temperature;
|
|
1587
|
+
}
|
|
1588
|
+
if (typeof params.topP === "number")
|
|
1589
|
+
body.top_p = params.topP;
|
|
1590
|
+
}
|
|
1591
|
+
if (mode === "effort" || mode === "adaptive") {
|
|
1592
|
+
body.thinking = { type: "adaptive" };
|
|
1593
|
+
if (mode === "effort" && params.reasoning) {
|
|
1594
|
+
const effort = anthropicEffortValue(params.model, params.reasoning);
|
|
1595
|
+
if (effort)
|
|
1596
|
+
body.output_config = { effort };
|
|
1597
|
+
}
|
|
1598
|
+
} else if (mode === "legacy" && params.reasoning) {
|
|
1599
|
+
const budget = resolveBudgetTokens(params.reasoning);
|
|
1600
|
+
if (budget) {
|
|
1601
|
+
body.thinking = { budget_tokens: budget, type: "enabled" };
|
|
1602
|
+
body.max_tokens = Math.max(max, budget + max);
|
|
1603
|
+
}
|
|
1604
|
+
}
|
|
1605
|
+
return body;
|
|
1606
|
+
};
|
|
1607
|
+
var classifyLine = (line) => {
|
|
1608
|
+
if (line.startsWith("event: ")) {
|
|
1609
|
+
return {
|
|
1610
|
+
field: "event",
|
|
1611
|
+
value: line.slice(EVENT_PREFIX_LENGTH2)
|
|
1612
|
+
};
|
|
1613
|
+
}
|
|
1614
|
+
if (line.startsWith("data: ")) {
|
|
1615
|
+
return {
|
|
1616
|
+
field: "data",
|
|
1617
|
+
value: line.slice(DATA_PREFIX_LENGTH2)
|
|
1618
|
+
};
|
|
1619
|
+
}
|
|
1620
|
+
return;
|
|
1621
|
+
};
|
|
1622
|
+
var applyClassified = (acc, classified) => {
|
|
1623
|
+
if (!classified) {
|
|
1624
|
+
return acc;
|
|
1625
|
+
}
|
|
1626
|
+
if (classified.field === "event") {
|
|
1627
|
+
return { eventData: acc.eventData, eventType: classified.value };
|
|
1628
|
+
}
|
|
1629
|
+
return { eventData: classified.value, eventType: acc.eventType };
|
|
1630
|
+
};
|
|
1631
|
+
var parseEventLines = (event) => event.split(`
|
|
1632
|
+
`).reduce((acc, line) => applyClassified(acc, classifyLine(line)), {
|
|
1633
|
+
eventData: "",
|
|
1634
|
+
eventType: ""
|
|
1635
|
+
});
|
|
1636
|
+
var safeParse = (text) => {
|
|
1637
|
+
try {
|
|
1638
|
+
const result = JSON.parse(text);
|
|
1639
|
+
return result;
|
|
1640
|
+
} catch {
|
|
1641
|
+
return;
|
|
1642
|
+
}
|
|
1643
|
+
};
|
|
1644
|
+
var tryParseJson = (text) => {
|
|
1645
|
+
const result = safeParse(text);
|
|
1646
|
+
if (isRecord3(result)) {
|
|
1647
|
+
return result;
|
|
1648
|
+
}
|
|
1649
|
+
return;
|
|
1650
|
+
};
|
|
1651
|
+
var getRecord = (obj, key) => {
|
|
1652
|
+
const val = obj[key];
|
|
1653
|
+
if (isRecord3(val)) {
|
|
1654
|
+
return val;
|
|
1655
|
+
}
|
|
1656
|
+
return;
|
|
1657
|
+
};
|
|
1658
|
+
var getString = (obj, key) => {
|
|
1659
|
+
const val = obj[key];
|
|
1660
|
+
if (typeof val === "string") {
|
|
1661
|
+
return val;
|
|
1662
|
+
}
|
|
1663
|
+
return "";
|
|
1664
|
+
};
|
|
1665
|
+
var getNumber = (obj, key) => {
|
|
1666
|
+
const val = obj[key];
|
|
1667
|
+
if (typeof val === "number") {
|
|
1668
|
+
return val;
|
|
1669
|
+
}
|
|
1670
|
+
return 0;
|
|
1671
|
+
};
|
|
1672
|
+
var handleContentBlockStart = (parsed, state) => {
|
|
1673
|
+
const block = getRecord(parsed, "content_block");
|
|
1674
|
+
if (block && block.type === "tool_use") {
|
|
1675
|
+
state.currentToolId = getString(block, "id");
|
|
1676
|
+
state.currentToolName = getString(block, "name");
|
|
1677
|
+
state.toolInputJson = "";
|
|
1678
|
+
state.isThinkingBlock = false;
|
|
1679
|
+
state.currentProviderBlock = undefined;
|
|
1680
|
+
} else if (block && block.type === "thinking") {
|
|
1681
|
+
state.isThinkingBlock = true;
|
|
1682
|
+
state.thinkingSignature = "";
|
|
1683
|
+
state.currentProviderBlock = undefined;
|
|
1684
|
+
} else {
|
|
1685
|
+
state.isThinkingBlock = false;
|
|
1686
|
+
state.currentProviderBlock = block && block.type !== "text" ? { ...block } : undefined;
|
|
1687
|
+
state.providerBlockInputJson = "";
|
|
1688
|
+
}
|
|
1689
|
+
};
|
|
1690
|
+
var handleContentBlockDelta = (parsed, state) => {
|
|
1691
|
+
const delta = getRecord(parsed, "delta");
|
|
1692
|
+
if (!delta) {
|
|
1693
|
+
return;
|
|
1694
|
+
}
|
|
1695
|
+
if (delta.type === "thinking_delta") {
|
|
1696
|
+
return {
|
|
1697
|
+
content: getString(delta, "thinking"),
|
|
1698
|
+
type: "thinking"
|
|
1699
|
+
};
|
|
1700
|
+
}
|
|
1701
|
+
if (delta.type === "text_delta") {
|
|
1702
|
+
return {
|
|
1703
|
+
content: getString(delta, "text"),
|
|
1704
|
+
type: "text"
|
|
1705
|
+
};
|
|
1706
|
+
}
|
|
1707
|
+
if (delta.type === "input_json_delta") {
|
|
1708
|
+
if (state.currentProviderBlock) {
|
|
1709
|
+
state.providerBlockInputJson += getString(delta, "partial_json");
|
|
1710
|
+
} else {
|
|
1711
|
+
state.toolInputJson += getString(delta, "partial_json");
|
|
1712
|
+
}
|
|
1713
|
+
}
|
|
1714
|
+
if (delta.type === "signature_delta") {
|
|
1715
|
+
state.thinkingSignature += getString(delta, "signature");
|
|
1716
|
+
}
|
|
1717
|
+
return;
|
|
1718
|
+
};
|
|
1719
|
+
var handleContentBlockStop = (state) => {
|
|
1720
|
+
if (state.isThinkingBlock && state.thinkingSignature) {
|
|
1721
|
+
state.isThinkingBlock = false;
|
|
1722
|
+
const signature = state.thinkingSignature;
|
|
1723
|
+
state.thinkingSignature = "";
|
|
1724
|
+
return {
|
|
1725
|
+
content: "",
|
|
1726
|
+
signature,
|
|
1727
|
+
type: "thinking"
|
|
1728
|
+
};
|
|
1729
|
+
}
|
|
1730
|
+
if (state.currentProviderBlock) {
|
|
1731
|
+
const data = { ...state.currentProviderBlock };
|
|
1732
|
+
if (state.providerBlockInputJson) {
|
|
1733
|
+
data.input = tryParseJson(state.providerBlockInputJson) ?? state.providerBlockInputJson;
|
|
1734
|
+
}
|
|
1735
|
+
state.currentProviderBlock = undefined;
|
|
1736
|
+
state.providerBlockInputJson = "";
|
|
1737
|
+
return {
|
|
1738
|
+
data,
|
|
1739
|
+
provider: state.providerName,
|
|
1740
|
+
type: "provider_event"
|
|
1741
|
+
};
|
|
1742
|
+
}
|
|
1743
|
+
if (!state.currentToolId) {
|
|
1744
|
+
return;
|
|
1745
|
+
}
|
|
1746
|
+
const input = tryParseJson(state.toolInputJson) ?? state.toolInputJson;
|
|
1747
|
+
const chunk = {
|
|
1748
|
+
id: state.currentToolId,
|
|
1749
|
+
input,
|
|
1750
|
+
name: state.currentToolName,
|
|
1751
|
+
type: "tool_use"
|
|
1752
|
+
};
|
|
1753
|
+
state.currentToolId = "";
|
|
1754
|
+
state.currentToolName = "";
|
|
1755
|
+
state.toolInputJson = "";
|
|
1756
|
+
return chunk;
|
|
1757
|
+
};
|
|
1758
|
+
var extractUsage3 = (usageRecord, existingUsage) => {
|
|
1759
|
+
if (!usageRecord) {
|
|
1760
|
+
return existingUsage;
|
|
1761
|
+
}
|
|
1762
|
+
const normalized = {
|
|
1763
|
+
cacheReadInputTokens: getNumber(usageRecord, "cache_read_input_tokens") || existingUsage?.cacheReadInputTokens || 0,
|
|
1764
|
+
cacheWriteInputTokens: getNumber(usageRecord, "cache_creation_input_tokens") || existingUsage?.cacheWriteInputTokens || 0,
|
|
1765
|
+
inputTokens: getNumber(usageRecord, "input_tokens") || existingUsage?.inputTokens || 0,
|
|
1766
|
+
outputTokens: getNumber(usageRecord, "output_tokens") || existingUsage?.outputTokens || 0,
|
|
1767
|
+
costCredits: getNumber(usageRecord, "cost") || existingUsage?.costCredits,
|
|
1768
|
+
reasoningTokens: getNumber(usageRecord, "reasoning_tokens") || existingUsage?.reasoningTokens,
|
|
1769
|
+
upstreamInferenceCostCredits: getNumber(getRecord(usageRecord, "cost_details") ?? {}, "upstream_inference_cost") || existingUsage?.upstreamInferenceCostCredits
|
|
1770
|
+
};
|
|
1771
|
+
const serverToolUse = getRecord(usageRecord, "server_tool_use");
|
|
1772
|
+
if (serverToolUse) {
|
|
1773
|
+
normalized.serverToolUse = Object.fromEntries(Object.entries(serverToolUse).filter((entry) => typeof entry[1] === "number"));
|
|
1774
|
+
}
|
|
1775
|
+
return normalized;
|
|
1776
|
+
};
|
|
1777
|
+
var mergeMetadata = (source, state) => {
|
|
1778
|
+
const providerMetadata = getRecord(source, "openrouter_metadata");
|
|
1779
|
+
const generationId = getString(source, "id") || undefined;
|
|
1780
|
+
const model = getString(source, "model") || undefined;
|
|
1781
|
+
const provider = getString(source, "provider") || undefined;
|
|
1782
|
+
const serviceTier = getString(source, "service_tier") || undefined;
|
|
1783
|
+
if (!providerMetadata && !generationId && !model && !provider && !serviceTier)
|
|
1784
|
+
return;
|
|
1785
|
+
state.metadata = {
|
|
1786
|
+
...state.metadata,
|
|
1787
|
+
generationId: generationId ?? state.metadata?.generationId,
|
|
1788
|
+
model: model ?? state.metadata?.model,
|
|
1789
|
+
provider: provider ?? state.metadata?.provider,
|
|
1790
|
+
providerMetadata: {
|
|
1791
|
+
...state.metadata?.providerMetadata,
|
|
1792
|
+
...providerMetadata
|
|
1793
|
+
},
|
|
1794
|
+
serviceTier: serviceTier ?? state.metadata?.serviceTier
|
|
1795
|
+
};
|
|
1796
|
+
};
|
|
1797
|
+
var handleMessageDelta = (parsed, state) => {
|
|
1798
|
+
const deltaUsage = getRecord(parsed, "usage");
|
|
1799
|
+
state.usage = extractUsage3(deltaUsage, state.usage);
|
|
1800
|
+
const delta = getRecord(parsed, "delta");
|
|
1801
|
+
const stopReason = delta ? getString(delta, "stop_reason") : "";
|
|
1802
|
+
if (stopReason)
|
|
1803
|
+
state.stopReason = stopReason;
|
|
1804
|
+
};
|
|
1805
|
+
var handleMessageStart = (parsed, state) => {
|
|
1806
|
+
const message = getRecord(parsed, "message");
|
|
1807
|
+
if (!message) {
|
|
1808
|
+
return;
|
|
1809
|
+
}
|
|
1810
|
+
const startUsage = getRecord(message, "usage");
|
|
1811
|
+
state.usage = extractUsage3(startUsage, state.usage);
|
|
1812
|
+
mergeMetadata(message, state);
|
|
1813
|
+
};
|
|
1814
|
+
var handleError = (parsed, state) => {
|
|
1815
|
+
const error = getRecord(parsed, "error");
|
|
1816
|
+
const errorMessage = error ? getString(error, "message") : "";
|
|
1817
|
+
const nativeErrorType = error ? getString(error, "type") : "";
|
|
1818
|
+
const errorType = error ? getString(error, "error_type") || nativeErrorType : "";
|
|
1819
|
+
const retryable = errorType === "provider_overloaded" || errorType === "rate_limit_exceeded" || errorType === "provider_unavailable" || errorType === "server" || nativeErrorType === "overloaded_error" || nativeErrorType === "rate_limit_error" || nativeErrorType === "api_error";
|
|
1820
|
+
throw new ProviderError({
|
|
1821
|
+
message: errorMessage || "Anthropic API error",
|
|
1822
|
+
metadata: error,
|
|
1823
|
+
provider: state.providerName,
|
|
1824
|
+
retryable,
|
|
1825
|
+
type: errorType || null
|
|
1826
|
+
});
|
|
1827
|
+
};
|
|
1828
|
+
var processEvent = (eventType, parsed, state) => {
|
|
1829
|
+
switch (eventType) {
|
|
1830
|
+
case "content_block_start": {
|
|
1831
|
+
handleContentBlockStart(parsed, state);
|
|
1832
|
+
return;
|
|
1833
|
+
}
|
|
1834
|
+
case "content_block_delta": {
|
|
1835
|
+
return handleContentBlockDelta(parsed, state);
|
|
1836
|
+
}
|
|
1837
|
+
case "content_block_stop": {
|
|
1838
|
+
return handleContentBlockStop(state);
|
|
1839
|
+
}
|
|
1840
|
+
case "message_delta": {
|
|
1841
|
+
handleMessageDelta(parsed, state);
|
|
1842
|
+
mergeMetadata(parsed, state);
|
|
1843
|
+
return;
|
|
1844
|
+
}
|
|
1845
|
+
case "message_start": {
|
|
1846
|
+
handleMessageStart(parsed, state);
|
|
1847
|
+
return;
|
|
1848
|
+
}
|
|
1849
|
+
case "message_stop": {
|
|
1850
|
+
mergeMetadata(parsed, state);
|
|
1851
|
+
return {
|
|
1852
|
+
stopReason: state.stopReason,
|
|
1853
|
+
metadata: state.metadata,
|
|
1854
|
+
type: "done",
|
|
1855
|
+
usage: state.usage
|
|
1856
|
+
};
|
|
1857
|
+
}
|
|
1858
|
+
case "error": {
|
|
1859
|
+
handleError(parsed, state);
|
|
1860
|
+
return;
|
|
1861
|
+
}
|
|
1862
|
+
default: {
|
|
1863
|
+
return;
|
|
1864
|
+
}
|
|
1865
|
+
}
|
|
1866
|
+
};
|
|
1867
|
+
var processSingleEvent = (event, state) => {
|
|
1868
|
+
if (!event.trim()) {
|
|
1869
|
+
return;
|
|
1870
|
+
}
|
|
1871
|
+
const { eventData, eventType } = parseEventLines(event);
|
|
1872
|
+
if (!eventData) {
|
|
1873
|
+
return;
|
|
1874
|
+
}
|
|
1875
|
+
const parsed = tryParseJson(eventData);
|
|
1876
|
+
if (!parsed) {
|
|
1877
|
+
return;
|
|
1878
|
+
}
|
|
1879
|
+
return processEvent(eventType, parsed, state);
|
|
1880
|
+
};
|
|
1881
|
+
var collectChunk = (event, state) => {
|
|
1882
|
+
const chunk = processSingleEvent(event, state);
|
|
1883
|
+
return chunk ? [chunk] : [];
|
|
1884
|
+
};
|
|
1885
|
+
var processBufferedEvents = (eventsText, state) => {
|
|
1886
|
+
const events = eventsText.split(`
|
|
1887
|
+
|
|
1888
|
+
`);
|
|
1889
|
+
state.buffer = events.pop() ?? "";
|
|
1890
|
+
return events.flatMap((event) => collectChunk(event, state));
|
|
1891
|
+
};
|
|
1892
|
+
var readNextChunks = async (reader, decoder, state, signal) => {
|
|
1893
|
+
if (signal?.aborted) {
|
|
1894
|
+
return { chunks: EMPTY_CHUNKS, done: true };
|
|
1895
|
+
}
|
|
1896
|
+
const { done, value } = await reader.read();
|
|
1897
|
+
if (done) {
|
|
1898
|
+
return { chunks: EMPTY_CHUNKS, done: true };
|
|
1899
|
+
}
|
|
1900
|
+
const rawText = state.buffer + decoder.decode(value, { stream: true });
|
|
1901
|
+
const chunks = processBufferedEvents(rawText, state);
|
|
1902
|
+
return { chunks, done: false };
|
|
1903
|
+
};
|
|
1904
|
+
var findDoneChunk = (chunks) => chunks.findIndex((c) => c.type === "done");
|
|
1905
|
+
var sseStreamLoop = async (reader, decoder, state, signal) => {
|
|
1906
|
+
const result = await readNextChunks(reader, decoder, state, signal);
|
|
1907
|
+
if (result.done) {
|
|
1908
|
+
return { chunks: result.chunks, finished: true };
|
|
1909
|
+
}
|
|
1910
|
+
const doneIdx = findDoneChunk(result.chunks);
|
|
1911
|
+
if (doneIdx >= 0) {
|
|
1912
|
+
return { chunks: result.chunks.slice(0, doneIdx + 1), finished: true };
|
|
1913
|
+
}
|
|
1914
|
+
return { chunks: result.chunks, finished: false };
|
|
1915
|
+
};
|
|
1916
|
+
async function* streamChunks(reader, decoder, state, signal) {
|
|
1917
|
+
let finished = false;
|
|
1918
|
+
while (!finished) {
|
|
1919
|
+
const result = await sseStreamLoop(reader, decoder, state, signal);
|
|
1920
|
+
({ finished } = result);
|
|
1921
|
+
yield* result.chunks;
|
|
1922
|
+
}
|
|
1923
|
+
}
|
|
1924
|
+
async function* parseSSEStream3(body, providerName, signal) {
|
|
1925
|
+
const reader = body.getReader();
|
|
1926
|
+
const decoder = new TextDecoder;
|
|
1927
|
+
const state = {
|
|
1928
|
+
buffer: "",
|
|
1929
|
+
currentToolId: "",
|
|
1930
|
+
currentToolName: "",
|
|
1931
|
+
isThinkingBlock: false,
|
|
1932
|
+
stopReason: "",
|
|
1933
|
+
thinkingSignature: "",
|
|
1934
|
+
toolInputJson: "",
|
|
1935
|
+
usage: undefined,
|
|
1936
|
+
providerName,
|
|
1937
|
+
providerBlockInputJson: ""
|
|
1938
|
+
};
|
|
1939
|
+
try {
|
|
1940
|
+
yield* streamChunks(reader, decoder, state, signal);
|
|
1941
|
+
} finally {
|
|
1942
|
+
reader.releaseLock();
|
|
1943
|
+
}
|
|
1944
|
+
}
|
|
1945
|
+
var fetchAndStream = async function* (baseUrl, config2, params, configuredMax, promptCaching, providerName) {
|
|
1946
|
+
const builtBody = buildRequestBody3(params, configuredMax, promptCaching);
|
|
1947
|
+
const body = config2.transformRequestBody ? config2.transformRequestBody(builtBody, params) : builtBody;
|
|
1948
|
+
const target = `${baseUrl}/v1/messages`;
|
|
1949
|
+
const fetchImpl = config2.fetch ?? fetch;
|
|
1950
|
+
const token = config2.tokenSource ? await Promise.resolve(config2.tokenSource()) : config2.apiKey;
|
|
1951
|
+
const suppliedHeaders = typeof config2.headers === "function" ? await config2.headers(params) : config2.headers ?? {};
|
|
1952
|
+
const requestHeaders = new Headers(suppliedHeaders);
|
|
1953
|
+
requestHeaders.set("Content-Type", "application/json");
|
|
1954
|
+
if (config2.authStyle === "bearer") {
|
|
1955
|
+
requestHeaders.set("Authorization", `Bearer ${token}`);
|
|
1956
|
+
} else {
|
|
1957
|
+
requestHeaders.set("anthropic-version", API_VERSION);
|
|
1958
|
+
requestHeaders.set("x-api-key", token);
|
|
1959
|
+
}
|
|
1960
|
+
const response = await fetchImpl(target, {
|
|
1961
|
+
...h2IfHttps3(target),
|
|
1962
|
+
body: JSON.stringify(body),
|
|
1963
|
+
headers: requestHeaders,
|
|
1964
|
+
method: "POST",
|
|
1965
|
+
signal: params.signal
|
|
1966
|
+
});
|
|
1967
|
+
if (!response.ok) {
|
|
1968
|
+
const errorText = await response.text();
|
|
1969
|
+
throw ProviderError.fromResponse(providerName, response.status, errorText);
|
|
1970
|
+
}
|
|
1971
|
+
if (!response.body) {
|
|
1972
|
+
throw new ProviderError({
|
|
1973
|
+
message: `${providerName} Messages API returned no response body`,
|
|
1974
|
+
provider: providerName,
|
|
1975
|
+
retryable: true
|
|
1976
|
+
});
|
|
1977
|
+
}
|
|
1978
|
+
yield* parseSSEStream3(response.body, providerName, params.signal);
|
|
1979
|
+
};
|
|
1980
|
+
var anthropic = (config2) => {
|
|
1981
|
+
if (!config2.apiKey && !config2.tokenSource)
|
|
1982
|
+
throw new Error("anthropic() requires either apiKey or tokenSource");
|
|
1983
|
+
const baseUrl = config2.baseUrl ?? DEFAULT_BASE_URL3;
|
|
1984
|
+
const configuredMax = config2.maxTokens ?? DEFAULT_MAX_TOKENS;
|
|
1985
|
+
const promptCaching = config2.promptCaching ?? true;
|
|
1986
|
+
const providerName = config2.providerName ?? "anthropic";
|
|
1987
|
+
return instrumentAIProvider({
|
|
1988
|
+
stream: (params) => fetchAndStream(baseUrl, config2, params, configuredMax, promptCaching, providerName)
|
|
1989
|
+
}, providerName);
|
|
1990
|
+
};
|
|
1991
|
+
|
|
1327
1992
|
// src/ai/providers/openrouterClient.ts
|
|
1328
|
-
var
|
|
1993
|
+
var OPENROUTER_PRICING_KEYS = [
|
|
1994
|
+
"prompt",
|
|
1995
|
+
"completion",
|
|
1996
|
+
"request",
|
|
1997
|
+
"image",
|
|
1998
|
+
"web_search",
|
|
1999
|
+
"internal_reasoning",
|
|
2000
|
+
"input_cache_read",
|
|
2001
|
+
"input_cache_write"
|
|
2002
|
+
];
|
|
2003
|
+
var estimateOpenRouterCost = (pricing, units) => {
|
|
2004
|
+
const components = {};
|
|
2005
|
+
let total = 0;
|
|
2006
|
+
for (const key of OPENROUTER_PRICING_KEYS) {
|
|
2007
|
+
const quantity = units[key];
|
|
2008
|
+
if (quantity === undefined)
|
|
2009
|
+
continue;
|
|
2010
|
+
if (!Number.isFinite(quantity) || quantity < 0)
|
|
2011
|
+
throw new Error(`OpenRouter ${key} units must be non-negative`);
|
|
2012
|
+
const rawPrice = pricing[key];
|
|
2013
|
+
if (rawPrice === undefined)
|
|
2014
|
+
continue;
|
|
2015
|
+
const price = Number(rawPrice);
|
|
2016
|
+
if (!Number.isFinite(price) || price < 0)
|
|
2017
|
+
throw new Error(`OpenRouter ${key} price must be non-negative`);
|
|
2018
|
+
components[key] = price * quantity;
|
|
2019
|
+
total += components[key];
|
|
2020
|
+
}
|
|
2021
|
+
return { components, total };
|
|
2022
|
+
};
|
|
2023
|
+
var estimateOpenRouterModelCost = (model, units) => estimateOpenRouterCost(model.pricing ?? {}, units);
|
|
2024
|
+
var DEFAULT_BASE_URL4 = "https://openrouter.ai/api/v1";
|
|
2025
|
+
var DEFAULT_BATCH_BASE_URL = "https://openrouter.ai/api/beta";
|
|
2026
|
+
var DEFAULT_SITE_URL = "https://openrouter.ai";
|
|
2027
|
+
var TERMINAL_BATCH_STATUSES = new Set([
|
|
2028
|
+
"completed",
|
|
2029
|
+
"failed",
|
|
2030
|
+
"expired",
|
|
2031
|
+
"cancelled"
|
|
2032
|
+
]);
|
|
1329
2033
|
var withoutLatestPrefix = (model) => model.startsWith("~") ? model.slice(1) : model;
|
|
1330
2034
|
var openRouterModelMatchesRule = (model, rule) => {
|
|
1331
2035
|
const normalizedModel = withoutLatestPrefix(model);
|
|
@@ -1339,6 +2043,22 @@ var assertAllowedModel = (model, allowedModels) => {
|
|
|
1339
2043
|
return;
|
|
1340
2044
|
throw new Error(`OpenRouter model "${model}" is not allowed`);
|
|
1341
2045
|
};
|
|
2046
|
+
var assertAllowedModelsInValue = (value, allowedModels, key = "") => {
|
|
2047
|
+
if (key === "model" && typeof value === "string")
|
|
2048
|
+
assertAllowedModel(value, allowedModels);
|
|
2049
|
+
if ((key === "models" || key === "analysis_models" || key === "allowed_models") && Array.isArray(value)) {
|
|
2050
|
+
for (const model of value)
|
|
2051
|
+
if (typeof model === "string")
|
|
2052
|
+
assertAllowedModel(model, allowedModels);
|
|
2053
|
+
}
|
|
2054
|
+
if (Array.isArray(value)) {
|
|
2055
|
+
for (const item of value)
|
|
2056
|
+
assertAllowedModelsInValue(item, allowedModels);
|
|
2057
|
+
} else if (value && typeof value === "object") {
|
|
2058
|
+
for (const [childKey, child] of Object.entries(value))
|
|
2059
|
+
assertAllowedModelsInValue(child, allowedModels, childKey);
|
|
2060
|
+
}
|
|
2061
|
+
};
|
|
1342
2062
|
var normalizePath = (path) => path.startsWith("/") ? path : `/${path}`;
|
|
1343
2063
|
var encodeModelPath = (model) => model.split("/").map(encodeURIComponent).join("/");
|
|
1344
2064
|
var withQuery = (url, query) => {
|
|
@@ -1351,13 +2071,142 @@ var withQuery = (url, query) => {
|
|
|
1351
2071
|
}
|
|
1352
2072
|
return result.toString();
|
|
1353
2073
|
};
|
|
2074
|
+
var parseImageSSE = async function* (response) {
|
|
2075
|
+
if (!response.body)
|
|
2076
|
+
throw new Error("OpenRouter image stream has no body");
|
|
2077
|
+
const reader = response.body.getReader();
|
|
2078
|
+
const decoder = new TextDecoder;
|
|
2079
|
+
let buffer = "";
|
|
2080
|
+
try {
|
|
2081
|
+
for (;; ) {
|
|
2082
|
+
const result = await reader.read();
|
|
2083
|
+
buffer += decoder.decode(result.value, { stream: !result.done });
|
|
2084
|
+
const lines = buffer.split(`
|
|
2085
|
+
`);
|
|
2086
|
+
buffer = lines.pop() ?? "";
|
|
2087
|
+
for (const line of lines) {
|
|
2088
|
+
if (!line.startsWith("data: "))
|
|
2089
|
+
continue;
|
|
2090
|
+
const data = line.slice(6);
|
|
2091
|
+
if (data === "[DONE]")
|
|
2092
|
+
return;
|
|
2093
|
+
try {
|
|
2094
|
+
const parsed = JSON.parse(data);
|
|
2095
|
+
if (parsed && typeof parsed === "object" && "type" in parsed)
|
|
2096
|
+
yield parsed;
|
|
2097
|
+
} catch {}
|
|
2098
|
+
}
|
|
2099
|
+
if (result.done)
|
|
2100
|
+
break;
|
|
2101
|
+
}
|
|
2102
|
+
if (buffer.startsWith("data: ")) {
|
|
2103
|
+
const parsed = JSON.parse(buffer.slice(6));
|
|
2104
|
+
if (parsed && typeof parsed === "object" && "type" in parsed)
|
|
2105
|
+
yield parsed;
|
|
2106
|
+
}
|
|
2107
|
+
} finally {
|
|
2108
|
+
reader.releaseLock();
|
|
2109
|
+
}
|
|
2110
|
+
};
|
|
2111
|
+
var toBytes = (value) => typeof value === "string" ? new TextEncoder().encode(value) : value;
|
|
2112
|
+
var toBase64Url = (bytes) => {
|
|
2113
|
+
let binary = "";
|
|
2114
|
+
for (const byte of bytes)
|
|
2115
|
+
binary += String.fromCharCode(byte);
|
|
2116
|
+
return btoa(binary).replaceAll("+", "-").replaceAll("/", "_").replace(/=+$/u, "");
|
|
2117
|
+
};
|
|
2118
|
+
var generateOpenRouterPKCE = async () => {
|
|
2119
|
+
const random = crypto.getRandomValues(new Uint8Array(32));
|
|
2120
|
+
const codeVerifier = toBase64Url(random);
|
|
2121
|
+
const digest = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(codeVerifier));
|
|
2122
|
+
return {
|
|
2123
|
+
codeChallenge: toBase64Url(new Uint8Array(digest)),
|
|
2124
|
+
codeChallengeMethod: "S256",
|
|
2125
|
+
codeVerifier
|
|
2126
|
+
};
|
|
2127
|
+
};
|
|
2128
|
+
var createOpenRouterAuthorizationUrl = (options = {}) => {
|
|
2129
|
+
const url = new URL("/auth", options.baseUrl ?? DEFAULT_SITE_URL);
|
|
2130
|
+
if (options.callbackUrl)
|
|
2131
|
+
url.searchParams.set("callback_url", options.callbackUrl);
|
|
2132
|
+
if (options.codeChallenge)
|
|
2133
|
+
url.searchParams.set("code_challenge", options.codeChallenge);
|
|
2134
|
+
if (options.codeChallengeMethod)
|
|
2135
|
+
url.searchParams.set("code_challenge_method", options.codeChallengeMethod);
|
|
2136
|
+
if (options.keyLabel)
|
|
2137
|
+
url.searchParams.set("key_label", options.keyLabel);
|
|
2138
|
+
return url.toString();
|
|
2139
|
+
};
|
|
2140
|
+
var exchangeOpenRouterAuthCode = async (body, options = {}) => {
|
|
2141
|
+
const response = await (options.fetch ?? globalThis.fetch)(`${(options.baseUrl ?? DEFAULT_BASE_URL4).replace(/\/$/, "")}/auth/keys`, {
|
|
2142
|
+
body: JSON.stringify(body),
|
|
2143
|
+
headers: { "Content-Type": "application/json" },
|
|
2144
|
+
method: "POST"
|
|
2145
|
+
});
|
|
2146
|
+
if (!response.ok)
|
|
2147
|
+
throw ProviderError.fromResponse("openrouter", response.status, await response.text());
|
|
2148
|
+
return response.json();
|
|
2149
|
+
};
|
|
2150
|
+
var createOpenRouterKeyLinks = async (key, siteUrl = DEFAULT_SITE_URL) => {
|
|
2151
|
+
const digest = new Uint8Array(await crypto.subtle.digest("SHA-256", new TextEncoder().encode(key)));
|
|
2152
|
+
const hash = Array.from(digest, (byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
2153
|
+
const root = siteUrl.replace(/\/$/, "");
|
|
2154
|
+
return {
|
|
2155
|
+
hash,
|
|
2156
|
+
logsUrl: `${root}/logs?api_key_hash=${hash}`,
|
|
2157
|
+
settingsUrl: `${root}/keys/${hash}`
|
|
2158
|
+
};
|
|
2159
|
+
};
|
|
2160
|
+
var hexToBytes = (hex) => {
|
|
2161
|
+
if (!/^[0-9a-f]+$/iu.test(hex) || hex.length % 2 !== 0)
|
|
2162
|
+
return;
|
|
2163
|
+
const bytes = new Uint8Array(hex.length / 2);
|
|
2164
|
+
for (let index = 0;index < bytes.length; index += 1) {
|
|
2165
|
+
bytes[index] = Number.parseInt(hex.slice(index * 2, index * 2 + 2), 16);
|
|
2166
|
+
}
|
|
2167
|
+
return bytes;
|
|
2168
|
+
};
|
|
2169
|
+
var constantTimeEqual = (left, right) => {
|
|
2170
|
+
if (left.length !== right.length)
|
|
2171
|
+
return false;
|
|
2172
|
+
let mismatch = 0;
|
|
2173
|
+
for (let index = 0;index < left.length; index += 1)
|
|
2174
|
+
mismatch |= (left[index] ?? 0) ^ (right[index] ?? 0);
|
|
2175
|
+
return mismatch === 0;
|
|
2176
|
+
};
|
|
2177
|
+
var verifyOpenRouterWebhookSignature = async (options) => {
|
|
2178
|
+
const fields = new Map(options.header.split(",").map((part) => {
|
|
2179
|
+
const [key2, ...rest] = part.trim().split("=");
|
|
2180
|
+
return [key2, rest.join("=")];
|
|
2181
|
+
}));
|
|
2182
|
+
const timestamp = fields.get("t");
|
|
2183
|
+
const supplied = fields.get("v1");
|
|
2184
|
+
if (!timestamp || !supplied)
|
|
2185
|
+
return false;
|
|
2186
|
+
const timestampNumber = Number(timestamp);
|
|
2187
|
+
const now = options.nowSeconds ?? Math.floor(Date.now() / 1000);
|
|
2188
|
+
const tolerance = options.toleranceSeconds ?? 300;
|
|
2189
|
+
if (!Number.isFinite(timestampNumber) || Math.abs(now - timestampNumber) > tolerance)
|
|
2190
|
+
return false;
|
|
2191
|
+
const key = await crypto.subtle.importKey("raw", Uint8Array.from(toBytes(options.secret)).buffer, { hash: "SHA-256", name: "HMAC" }, false, ["sign"]);
|
|
2192
|
+
const prefix = new TextEncoder().encode(`${timestamp},`);
|
|
2193
|
+
const body = toBytes(options.body);
|
|
2194
|
+
const payload = new Uint8Array(prefix.length + body.length);
|
|
2195
|
+
payload.set(prefix);
|
|
2196
|
+
payload.set(body, prefix.length);
|
|
2197
|
+
const expected = new Uint8Array(await crypto.subtle.sign("HMAC", key, payload.buffer));
|
|
2198
|
+
const suppliedBytes = hexToBytes(supplied);
|
|
2199
|
+
return suppliedBytes ? constantTimeEqual(expected, suppliedBytes) : false;
|
|
2200
|
+
};
|
|
1354
2201
|
var createOpenRouterClient = (config2) => {
|
|
1355
2202
|
if (!config2.apiKey && !config2.tokenSource)
|
|
1356
2203
|
throw new Error("createOpenRouterClient() requires either apiKey or tokenSource");
|
|
1357
|
-
const baseUrl = (config2.baseUrl ??
|
|
2204
|
+
const baseUrl = (config2.baseUrl ?? DEFAULT_BASE_URL4).replace(/\/$/, "");
|
|
2205
|
+
const batchBaseUrl = (config2.batchBaseUrl ?? (config2.baseUrl ? new URL("../beta", `${baseUrl}/`).toString() : DEFAULT_BATCH_BASE_URL)).replace(/\/$/, "");
|
|
1358
2206
|
const fetchImpl = config2.fetch ?? globalThis.fetch;
|
|
1359
2207
|
const allowedModels = config2.allowedModels ? [...config2.allowedModels] : undefined;
|
|
1360
|
-
const
|
|
2208
|
+
const defaultWorkspaceId = config2.workspaceId;
|
|
2209
|
+
const requestRawAt = async (rootUrl, path, options = {}) => {
|
|
1361
2210
|
const token = config2.tokenSource ? await Promise.resolve(config2.tokenSource()) : config2.apiKey;
|
|
1362
2211
|
const suppliedHeaders = typeof config2.headers === "function" ? await config2.headers() : config2.headers ?? {};
|
|
1363
2212
|
const headers = new Headers(suppliedHeaders);
|
|
@@ -1371,13 +2220,16 @@ var createOpenRouterClient = (config2) => {
|
|
|
1371
2220
|
body = JSON.stringify(options.body);
|
|
1372
2221
|
}
|
|
1373
2222
|
const { query, ...requestInit } = options;
|
|
1374
|
-
const response = await fetchImpl(withQuery(`${
|
|
2223
|
+
const response = await fetchImpl(withQuery(`${rootUrl}${normalizePath(path)}`, options.query), { ...requestInit, body, headers });
|
|
1375
2224
|
if (!response.ok) {
|
|
1376
2225
|
throw ProviderError.fromResponse("openrouter", response.status, await response.text());
|
|
1377
2226
|
}
|
|
1378
2227
|
return response;
|
|
1379
2228
|
};
|
|
2229
|
+
const requestRaw = (path, options = {}) => requestRawAt(baseUrl, path, options);
|
|
1380
2230
|
const request = async (path, options = {}) => (await requestRaw(path, options)).json();
|
|
2231
|
+
const requestBatch = async (path, options = {}) => (await requestRawAt(batchBaseUrl, path, options)).json();
|
|
2232
|
+
const getBatch = (id) => requestBatch(`/batches/${encodeURIComponent(id)}`);
|
|
1381
2233
|
const listModels = async (query) => {
|
|
1382
2234
|
const result = await request("/models", { query });
|
|
1383
2235
|
if (!allowedModels)
|
|
@@ -1396,10 +2248,45 @@ var createOpenRouterClient = (config2) => {
|
|
|
1396
2248
|
};
|
|
1397
2249
|
};
|
|
1398
2250
|
return {
|
|
1399
|
-
|
|
2251
|
+
addWorkspaceMembers: (id, userIds) => request(`/workspaces/${encodeURIComponent(id)}/members/add`, { body: { user_ids: [...userIds] }, method: "POST" }),
|
|
2252
|
+
createAuthCode: (body) => request("/auth/keys/code", {
|
|
2253
|
+
body: {
|
|
2254
|
+
...body,
|
|
2255
|
+
workspace_id: body.workspace_id ?? defaultWorkspaceId
|
|
2256
|
+
},
|
|
1400
2257
|
method: "POST"
|
|
1401
2258
|
}),
|
|
1402
|
-
|
|
2259
|
+
createPresetFromChatCompletions: (slug, body) => {
|
|
2260
|
+
assertAllowedModelsInValue(body, allowedModels);
|
|
2261
|
+
return request(`/presets/${encodeURIComponent(slug)}/chat/completions`, { body, method: "POST" });
|
|
2262
|
+
},
|
|
2263
|
+
createPresetFromMessages: (slug, body) => {
|
|
2264
|
+
assertAllowedModelsInValue(body, allowedModels);
|
|
2265
|
+
return request(`/presets/${encodeURIComponent(slug)}/messages`, { body, method: "POST" });
|
|
2266
|
+
},
|
|
2267
|
+
createPresetFromResponses: (slug, body) => {
|
|
2268
|
+
assertAllowedModelsInValue(body, allowedModels);
|
|
2269
|
+
return request(`/presets/${encodeURIComponent(slug)}/responses`, { body, method: "POST" });
|
|
2270
|
+
},
|
|
2271
|
+
createWorkspace: (body) => {
|
|
2272
|
+
assertAllowedModelsInValue(body, allowedModels);
|
|
2273
|
+
return request("/workspaces", {
|
|
2274
|
+
body,
|
|
2275
|
+
method: "POST"
|
|
2276
|
+
});
|
|
2277
|
+
},
|
|
2278
|
+
createBatch: (body) => {
|
|
2279
|
+
assertAllowedModel(body.model, allowedModels);
|
|
2280
|
+
return requestBatch("/batches", {
|
|
2281
|
+
body: {
|
|
2282
|
+
endpoint: body.endpoint,
|
|
2283
|
+
model: body.model,
|
|
2284
|
+
requests: body.requests,
|
|
2285
|
+
...body.completion_window ? { completion_window: body.completion_window } : {}
|
|
2286
|
+
},
|
|
2287
|
+
method: "POST"
|
|
2288
|
+
});
|
|
2289
|
+
},
|
|
1403
2290
|
createEmbedding: (body) => {
|
|
1404
2291
|
assertAllowedModel(body.model, allowedModels);
|
|
1405
2292
|
return request("/embeddings", {
|
|
@@ -1414,6 +2301,17 @@ var createOpenRouterClient = (config2) => {
|
|
|
1414
2301
|
method: "POST"
|
|
1415
2302
|
});
|
|
1416
2303
|
},
|
|
2304
|
+
deleteFile: (id, workspaceId = defaultWorkspaceId) => request(`/files/${encodeURIComponent(id)}`, { method: "DELETE", query: { workspace_id: workspaceId } }),
|
|
2305
|
+
deleteWorkspace: (id) => request(`/workspaces/${encodeURIComponent(id)}`, {
|
|
2306
|
+
method: "DELETE"
|
|
2307
|
+
}),
|
|
2308
|
+
deleteWorkspaceBudget: (id, interval) => request(`/workspaces/${encodeURIComponent(id)}/budgets/${interval}`, { method: "DELETE" }),
|
|
2309
|
+
downloadFile: (id, workspaceId = defaultWorkspaceId) => requestRaw(`/files/${encodeURIComponent(id)}/content`, {
|
|
2310
|
+
query: { workspace_id: workspaceId }
|
|
2311
|
+
}),
|
|
2312
|
+
downloadVideo: (id, index = 0) => requestRaw(`/videos/${encodeURIComponent(id)}/content`, {
|
|
2313
|
+
query: { index }
|
|
2314
|
+
}),
|
|
1417
2315
|
generateVideo: (body) => {
|
|
1418
2316
|
assertAllowedModel(body.model, allowedModels);
|
|
1419
2317
|
return request("/videos", {
|
|
@@ -1421,25 +2319,90 @@ var createOpenRouterClient = (config2) => {
|
|
|
1421
2319
|
method: "POST"
|
|
1422
2320
|
});
|
|
1423
2321
|
},
|
|
1424
|
-
getBatch
|
|
2322
|
+
getBatch,
|
|
2323
|
+
getActivity: (query) => request("/activity", {
|
|
2324
|
+
query: {
|
|
2325
|
+
...query,
|
|
2326
|
+
workspace_id: query?.workspace_id ?? defaultWorkspaceId
|
|
2327
|
+
}
|
|
2328
|
+
}),
|
|
2329
|
+
getAnalyticsMeta: () => request("/analytics/meta"),
|
|
1425
2330
|
getCredits: () => request("/credits"),
|
|
1426
2331
|
getCurrentKey: () => request("/key"),
|
|
2332
|
+
getFile: (id, workspaceId = defaultWorkspaceId) => request(`/files/${encodeURIComponent(id)}`, {
|
|
2333
|
+
query: { workspace_id: workspaceId }
|
|
2334
|
+
}),
|
|
1427
2335
|
getGeneration: (id) => request("/generation", {
|
|
1428
2336
|
query: { id }
|
|
1429
2337
|
}),
|
|
2338
|
+
getGenerationContent: (id) => request("/generation/content", {
|
|
2339
|
+
query: { id }
|
|
2340
|
+
}),
|
|
1430
2341
|
getModelEndpoints: (model) => {
|
|
1431
2342
|
assertAllowedModel(model, allowedModels);
|
|
1432
2343
|
return request(`/models/${encodeModelPath(model)}/endpoints`);
|
|
1433
2344
|
},
|
|
2345
|
+
getModel: (model) => {
|
|
2346
|
+
assertAllowedModel(model, allowedModels);
|
|
2347
|
+
return request(`/model/${encodeModelPath(model)}`);
|
|
2348
|
+
},
|
|
2349
|
+
getImageModelEndpoints: (model) => {
|
|
2350
|
+
assertAllowedModel(model, allowedModels);
|
|
2351
|
+
return request(`/images/models/${encodeModelPath(model)}/endpoints`);
|
|
2352
|
+
},
|
|
2353
|
+
getPreset: (slug) => request(`/presets/${encodeURIComponent(slug)}`),
|
|
2354
|
+
getPresetVersion: (slug, version) => request(`/presets/${encodeURIComponent(slug)}/versions/${encodeURIComponent(String(version))}`),
|
|
2355
|
+
getTaskClassifications: (window = "7d") => request("/classifications/task", {
|
|
2356
|
+
query: { window }
|
|
2357
|
+
}),
|
|
1434
2358
|
getVideo: (id) => request(`/videos/${encodeURIComponent(id)}`),
|
|
2359
|
+
getWorkspace: (id) => request(`/workspaces/${encodeURIComponent(id)}`),
|
|
1435
2360
|
listImageModels: async () => filterModelList(await request("/images/models")),
|
|
2361
|
+
listFiles: (query) => request("/files", {
|
|
2362
|
+
query: {
|
|
2363
|
+
...query,
|
|
2364
|
+
workspace_id: query?.workspace_id ?? defaultWorkspaceId
|
|
2365
|
+
}
|
|
2366
|
+
}),
|
|
1436
2367
|
listModels,
|
|
1437
|
-
|
|
2368
|
+
listUserModels: async () => filterModelList(await request("/models/user")),
|
|
2369
|
+
listZdrEndpoints: async () => {
|
|
2370
|
+
const result = await request("/endpoints/zdr");
|
|
2371
|
+
if (!allowedModels)
|
|
2372
|
+
return result;
|
|
2373
|
+
return {
|
|
2374
|
+
...result,
|
|
2375
|
+
data: result.data.filter((endpoint) => allowedModels.some((rule) => openRouterModelMatchesRule(endpoint.model_id, rule)))
|
|
2376
|
+
};
|
|
2377
|
+
},
|
|
2378
|
+
countModels: (outputModalities) => request("/models/count", {
|
|
2379
|
+
query: { output_modalities: outputModalities }
|
|
2380
|
+
}),
|
|
2381
|
+
listPresets: (offset = 0, limit = 100) => request("/presets", {
|
|
2382
|
+
query: { limit, offset }
|
|
2383
|
+
}),
|
|
2384
|
+
listPresetVersions: (slug, offset = 0, limit = 100) => request(`/presets/${encodeURIComponent(slug)}/versions`, { query: { limit, offset } }),
|
|
1438
2385
|
listProviders: () => request("/providers"),
|
|
1439
2386
|
listRerankModels: async () => filterModelList(await request("/rerank/models")),
|
|
1440
2387
|
listVideoModels: async () => filterModelList(await request("/videos/models")),
|
|
2388
|
+
listWorkspaceBudgets: (id) => request(`/workspaces/${encodeURIComponent(id)}/budgets`),
|
|
2389
|
+
listWorkspaces: (offset = 0, limit = 100) => request("/workspaces", { query: { limit, offset } }),
|
|
2390
|
+
queryAnalytics: (body) => request("/analytics/query", {
|
|
2391
|
+
body,
|
|
2392
|
+
method: "POST"
|
|
2393
|
+
}),
|
|
2394
|
+
removeWorkspaceMembers: (id, userIds) => request(`/workspaces/${encodeURIComponent(id)}/members/remove`, { body: { user_ids: [...userIds] }, method: "POST" }),
|
|
1441
2395
|
request,
|
|
1442
2396
|
requestRaw,
|
|
2397
|
+
streamImage: async function* (body, options = {}) {
|
|
2398
|
+
assertAllowedModel(body.model, allowedModels);
|
|
2399
|
+
const response = await requestRaw("/images", {
|
|
2400
|
+
...options,
|
|
2401
|
+
body: { ...body, stream: true },
|
|
2402
|
+
method: "POST"
|
|
2403
|
+
});
|
|
2404
|
+
yield* parseImageSSE(response);
|
|
2405
|
+
},
|
|
1443
2406
|
respond: (body) => {
|
|
1444
2407
|
assertAllowedModel(body.model, allowedModels);
|
|
1445
2408
|
return body.stream ? requestRaw("/responses", { body, method: "POST" }) : request("/responses", {
|
|
@@ -1471,12 +2434,57 @@ var createOpenRouterClient = (config2) => {
|
|
|
1471
2434
|
body,
|
|
1472
2435
|
method: "POST"
|
|
1473
2436
|
});
|
|
2437
|
+
},
|
|
2438
|
+
uploadFile: (file, options = {}) => {
|
|
2439
|
+
const body = new FormData;
|
|
2440
|
+
if (options.filename)
|
|
2441
|
+
body.append("file", file, options.filename);
|
|
2442
|
+
else
|
|
2443
|
+
body.append("file", file);
|
|
2444
|
+
return request("/files", {
|
|
2445
|
+
body,
|
|
2446
|
+
method: "POST",
|
|
2447
|
+
query: { workspace_id: options.workspaceId ?? defaultWorkspaceId }
|
|
2448
|
+
});
|
|
2449
|
+
},
|
|
2450
|
+
updateWorkspace: (id, body) => {
|
|
2451
|
+
assertAllowedModelsInValue(body, allowedModels);
|
|
2452
|
+
return request(`/workspaces/${encodeURIComponent(id)}`, { body, method: "PATCH" });
|
|
2453
|
+
},
|
|
2454
|
+
upsertWorkspaceBudget: (id, interval, limitUsd) => request(`/workspaces/${encodeURIComponent(id)}/budgets/${interval}`, { body: { limit_usd: limitUsd }, method: "PUT" }),
|
|
2455
|
+
waitForBatch: async (id, options = {}) => {
|
|
2456
|
+
const intervalMs = options.intervalMs ?? 1000;
|
|
2457
|
+
const timeoutMs = options.timeoutMs;
|
|
2458
|
+
if (!Number.isFinite(intervalMs) || intervalMs < 0)
|
|
2459
|
+
throw new Error("OpenRouter batch intervalMs must be non-negative");
|
|
2460
|
+
if (timeoutMs !== undefined && (!Number.isFinite(timeoutMs) || timeoutMs < 0))
|
|
2461
|
+
throw new Error("OpenRouter batch timeoutMs must be non-negative");
|
|
2462
|
+
const startedAt = Date.now();
|
|
2463
|
+
for (;; ) {
|
|
2464
|
+
options.signal?.throwIfAborted();
|
|
2465
|
+
const batch = await getBatch(id);
|
|
2466
|
+
if (TERMINAL_BATCH_STATUSES.has(batch.status))
|
|
2467
|
+
return batch;
|
|
2468
|
+
if (timeoutMs !== undefined && Date.now() - startedAt + intervalMs > timeoutMs)
|
|
2469
|
+
throw new Error(`Timed out waiting for OpenRouter batch "${id}"`);
|
|
2470
|
+
await new Promise((resolve, reject) => {
|
|
2471
|
+
const onAbort = () => {
|
|
2472
|
+
clearTimeout(timeout);
|
|
2473
|
+
reject(options.signal?.reason);
|
|
2474
|
+
};
|
|
2475
|
+
const timeout = setTimeout(() => {
|
|
2476
|
+
options.signal?.removeEventListener("abort", onAbort);
|
|
2477
|
+
resolve();
|
|
2478
|
+
}, intervalMs);
|
|
2479
|
+
options.signal?.addEventListener("abort", onAbort, { once: true });
|
|
2480
|
+
});
|
|
2481
|
+
}
|
|
1474
2482
|
}
|
|
1475
2483
|
};
|
|
1476
2484
|
};
|
|
1477
2485
|
|
|
1478
2486
|
// src/ai/providers/openrouter.ts
|
|
1479
|
-
var
|
|
2487
|
+
var DEFAULT_BASE_URL5 = "https://openrouter.ai/api";
|
|
1480
2488
|
var MAX_APP_CATEGORIES = 2;
|
|
1481
2489
|
var withoutLatestPrefix2 = (model) => model.startsWith("~") ? model.slice(1) : model;
|
|
1482
2490
|
var modelForOpenAICapabilities = (model) => {
|
|
@@ -1607,7 +2615,7 @@ var assertAllowedPreset = (preset, allowedPresets) => {
|
|
|
1607
2615
|
var assertIndirectModels = (value, allowedModels, key = "") => {
|
|
1608
2616
|
if (key === "model" && typeof value === "string")
|
|
1609
2617
|
assertAllowedModel2(value, allowedModels);
|
|
1610
|
-
if (key === "models" && Array.isArray(value)) {
|
|
2618
|
+
if ((key === "models" || key === "analysis_models" || key === "allowed_models") && Array.isArray(value)) {
|
|
1611
2619
|
for (const model of value) {
|
|
1612
2620
|
if (typeof model === "string")
|
|
1613
2621
|
assertAllowedModel2(model, allowedModels);
|
|
@@ -1638,6 +2646,7 @@ var assertRequestOptions = (options, allowedModels, allowedPresets, allowedProvi
|
|
|
1638
2646
|
assertAllowedModel2(model, allowedModels);
|
|
1639
2647
|
}
|
|
1640
2648
|
assertIndirectModels(options.serverTools, allowedModels);
|
|
2649
|
+
assertIndirectModels(options.messagesTools, allowedModels);
|
|
1641
2650
|
assertIndirectModels(options.plugins, allowedModels);
|
|
1642
2651
|
if (options.extraBody) {
|
|
1643
2652
|
const unsafe = Object.keys(options.extraBody).find((key) => SECURITY_SENSITIVE_EXTRA_BODY_FIELDS.has(key));
|
|
@@ -1656,17 +2665,41 @@ var snapshotPolicy = (config2) => ({
|
|
|
1656
2665
|
allowedModels: config2.allowedModels ? [...config2.allowedModels] : undefined,
|
|
1657
2666
|
allowedPresets: config2.allowedPresets ? [...config2.allowedPresets] : undefined
|
|
1658
2667
|
});
|
|
1659
|
-
var transformOpenRouterRequest = (config2, allowedModels, allowedPresets, body, params) => {
|
|
2668
|
+
var transformOpenRouterRequest = (config2, allowedModels, allowedPresets, body, params, skin = "openai") => {
|
|
1660
2669
|
const options = requestOptionsFor(params, config2.requestOptions);
|
|
1661
2670
|
assertRequestOptions(options, allowedModels, allowedPresets, config2.allowedProviders);
|
|
1662
2671
|
const transformed = { ...body, ...options.extraBody };
|
|
1663
|
-
if (
|
|
1664
|
-
transformed.
|
|
1665
|
-
|
|
1666
|
-
}
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
2672
|
+
if (options.audioOutput) {
|
|
2673
|
+
transformed.audio = options.audioOutput;
|
|
2674
|
+
transformed.modalities = ["text", "audio"];
|
|
2675
|
+
}
|
|
2676
|
+
if (skin === "openai") {
|
|
2677
|
+
const requestedReasoning = {};
|
|
2678
|
+
if (params.reasoning?.budgetTokens !== undefined) {
|
|
2679
|
+
requestedReasoning.max_tokens = params.reasoning.budgetTokens;
|
|
2680
|
+
delete transformed.reasoning_effort;
|
|
2681
|
+
} else if (params.reasoning?.effort) {
|
|
2682
|
+
requestedReasoning.effort = params.reasoning.effort;
|
|
2683
|
+
delete transformed.reasoning_effort;
|
|
2684
|
+
}
|
|
2685
|
+
if (options.reasoning) {
|
|
2686
|
+
Object.assign(requestedReasoning, options.reasoning);
|
|
2687
|
+
if (options.reasoning.maxTokens !== undefined) {
|
|
2688
|
+
requestedReasoning.max_tokens = options.reasoning.maxTokens;
|
|
2689
|
+
delete requestedReasoning.maxTokens;
|
|
2690
|
+
}
|
|
2691
|
+
}
|
|
2692
|
+
if (Object.keys(requestedReasoning).length > 0)
|
|
2693
|
+
transformed.reasoning = requestedReasoning;
|
|
2694
|
+
}
|
|
2695
|
+
const automaticCacheControl = params.promptCaching === true || params.cacheSystemPrompt === true ? { type: "ephemeral" } : undefined;
|
|
2696
|
+
const cacheControl = options.cacheControl ?? automaticCacheControl;
|
|
2697
|
+
if (cacheControl)
|
|
2698
|
+
transformed.cache_control = cacheControl;
|
|
2699
|
+
if (options.promptCacheKey)
|
|
2700
|
+
transformed.prompt_cache_key = options.promptCacheKey;
|
|
2701
|
+
if (options.promptCacheOptions)
|
|
2702
|
+
transformed.prompt_cache_options = options.promptCacheOptions;
|
|
1670
2703
|
const routing = mapRouting({ ...config2.routing, ...options.routing }, config2.allowedProviders);
|
|
1671
2704
|
if (Object.keys(routing).length > 0)
|
|
1672
2705
|
transformed.provider = routing;
|
|
@@ -1686,12 +2719,22 @@ var transformOpenRouterRequest = (config2, allowedModels, allowedPresets, body,
|
|
|
1686
2719
|
...options.serverTools
|
|
1687
2720
|
];
|
|
1688
2721
|
}
|
|
2722
|
+
if (options.messagesTools) {
|
|
2723
|
+
if (skin !== "messages")
|
|
2724
|
+
throw new Error("OpenRouter messagesTools requires openrouterMessages()");
|
|
2725
|
+
transformed.tools = [
|
|
2726
|
+
...Array.isArray(transformed.tools) ? transformed.tools : [],
|
|
2727
|
+
...options.messagesTools
|
|
2728
|
+
];
|
|
2729
|
+
}
|
|
1689
2730
|
if (options.serviceTier)
|
|
1690
2731
|
transformed.service_tier = options.serviceTier;
|
|
1691
2732
|
if (options.sessionId)
|
|
1692
2733
|
transformed.session_id = options.sessionId;
|
|
1693
2734
|
if (options.stopServerToolsWhen)
|
|
1694
2735
|
transformed.stop_server_tools_when = options.stopServerToolsWhen;
|
|
2736
|
+
if (options.trace)
|
|
2737
|
+
transformed.trace = options.trace;
|
|
1695
2738
|
if (options.transforms)
|
|
1696
2739
|
transformed.transforms = [...options.transforms];
|
|
1697
2740
|
if (options.user)
|
|
@@ -1721,7 +2764,7 @@ var openrouter = (config2) => {
|
|
|
1721
2764
|
const { allowedModels, allowedPresets } = snapshotPolicy(config2);
|
|
1722
2765
|
const provider = openai({
|
|
1723
2766
|
apiKey: config2.apiKey,
|
|
1724
|
-
baseUrl: config2.baseUrl ??
|
|
2767
|
+
baseUrl: config2.baseUrl ?? DEFAULT_BASE_URL5,
|
|
1725
2768
|
fetch: config2.fetch,
|
|
1726
2769
|
headers: (params) => resolveAttributionHeaders(config2, params),
|
|
1727
2770
|
modelForCapabilities: modelForOpenAICapabilities,
|
|
@@ -1736,7 +2779,7 @@ var openrouterResponses = (config2) => {
|
|
|
1736
2779
|
const { allowedModels, allowedPresets } = snapshotPolicy(config2);
|
|
1737
2780
|
const provider = openaiResponses({
|
|
1738
2781
|
apiKey: config2.apiKey,
|
|
1739
|
-
baseUrl: config2.baseUrl ??
|
|
2782
|
+
baseUrl: config2.baseUrl ?? DEFAULT_BASE_URL5,
|
|
1740
2783
|
fetch: config2.fetch,
|
|
1741
2784
|
headers: (params) => resolveAttributionHeaders(config2, params),
|
|
1742
2785
|
modelForCapabilities: modelForOpenAICapabilities,
|
|
@@ -1746,12 +2789,35 @@ var openrouterResponses = (config2) => {
|
|
|
1746
2789
|
});
|
|
1747
2790
|
return withOpenRouterPolicy(provider, allowedModels, allowedPresets);
|
|
1748
2791
|
};
|
|
2792
|
+
var openrouterMessages = (config2) => {
|
|
2793
|
+
assertRoutingPolicy(config2);
|
|
2794
|
+
const { allowedModels, allowedPresets } = snapshotPolicy(config2);
|
|
2795
|
+
const provider = anthropic({
|
|
2796
|
+
apiKey: config2.apiKey,
|
|
2797
|
+
authStyle: "bearer",
|
|
2798
|
+
baseUrl: config2.baseUrl ?? DEFAULT_BASE_URL5,
|
|
2799
|
+
fetch: config2.fetch,
|
|
2800
|
+
headers: (params) => resolveAttributionHeaders(config2, params),
|
|
2801
|
+
providerName: "openrouter",
|
|
2802
|
+
tokenSource: config2.tokenSource,
|
|
2803
|
+
transformRequestBody: (body, params) => transformOpenRouterRequest(config2, allowedModels, allowedPresets, body, params, "messages")
|
|
2804
|
+
});
|
|
2805
|
+
return withOpenRouterPolicy(provider, allowedModels, allowedPresets);
|
|
2806
|
+
};
|
|
1749
2807
|
export {
|
|
2808
|
+
verifyOpenRouterWebhookSignature,
|
|
1750
2809
|
openrouterResponses,
|
|
2810
|
+
openrouterMessages,
|
|
1751
2811
|
openrouter,
|
|
1752
2812
|
openRouterModelMatchesRule,
|
|
1753
|
-
|
|
2813
|
+
generateOpenRouterPKCE,
|
|
2814
|
+
exchangeOpenRouterAuthCode,
|
|
2815
|
+
estimateOpenRouterModelCost,
|
|
2816
|
+
estimateOpenRouterCost,
|
|
2817
|
+
createOpenRouterKeyLinks,
|
|
2818
|
+
createOpenRouterClient,
|
|
2819
|
+
createOpenRouterAuthorizationUrl
|
|
1754
2820
|
};
|
|
1755
2821
|
|
|
1756
|
-
//# debugId=
|
|
2822
|
+
//# debugId=FABF06EDFC815F9A64756E2164756E21
|
|
1757
2823
|
//# sourceMappingURL=openrouter.js.map
|