@absolutejs/ai 0.0.49 → 0.0.51
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +127 -0
- package/dist/ai/client/index.js +30 -1
- package/dist/ai/client/index.js.map +5 -5
- package/dist/ai/index.js +1174 -92
- package/dist/ai/index.js.map +18 -16
- package/dist/ai/providers/anthropic.js +105 -26
- package/dist/ai/providers/anthropic.js.map +5 -5
- package/dist/ai/providers/gemini.js +37 -4
- package/dist/ai/providers/gemini.js.map +5 -5
- package/dist/ai/providers/ollama.js +7 -3
- package/dist/ai/providers/ollama.js.map +4 -4
- package/dist/ai/providers/openai.js +177 -36
- package/dist/ai/providers/openai.js.map +5 -5
- package/dist/ai/providers/openaiCompatible.js +177 -36
- package/dist/ai/providers/openaiCompatible.js.map +5 -5
- package/dist/ai/providers/openaiResponses.js +147 -33
- package/dist/ai/providers/openaiResponses.js.map +5 -5
- package/dist/ai/providers/openrouter.js +2607 -0
- package/dist/ai/providers/openrouter.js.map +18 -0
- 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/generateAI.d.ts +4 -1
- package/dist/src/ai/index.d.ts +2 -0
- package/dist/src/ai/providers/openai.d.ts +7 -3
- package/dist/src/ai/providers/openaiResponses.d.ts +9 -4
- package/dist/src/ai/providers/openrouter.d.ts +157 -0
- package/dist/src/ai/providers/openrouterClient.d.ts +276 -0
- 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 +116 -8
- 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 +9 -2
package/dist/ai/index.js
CHANGED
|
@@ -6,7 +6,8 @@ var PROVIDER_STATUS_PAGES = {
|
|
|
6
6
|
anthropic: "https://status.claude.com",
|
|
7
7
|
gemini: "https://status.cloud.google.com",
|
|
8
8
|
google: "https://status.cloud.google.com",
|
|
9
|
-
openai: "https://status.openai.com"
|
|
9
|
+
openai: "https://status.openai.com",
|
|
10
|
+
openrouter: "https://status.openrouter.ai"
|
|
10
11
|
};
|
|
11
12
|
var RETRYABLE_STATUSES = new Set([
|
|
12
13
|
408,
|
|
@@ -42,6 +43,7 @@ class ProviderError extends Error {
|
|
|
42
43
|
status;
|
|
43
44
|
type;
|
|
44
45
|
retryable;
|
|
46
|
+
metadata;
|
|
45
47
|
statusPageUrl;
|
|
46
48
|
constructor(init) {
|
|
47
49
|
super(init.message, init.cause === undefined ? undefined : { cause: init.cause });
|
|
@@ -50,6 +52,7 @@ class ProviderError extends Error {
|
|
|
50
52
|
this.status = init.status ?? null;
|
|
51
53
|
this.type = init.type ?? null;
|
|
52
54
|
this.retryable = init.retryable;
|
|
55
|
+
this.metadata = init.metadata;
|
|
53
56
|
this.statusPageUrl = providerStatusPage(init.provider);
|
|
54
57
|
}
|
|
55
58
|
static fromResponse(provider, status, body, type) {
|
|
@@ -222,7 +225,8 @@ var withResilience = (provider, providerName = "unknown") => {
|
|
|
222
225
|
yield* attempt(params, attemptNo + 1);
|
|
223
226
|
return;
|
|
224
227
|
}
|
|
225
|
-
|
|
228
|
+
if (providerError.retryable)
|
|
229
|
+
noteFailure(providerName, providerError);
|
|
226
230
|
throw providerError;
|
|
227
231
|
}
|
|
228
232
|
};
|
|
@@ -456,7 +460,7 @@ var mapContentBlockToOpenAI = (block) => {
|
|
|
456
460
|
if (block.type === "image") {
|
|
457
461
|
return {
|
|
458
462
|
image_url: {
|
|
459
|
-
url: `data:${block.source.media_type};base64,${block.source.data}`
|
|
463
|
+
url: block.source.type === "url" ? block.source.url : `data:${block.source.media_type};base64,${block.source.data}`
|
|
460
464
|
},
|
|
461
465
|
type: "image_url"
|
|
462
466
|
};
|
|
@@ -464,12 +468,26 @@ var mapContentBlockToOpenAI = (block) => {
|
|
|
464
468
|
if (block.type === "document") {
|
|
465
469
|
return {
|
|
466
470
|
file: {
|
|
467
|
-
file_data: `data:${block.source.media_type};base64,${block.source.data}`,
|
|
471
|
+
file_data: block.source.type === "url" ? block.source.url : `data:${block.source.media_type};base64,${block.source.data}`,
|
|
468
472
|
filename: block.name ?? "document.pdf"
|
|
469
473
|
},
|
|
470
474
|
type: "file"
|
|
471
475
|
};
|
|
472
476
|
}
|
|
477
|
+
if (block.type === "audio") {
|
|
478
|
+
return {
|
|
479
|
+
input_audio: { data: block.source.data, format: block.source.format },
|
|
480
|
+
type: "input_audio"
|
|
481
|
+
};
|
|
482
|
+
}
|
|
483
|
+
if (block.type === "video") {
|
|
484
|
+
return {
|
|
485
|
+
type: "video_url",
|
|
486
|
+
video_url: {
|
|
487
|
+
url: block.source.type === "url" ? block.source.url : `data:${block.source.media_type};base64,${block.source.data}`
|
|
488
|
+
}
|
|
489
|
+
};
|
|
490
|
+
}
|
|
473
491
|
if (block.type === "text") {
|
|
474
492
|
return { text: block.content, type: "text" };
|
|
475
493
|
}
|
|
@@ -479,17 +497,20 @@ var mapOpenAIContent = (msg) => {
|
|
|
479
497
|
if (typeof msg.content === "string") {
|
|
480
498
|
return msg.content;
|
|
481
499
|
}
|
|
482
|
-
const hasMedia = msg.content.some((block) => block.type === "image" || block.type === "document");
|
|
500
|
+
const hasMedia = msg.content.some((block) => block.type === "image" || block.type === "document" || block.type === "audio" || block.type === "video");
|
|
483
501
|
if (!hasMedia) {
|
|
484
502
|
return null;
|
|
485
503
|
}
|
|
486
504
|
return msg.content.map(mapContentBlockToOpenAI).filter((mapped) => mapped !== null);
|
|
487
505
|
};
|
|
488
|
-
var buildRequestBody = (params) => {
|
|
506
|
+
var buildRequestBody = (params, capabilityModel = params.model) => {
|
|
489
507
|
const messages = convertToolResultMessages(params.messages.map((msg) => ({
|
|
490
508
|
content: mapOpenAIContent(msg),
|
|
491
509
|
role: msg.role
|
|
492
510
|
})), params);
|
|
511
|
+
if (params.systemPrompt) {
|
|
512
|
+
messages.unshift({ content: params.systemPrompt, role: "system" });
|
|
513
|
+
}
|
|
493
514
|
const body = {
|
|
494
515
|
messages,
|
|
495
516
|
model: params.model,
|
|
@@ -510,12 +531,12 @@ var buildRequestBody = (params) => {
|
|
|
510
531
|
body.parallel_tool_calls = params.parallelToolCalls;
|
|
511
532
|
}
|
|
512
533
|
}
|
|
513
|
-
if (isOpenAIReasoningModel(
|
|
534
|
+
if (isOpenAIReasoningModel(capabilityModel)) {
|
|
514
535
|
if (typeof params.maxTokens === "number") {
|
|
515
536
|
body.max_completion_tokens = params.maxTokens;
|
|
516
537
|
}
|
|
517
538
|
if (params.reasoning) {
|
|
518
|
-
const effort = openaiEffortValue(
|
|
539
|
+
const effort = openaiEffortValue(capabilityModel, params.reasoning);
|
|
519
540
|
if (effort)
|
|
520
541
|
body.reasoning_effort = effort;
|
|
521
542
|
}
|
|
@@ -576,8 +597,12 @@ var extractUsage = (parsedUsage) => {
|
|
|
576
597
|
const cached = parsedUsage.cached_tokens ?? 0;
|
|
577
598
|
return {
|
|
578
599
|
cacheReadInputTokens: cached,
|
|
600
|
+
cacheWriteInputTokens: parsedUsage.cache_write_tokens || undefined,
|
|
601
|
+
costCredits: parsedUsage.cost,
|
|
579
602
|
inputTokens: Math.max(0, prompt - cached),
|
|
580
|
-
outputTokens: parsedUsage.completion_tokens ?? 0
|
|
603
|
+
outputTokens: parsedUsage.completion_tokens ?? 0,
|
|
604
|
+
reasoningTokens: parsedUsage.reasoning_tokens || undefined,
|
|
605
|
+
upstreamInferenceCostCredits: parsedUsage.upstream_inference_cost || undefined
|
|
581
606
|
};
|
|
582
607
|
};
|
|
583
608
|
var resolveToolCallIndex = (toolCall) => {
|
|
@@ -629,9 +654,50 @@ var processDelta = function* (delta, pendingToolCalls) {
|
|
|
629
654
|
if (typeof delta.content === "string") {
|
|
630
655
|
yield { content: delta.content, type: "text" };
|
|
631
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
|
+
}
|
|
632
669
|
if (isRecordArray(delta.tool_calls)) {
|
|
633
670
|
processToolCallDeltas(delta.tool_calls, pendingToolCalls);
|
|
634
671
|
}
|
|
672
|
+
if (Array.isArray(delta.annotations)) {
|
|
673
|
+
for (const annotation of delta.annotations) {
|
|
674
|
+
if (!isRecord(annotation) || annotation.type !== "url_citation")
|
|
675
|
+
continue;
|
|
676
|
+
const citation = isRecord(annotation.url_citation) ? annotation.url_citation : annotation;
|
|
677
|
+
if (typeof citation.url !== "string")
|
|
678
|
+
continue;
|
|
679
|
+
yield {
|
|
680
|
+
content: typeof citation.content === "string" ? citation.content : undefined,
|
|
681
|
+
endIndex: typeof citation.end_index === "number" ? citation.end_index : undefined,
|
|
682
|
+
startIndex: typeof citation.start_index === "number" ? citation.start_index : undefined,
|
|
683
|
+
title: typeof citation.title === "string" ? citation.title : undefined,
|
|
684
|
+
type: "citation",
|
|
685
|
+
url: citation.url
|
|
686
|
+
};
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
};
|
|
690
|
+
var narrowResponseMetadata = (parsed) => {
|
|
691
|
+
const providerMetadata = isRecord(parsed.openrouter_metadata) ? parsed.openrouter_metadata : undefined;
|
|
692
|
+
const generationId = typeof parsed.id === "string" ? parsed.id : undefined;
|
|
693
|
+
const model = typeof parsed.model === "string" ? parsed.model : undefined;
|
|
694
|
+
const serviceTier = typeof parsed.service_tier === "string" ? parsed.service_tier : undefined;
|
|
695
|
+
const selected = providerMetadata && isRecord(providerMetadata.endpoints) ? providerMetadata.endpoints.available : undefined;
|
|
696
|
+
const selectedEndpoint = Array.isArray(selected) ? selected.find((entry) => isRecord(entry) && entry.selected === true) : undefined;
|
|
697
|
+
const provider = isRecord(selectedEndpoint) && typeof selectedEndpoint.provider === "string" ? selectedEndpoint.provider : undefined;
|
|
698
|
+
if (!providerMetadata && !generationId && !model && !serviceTier)
|
|
699
|
+
return;
|
|
700
|
+
return { generationId, model, provider, providerMetadata, serviceTier };
|
|
635
701
|
};
|
|
636
702
|
var processChoice = function* (choice, pendingToolCalls) {
|
|
637
703
|
const delta = isRecord(choice.delta) ? choice.delta : null;
|
|
@@ -650,13 +716,25 @@ var narrowUsageRecord = (parsed) => {
|
|
|
650
716
|
const promptTokens = typeof usage.prompt_tokens === "number" ? usage.prompt_tokens : 0;
|
|
651
717
|
const completionTokens = typeof usage.completion_tokens === "number" ? usage.completion_tokens : 0;
|
|
652
718
|
const cachedTokens = isRecord(usage.prompt_tokens_details) && typeof usage.prompt_tokens_details.cached_tokens === "number" ? usage.prompt_tokens_details.cached_tokens : 0;
|
|
653
|
-
|
|
719
|
+
const cacheWriteTokens = isRecord(usage.prompt_tokens_details) && typeof usage.prompt_tokens_details.cache_write_tokens === "number" ? usage.prompt_tokens_details.cache_write_tokens : 0;
|
|
720
|
+
const reasoningTokens = isRecord(usage.completion_tokens_details) && typeof usage.completion_tokens_details.reasoning_tokens === "number" ? usage.completion_tokens_details.reasoning_tokens : 0;
|
|
721
|
+
const cost = typeof usage.cost === "number" ? usage.cost : undefined;
|
|
722
|
+
const upstreamInferenceCost = isRecord(usage.cost_details) && typeof usage.cost_details.upstream_inference_cost === "number" ? usage.cost_details.upstream_inference_cost : undefined;
|
|
723
|
+
const normalized = extractUsage({
|
|
724
|
+
cache_write_tokens: cacheWriteTokens,
|
|
654
725
|
cached_tokens: cachedTokens,
|
|
655
726
|
completion_tokens: completionTokens,
|
|
656
|
-
|
|
727
|
+
cost,
|
|
728
|
+
prompt_tokens: promptTokens,
|
|
729
|
+
reasoning_tokens: reasoningTokens,
|
|
730
|
+
upstream_inference_cost: upstreamInferenceCost
|
|
657
731
|
});
|
|
732
|
+
if (isRecord(usage.server_tool_use)) {
|
|
733
|
+
normalized.serverToolUse = Object.fromEntries(Object.entries(usage.server_tool_use).filter((entry) => typeof entry[1] === "number"));
|
|
734
|
+
}
|
|
735
|
+
return normalized;
|
|
658
736
|
};
|
|
659
|
-
var processSSELine = function* (line, pendingToolCalls,
|
|
737
|
+
var processSSELine = function* (line, pendingToolCalls, providerName) {
|
|
660
738
|
const trimmed = line.trim();
|
|
661
739
|
if (!trimmed || !trimmed.startsWith("data: ")) {
|
|
662
740
|
return;
|
|
@@ -664,7 +742,6 @@ var processSSELine = function* (line, pendingToolCalls, currentUsage) {
|
|
|
664
742
|
const data = trimmed.slice(SSE_DATA_PREFIX_LENGTH);
|
|
665
743
|
if (data === DONE_SENTINEL) {
|
|
666
744
|
yield* flushPendingToolCalls(pendingToolCalls);
|
|
667
|
-
yield { type: "done", usage: currentUsage };
|
|
668
745
|
return;
|
|
669
746
|
}
|
|
670
747
|
let parsed;
|
|
@@ -673,10 +750,28 @@ var processSSELine = function* (line, pendingToolCalls, currentUsage) {
|
|
|
673
750
|
} catch {
|
|
674
751
|
return;
|
|
675
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
|
+
}
|
|
676
767
|
const usageUpdate = narrowUsageRecord(parsed);
|
|
677
768
|
if (usageUpdate) {
|
|
678
769
|
yield { type: "usage_update", usage: usageUpdate };
|
|
679
770
|
}
|
|
771
|
+
const metadata = narrowResponseMetadata(parsed);
|
|
772
|
+
if (metadata) {
|
|
773
|
+
yield { metadata, type: "response_metadata" };
|
|
774
|
+
}
|
|
680
775
|
const { choices } = parsed;
|
|
681
776
|
if (!isRecordArray(choices)) {
|
|
682
777
|
return;
|
|
@@ -685,21 +780,46 @@ var processSSELine = function* (line, pendingToolCalls, currentUsage) {
|
|
|
685
780
|
if (!firstChoice) {
|
|
686
781
|
return;
|
|
687
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
|
+
}
|
|
688
796
|
yield* processChoice(firstChoice, pendingToolCalls);
|
|
689
797
|
};
|
|
690
798
|
var isUsageUpdate = (chunk) => chunk.type === "usage_update";
|
|
691
|
-
var collectYieldableChunks = (line, pendingToolCalls, usageRef) => {
|
|
692
|
-
const allChunks = Array.from(processSSELine(line, pendingToolCalls,
|
|
799
|
+
var collectYieldableChunks = (line, pendingToolCalls, usageRef, metadataRef, providerName) => {
|
|
800
|
+
const allChunks = Array.from(processSSELine(line, pendingToolCalls, providerName));
|
|
693
801
|
const usageChunks = allChunks.filter(isUsageUpdate);
|
|
694
802
|
const lastUsage = usageChunks.at(NOT_FOUND);
|
|
695
803
|
if (lastUsage) {
|
|
696
804
|
usageRef.current = lastUsage.usage;
|
|
697
805
|
}
|
|
698
|
-
|
|
806
|
+
const metadataChunks = allChunks.filter((chunk) => chunk.type === "response_metadata");
|
|
807
|
+
const lastMetadata = metadataChunks.at(NOT_FOUND);
|
|
808
|
+
if (lastMetadata && "metadata" in lastMetadata) {
|
|
809
|
+
metadataRef.current = {
|
|
810
|
+
...metadataRef.current,
|
|
811
|
+
...lastMetadata.metadata,
|
|
812
|
+
providerMetadata: {
|
|
813
|
+
...metadataRef.current?.providerMetadata,
|
|
814
|
+
...lastMetadata.metadata.providerMetadata
|
|
815
|
+
}
|
|
816
|
+
};
|
|
817
|
+
}
|
|
818
|
+
return allChunks.filter((chunk) => !isUsageUpdate(chunk) && chunk.type !== "response_metadata");
|
|
699
819
|
};
|
|
700
|
-
var processSSELines = function* (lines, pendingToolCalls, usageRef) {
|
|
820
|
+
var processSSELines = function* (lines, pendingToolCalls, usageRef, metadataRef, providerName) {
|
|
701
821
|
for (const line of lines) {
|
|
702
|
-
yield* collectYieldableChunks(line, pendingToolCalls, usageRef);
|
|
822
|
+
yield* collectYieldableChunks(line, pendingToolCalls, usageRef, metadataRef, providerName);
|
|
703
823
|
}
|
|
704
824
|
};
|
|
705
825
|
var processStreamValue = (value, decoder, state) => {
|
|
@@ -712,51 +832,67 @@ var processStreamValue = (value, decoder, state) => {
|
|
|
712
832
|
var drainReader = async function* (reader, decoder, state, signal) {
|
|
713
833
|
for (let result = await reader.read();!result.done && !signal?.aborted; result = await reader.read()) {
|
|
714
834
|
const lines = processStreamValue(result.value, decoder, state);
|
|
715
|
-
yield* processSSELines(lines, state.pendingToolCalls, state.usageRef);
|
|
835
|
+
yield* processSSELines(lines, state.pendingToolCalls, state.usageRef, state.metadataRef, state.providerName);
|
|
716
836
|
}
|
|
717
837
|
};
|
|
718
|
-
var parseSSEStream = async function* (body, signal) {
|
|
838
|
+
var parseSSEStream = async function* (body, initialMetadata, providerName = "openai", signal) {
|
|
719
839
|
const reader = body.getReader();
|
|
720
840
|
const decoder = new TextDecoder;
|
|
721
841
|
const state = {
|
|
722
842
|
buffer: "",
|
|
843
|
+
metadataRef: { current: initialMetadata },
|
|
723
844
|
pendingToolCalls: new Map,
|
|
845
|
+
providerName,
|
|
724
846
|
usageRef: { current: undefined }
|
|
725
847
|
};
|
|
726
848
|
try {
|
|
727
849
|
yield* drainReader(reader, decoder, state, signal);
|
|
728
|
-
yield {
|
|
850
|
+
yield {
|
|
851
|
+
metadata: state.metadataRef.current,
|
|
852
|
+
type: "done",
|
|
853
|
+
usage: state.usageRef.current
|
|
854
|
+
};
|
|
729
855
|
} finally {
|
|
730
856
|
reader.releaseLock();
|
|
731
857
|
}
|
|
732
858
|
};
|
|
733
|
-
var fetchOpenAIStream = async function* (baseUrl, apiKey, body, signal) {
|
|
859
|
+
var fetchOpenAIStream = async function* (baseUrl, apiKey, body, fetchImpl, headers, providerName, signal) {
|
|
734
860
|
const target = `${baseUrl}/v1/chat/completions`;
|
|
735
|
-
const
|
|
861
|
+
const requestHeaders = new Headers(headers);
|
|
862
|
+
requestHeaders.set("Authorization", `Bearer ${apiKey}`);
|
|
863
|
+
requestHeaders.set("Content-Type", "application/json");
|
|
864
|
+
const response = await fetchImpl(target, {
|
|
736
865
|
...h2IfHttps(target),
|
|
737
866
|
body: JSON.stringify(body),
|
|
738
|
-
headers:
|
|
739
|
-
Authorization: `Bearer ${apiKey}`,
|
|
740
|
-
"Content-Type": "application/json"
|
|
741
|
-
},
|
|
867
|
+
headers: requestHeaders,
|
|
742
868
|
method: "POST",
|
|
743
869
|
signal
|
|
744
870
|
});
|
|
745
871
|
if (!response.ok) {
|
|
746
872
|
const errorText = await response.text();
|
|
747
|
-
throw ProviderError.fromResponse(
|
|
873
|
+
throw ProviderError.fromResponse(providerName, response.status, errorText);
|
|
748
874
|
}
|
|
749
875
|
if (!response.body) {
|
|
750
876
|
throw new ProviderError({
|
|
751
|
-
message:
|
|
752
|
-
provider:
|
|
877
|
+
message: `${providerName} API returned no response body`,
|
|
878
|
+
provider: providerName,
|
|
753
879
|
retryable: true
|
|
754
880
|
});
|
|
755
881
|
}
|
|
756
|
-
yield* parseSSEStream(response.body,
|
|
882
|
+
yield* parseSSEStream(response.body, {
|
|
883
|
+
generationId: response.headers.get("X-Generation-Id") ?? undefined,
|
|
884
|
+
providerMetadata: {
|
|
885
|
+
cacheAge: response.headers.get("X-OpenRouter-Cache-Age") ?? undefined,
|
|
886
|
+
cacheSourceId: response.headers.get("X-OpenRouter-Cache-Source-Id") ?? undefined,
|
|
887
|
+
cacheStatus: response.headers.get("X-OpenRouter-Cache-Status") ?? undefined,
|
|
888
|
+
cacheTtl: response.headers.get("X-OpenRouter-Cache-TTL") ?? undefined
|
|
889
|
+
}
|
|
890
|
+
}, providerName, signal);
|
|
757
891
|
};
|
|
758
892
|
var openai = (config2) => {
|
|
759
893
|
const baseUrl = config2.baseUrl ?? DEFAULT_BASE_URL;
|
|
894
|
+
const fetchImpl = config2.fetch ?? globalThis.fetch;
|
|
895
|
+
const providerName = config2.providerName ?? "openai";
|
|
760
896
|
if (!config2.apiKey && !config2.tokenSource) {
|
|
761
897
|
throw new Error("openai() requires either apiKey or tokenSource");
|
|
762
898
|
}
|
|
@@ -766,15 +902,20 @@ var openai = (config2) => {
|
|
|
766
902
|
}
|
|
767
903
|
return config2.apiKey;
|
|
768
904
|
};
|
|
905
|
+
const resolveHeaders = async (params) => typeof config2.headers === "function" ? await config2.headers(params) : config2.headers ?? {};
|
|
769
906
|
return instrumentAIProvider({
|
|
770
907
|
stream: (params) => {
|
|
771
|
-
const
|
|
908
|
+
const openaiBody = buildRequestBody(params, config2.modelForCapabilities?.(params.model) ?? params.model);
|
|
909
|
+
const body = config2.transformRequestBody ? config2.transformRequestBody(openaiBody, params) : openaiBody;
|
|
772
910
|
return async function* () {
|
|
773
|
-
const apiKey = await
|
|
774
|
-
|
|
911
|
+
const [apiKey, headers] = await Promise.all([
|
|
912
|
+
resolveKey(),
|
|
913
|
+
resolveHeaders(params)
|
|
914
|
+
]);
|
|
915
|
+
yield* fetchOpenAIStream(baseUrl, apiKey, body, fetchImpl, headers, providerName, params.signal);
|
|
775
916
|
}();
|
|
776
917
|
}
|
|
777
|
-
},
|
|
918
|
+
}, providerName);
|
|
778
919
|
};
|
|
779
920
|
|
|
780
921
|
// src/ai/providers/openaiCompatible.ts
|
|
@@ -826,7 +967,7 @@ var mapBlockToResponsesFormat = (block) => {
|
|
|
826
967
|
if (block.type === "image") {
|
|
827
968
|
return {
|
|
828
969
|
image_url: {
|
|
829
|
-
url: `data:${block.source.media_type};base64,${block.source.data}`
|
|
970
|
+
url: block.source.type === "url" ? block.source.url : `data:${block.source.media_type};base64,${block.source.data}`
|
|
830
971
|
},
|
|
831
972
|
type: "input_image"
|
|
832
973
|
};
|
|
@@ -834,12 +975,24 @@ var mapBlockToResponsesFormat = (block) => {
|
|
|
834
975
|
if (block.type === "document") {
|
|
835
976
|
return {
|
|
836
977
|
file: {
|
|
837
|
-
file_data: `data:${block.source.media_type};base64,${block.source.data}`,
|
|
978
|
+
file_data: block.source.type === "url" ? block.source.url : `data:${block.source.media_type};base64,${block.source.data}`,
|
|
838
979
|
filename: block.name ?? "document.pdf"
|
|
839
980
|
},
|
|
840
981
|
type: "input_file"
|
|
841
982
|
};
|
|
842
983
|
}
|
|
984
|
+
if (block.type === "audio") {
|
|
985
|
+
return {
|
|
986
|
+
input_audio: { data: block.source.data, format: block.source.format },
|
|
987
|
+
type: "input_audio"
|
|
988
|
+
};
|
|
989
|
+
}
|
|
990
|
+
if (block.type === "video") {
|
|
991
|
+
return {
|
|
992
|
+
type: "input_video",
|
|
993
|
+
video_url: block.source.type === "url" ? block.source.url : `data:${block.source.media_type};base64,${block.source.data}`
|
|
994
|
+
};
|
|
995
|
+
}
|
|
843
996
|
return null;
|
|
844
997
|
};
|
|
845
998
|
var mapContentToResponsesFormat = (content) => {
|
|
@@ -851,7 +1004,12 @@ var mapContentToResponsesFormat = (content) => {
|
|
|
851
1004
|
};
|
|
852
1005
|
var hasToolBlocks = (content) => content.some((block) => block.type === "tool_use" || block.type === "tool_result");
|
|
853
1006
|
var convertToolBlock = (block) => {
|
|
1007
|
+
if (block.type === "provider_data" && block.provider === "openrouter") {
|
|
1008
|
+
return { ...block.data };
|
|
1009
|
+
}
|
|
854
1010
|
if (block.type === "tool_use") {
|
|
1011
|
+
if (block.providerData)
|
|
1012
|
+
return { ...block.providerData };
|
|
855
1013
|
return {
|
|
856
1014
|
arguments: typeof block.input === "string" ? block.input : JSON.stringify(block.input),
|
|
857
1015
|
call_id: block.id,
|
|
@@ -903,7 +1061,7 @@ var buildTools = (tools, isImageModel) => {
|
|
|
903
1061
|
}
|
|
904
1062
|
return result.length > 0 ? result : undefined;
|
|
905
1063
|
};
|
|
906
|
-
var buildRequestBody2 = (params, isImageModel) => {
|
|
1064
|
+
var buildRequestBody2 = (params, isImageModel, capabilityModel = params.model) => {
|
|
907
1065
|
const body = {
|
|
908
1066
|
input: buildInput(params.messages),
|
|
909
1067
|
model: params.model,
|
|
@@ -955,8 +1113,8 @@ var buildRequestBody2 = (params, isImageModel) => {
|
|
|
955
1113
|
};
|
|
956
1114
|
}
|
|
957
1115
|
}
|
|
958
|
-
if (params.reasoning && isOpenAIReasoningModel(
|
|
959
|
-
const effort = openaiEffortValue(
|
|
1116
|
+
if (params.reasoning && isOpenAIReasoningModel(capabilityModel)) {
|
|
1117
|
+
const effort = openaiEffortValue(capabilityModel, params.reasoning);
|
|
960
1118
|
if (effort) {
|
|
961
1119
|
body.reasoning = {
|
|
962
1120
|
effort,
|
|
@@ -987,11 +1145,32 @@ var extractUsage2 = (response) => {
|
|
|
987
1145
|
const { usage } = response;
|
|
988
1146
|
const input = typeof usage.input_tokens === "number" ? usage.input_tokens : 0;
|
|
989
1147
|
const cached = isRecord2(usage.input_tokens_details) && typeof usage.input_tokens_details.cached_tokens === "number" ? usage.input_tokens_details.cached_tokens : 0;
|
|
990
|
-
|
|
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 = {
|
|
991
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,
|
|
992
1155
|
inputTokens: Math.max(0, input - cached),
|
|
993
|
-
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
|
|
994
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 };
|
|
995
1174
|
};
|
|
996
1175
|
var extractMimeFormat = (mimeType) => {
|
|
997
1176
|
if (typeof mimeType !== "string") {
|
|
@@ -1048,6 +1227,7 @@ var processFunctionCallArgumentsDone = function* (parsed, pendingCalls) {
|
|
|
1048
1227
|
id: callId || pending?.callId || itemId,
|
|
1049
1228
|
input: parseToolInput2(args),
|
|
1050
1229
|
name,
|
|
1230
|
+
providerData: pending?.providerData ? { ...pending.providerData, arguments: args } : undefined,
|
|
1051
1231
|
type: "tool_use"
|
|
1052
1232
|
};
|
|
1053
1233
|
};
|
|
@@ -1066,9 +1246,21 @@ var processOutputItemAdded = (parsed, pendingCalls) => {
|
|
|
1066
1246
|
pendingCalls.set(itemId, {
|
|
1067
1247
|
arguments: "",
|
|
1068
1248
|
callId,
|
|
1069
|
-
name
|
|
1249
|
+
name,
|
|
1250
|
+
providerData: { ...item }
|
|
1070
1251
|
});
|
|
1071
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
|
+
};
|
|
1072
1264
|
var isCompletedImageGeneration = (item) => item.type === "image_generation_call" && item.status === "completed" && typeof item.result === "string" && item.result !== "";
|
|
1073
1265
|
var buildImageChunk = (item) => ({
|
|
1074
1266
|
data: typeof item.result === "string" ? item.result : "",
|
|
@@ -1084,6 +1276,30 @@ var extractImageFromOutput = function* (output) {
|
|
|
1084
1276
|
yield buildImageChunk(item);
|
|
1085
1277
|
}
|
|
1086
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
|
+
};
|
|
1087
1303
|
var processCompleted = function* (parsed) {
|
|
1088
1304
|
if (!isRecord2(parsed.response)) {
|
|
1089
1305
|
yield { type: "done", usage: undefined };
|
|
@@ -1091,12 +1307,26 @@ var processCompleted = function* (parsed) {
|
|
|
1091
1307
|
}
|
|
1092
1308
|
const { response } = parsed;
|
|
1093
1309
|
const usage = extractUsage2(response);
|
|
1310
|
+
const metadata = extractResponseMetadata(response);
|
|
1094
1311
|
if (isRecordArray2(response.output)) {
|
|
1312
|
+
yield* extractCitationsFromOutput(response.output);
|
|
1095
1313
|
yield* extractImageFromOutput(response.output);
|
|
1096
1314
|
}
|
|
1097
|
-
yield { type: "done", usage };
|
|
1315
|
+
yield { metadata, type: "done", usage };
|
|
1316
|
+
};
|
|
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
|
+
});
|
|
1098
1328
|
};
|
|
1099
|
-
var processSSEEvent = function* (eventType, parsed, pendingCalls) {
|
|
1329
|
+
var processSSEEvent = function* (eventType, parsed, pendingCalls, providerName) {
|
|
1100
1330
|
switch (eventType) {
|
|
1101
1331
|
case "response.reasoning_summary_text.delta": {
|
|
1102
1332
|
const delta = typeof parsed.delta === "string" ? parsed.delta : "";
|
|
@@ -1117,6 +1347,9 @@ var processSSEEvent = function* (eventType, parsed, pendingCalls) {
|
|
|
1117
1347
|
case "response.output_item.added":
|
|
1118
1348
|
processOutputItemAdded(parsed, pendingCalls);
|
|
1119
1349
|
break;
|
|
1350
|
+
case "response.output_item.done":
|
|
1351
|
+
yield* processOutputItemDone(parsed);
|
|
1352
|
+
break;
|
|
1120
1353
|
case "response.function_call_arguments.delta":
|
|
1121
1354
|
processFunctionCallArgumentsDelta(parsed, pendingCalls);
|
|
1122
1355
|
break;
|
|
@@ -1127,11 +1360,10 @@ var processSSEEvent = function* (eventType, parsed, pendingCalls) {
|
|
|
1127
1360
|
yield* processCompleted(parsed);
|
|
1128
1361
|
break;
|
|
1129
1362
|
case "response.failed":
|
|
1130
|
-
case "response.incomplete":
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
throw
|
|
1134
|
-
}
|
|
1363
|
+
case "response.incomplete":
|
|
1364
|
+
case "response.error":
|
|
1365
|
+
case "error":
|
|
1366
|
+
throw responseFailure(eventType, parsed, providerName);
|
|
1135
1367
|
}
|
|
1136
1368
|
};
|
|
1137
1369
|
var flushSSEBuffer = function* (state) {
|
|
@@ -1140,7 +1372,7 @@ var flushSSEBuffer = function* (state) {
|
|
|
1140
1372
|
}
|
|
1141
1373
|
const parsed = parseJSON(state.buffer);
|
|
1142
1374
|
if (parsed) {
|
|
1143
|
-
yield* processSSEEvent(state.currentEvent, parsed, state.pendingCalls);
|
|
1375
|
+
yield* processSSEEvent(state.currentEvent, parsed, state.pendingCalls, state.providerName);
|
|
1144
1376
|
}
|
|
1145
1377
|
state.currentEvent = "";
|
|
1146
1378
|
state.buffer = "";
|
|
@@ -1178,41 +1410,47 @@ var drainReader2 = async function* (reader, decoder, state, signal) {
|
|
|
1178
1410
|
yield* processSSELines2([textBuffer, ""], state);
|
|
1179
1411
|
}
|
|
1180
1412
|
};
|
|
1181
|
-
var parseSSEStream2 = async function* (body, signal) {
|
|
1413
|
+
var parseSSEStream2 = async function* (body, providerName, signal) {
|
|
1182
1414
|
const reader = body.getReader();
|
|
1183
1415
|
const decoder = new TextDecoder;
|
|
1184
1416
|
const state = {
|
|
1185
1417
|
buffer: "",
|
|
1186
1418
|
currentEvent: "",
|
|
1187
1419
|
pendingCalls: new Map,
|
|
1188
|
-
usage: undefined
|
|
1420
|
+
usage: undefined,
|
|
1421
|
+
providerName
|
|
1189
1422
|
};
|
|
1190
1423
|
try {
|
|
1191
1424
|
yield* drainReader2(reader, decoder, state, signal);
|
|
1425
|
+
yield* flushSSEBuffer(state);
|
|
1192
1426
|
} finally {
|
|
1193
1427
|
reader.releaseLock();
|
|
1194
1428
|
}
|
|
1195
1429
|
};
|
|
1196
|
-
var fetchResponsesStream = async function* (baseUrl, apiKey, body, signal) {
|
|
1430
|
+
var fetchResponsesStream = async function* (baseUrl, apiKey, body, fetchImpl, headers, providerName, signal) {
|
|
1197
1431
|
const target = `${baseUrl}/v1/responses`;
|
|
1198
|
-
const
|
|
1432
|
+
const requestHeaders = new Headers(headers);
|
|
1433
|
+
requestHeaders.set("Authorization", `Bearer ${apiKey}`);
|
|
1434
|
+
requestHeaders.set("Content-Type", "application/json");
|
|
1435
|
+
const response = await fetchImpl(target, {
|
|
1199
1436
|
...h2IfHttps2(target),
|
|
1200
1437
|
body: JSON.stringify(body),
|
|
1201
|
-
headers:
|
|
1202
|
-
Authorization: `Bearer ${apiKey}`,
|
|
1203
|
-
"Content-Type": "application/json"
|
|
1204
|
-
},
|
|
1438
|
+
headers: requestHeaders,
|
|
1205
1439
|
method: "POST",
|
|
1206
1440
|
signal
|
|
1207
1441
|
});
|
|
1208
1442
|
if (!response.ok) {
|
|
1209
1443
|
const errorText = await response.text();
|
|
1210
|
-
throw
|
|
1444
|
+
throw ProviderError.fromResponse(providerName, response.status, errorText);
|
|
1211
1445
|
}
|
|
1212
1446
|
if (!response.body) {
|
|
1213
|
-
throw new
|
|
1447
|
+
throw new ProviderError({
|
|
1448
|
+
message: `${providerName} Responses API returned no response body`,
|
|
1449
|
+
provider: providerName,
|
|
1450
|
+
retryable: true
|
|
1451
|
+
});
|
|
1214
1452
|
}
|
|
1215
|
-
yield* parseSSEStream2(response.body, signal);
|
|
1453
|
+
yield* parseSSEStream2(response.body, providerName, signal);
|
|
1216
1454
|
};
|
|
1217
1455
|
var resolveImageModels = (imageModels) => {
|
|
1218
1456
|
if (!imageModels) {
|
|
@@ -1224,15 +1462,28 @@ var resolveImageModels = (imageModels) => {
|
|
|
1224
1462
|
return new Set(imageModels);
|
|
1225
1463
|
};
|
|
1226
1464
|
var openaiResponses = (config2) => {
|
|
1465
|
+
if (!config2.apiKey && !config2.tokenSource)
|
|
1466
|
+
throw new Error("openaiResponses() requires either apiKey or tokenSource");
|
|
1227
1467
|
const baseUrl = config2.baseUrl ?? DEFAULT_BASE_URL2;
|
|
1468
|
+
const fetchImpl = config2.fetch ?? globalThis.fetch;
|
|
1228
1469
|
const imageModels = resolveImageModels(config2.imageModels);
|
|
1470
|
+
const providerName = config2.providerName ?? "openai-responses";
|
|
1471
|
+
const resolveKey = async () => config2.tokenSource ? await Promise.resolve(config2.tokenSource()) : config2.apiKey;
|
|
1472
|
+
const resolveHeaders = async (params) => typeof config2.headers === "function" ? await config2.headers(params) : config2.headers ?? {};
|
|
1229
1473
|
return instrumentAIProvider({
|
|
1230
1474
|
stream: (params) => {
|
|
1231
1475
|
const isImageModel = imageModels.has(params.model);
|
|
1232
|
-
const
|
|
1233
|
-
|
|
1476
|
+
const builtBody = buildRequestBody2(params, isImageModel, config2.modelForCapabilities?.(params.model) ?? params.model);
|
|
1477
|
+
const body = config2.transformRequestBody ? config2.transformRequestBody(builtBody, params) : builtBody;
|
|
1478
|
+
return async function* () {
|
|
1479
|
+
const [apiKey, headers] = await Promise.all([
|
|
1480
|
+
resolveKey(),
|
|
1481
|
+
resolveHeaders(params)
|
|
1482
|
+
]);
|
|
1483
|
+
yield* fetchResponsesStream(baseUrl, apiKey, body, fetchImpl, headers, providerName, params.signal);
|
|
1484
|
+
}();
|
|
1234
1485
|
}
|
|
1235
|
-
},
|
|
1486
|
+
}, providerName);
|
|
1236
1487
|
};
|
|
1237
1488
|
|
|
1238
1489
|
// src/ai/providers/gemini.ts
|
|
@@ -1251,14 +1502,43 @@ var mapContentBlock = (block) => {
|
|
|
1251
1502
|
case "text":
|
|
1252
1503
|
return { text: block.content };
|
|
1253
1504
|
case "image":
|
|
1254
|
-
return {
|
|
1505
|
+
return block.source.type === "url" ? {
|
|
1506
|
+
fileData: {
|
|
1507
|
+
fileUri: block.source.url,
|
|
1508
|
+
mimeType: block.source.media_type
|
|
1509
|
+
}
|
|
1510
|
+
} : {
|
|
1255
1511
|
inlineData: {
|
|
1256
1512
|
data: block.source.data,
|
|
1257
1513
|
mimeType: block.source.media_type
|
|
1258
1514
|
}
|
|
1259
1515
|
};
|
|
1260
1516
|
case "document":
|
|
1517
|
+
return block.source.type === "url" ? {
|
|
1518
|
+
fileData: {
|
|
1519
|
+
fileUri: block.source.url,
|
|
1520
|
+
mimeType: block.source.media_type
|
|
1521
|
+
}
|
|
1522
|
+
} : {
|
|
1523
|
+
inlineData: {
|
|
1524
|
+
data: block.source.data,
|
|
1525
|
+
mimeType: block.source.media_type
|
|
1526
|
+
}
|
|
1527
|
+
};
|
|
1528
|
+
case "audio":
|
|
1261
1529
|
return {
|
|
1530
|
+
inlineData: {
|
|
1531
|
+
data: block.source.data,
|
|
1532
|
+
mimeType: `audio/${block.source.format}`
|
|
1533
|
+
}
|
|
1534
|
+
};
|
|
1535
|
+
case "video":
|
|
1536
|
+
return block.source.type === "url" ? {
|
|
1537
|
+
fileData: {
|
|
1538
|
+
fileUri: block.source.url,
|
|
1539
|
+
mimeType: block.source.media_type
|
|
1540
|
+
}
|
|
1541
|
+
} : {
|
|
1262
1542
|
inlineData: {
|
|
1263
1543
|
data: block.source.data,
|
|
1264
1544
|
mimeType: block.source.media_type
|
|
@@ -1564,6 +1844,8 @@ var mapContentBlock2 = (block) => {
|
|
|
1564
1844
|
};
|
|
1565
1845
|
}
|
|
1566
1846
|
if (block.type === "tool_use") {
|
|
1847
|
+
if (block.providerData)
|
|
1848
|
+
return { ...block.providerData };
|
|
1567
1849
|
return {
|
|
1568
1850
|
id: block.id,
|
|
1569
1851
|
input: block.input,
|
|
@@ -1571,6 +1853,12 @@ var mapContentBlock2 = (block) => {
|
|
|
1571
1853
|
type: "tool_use"
|
|
1572
1854
|
};
|
|
1573
1855
|
}
|
|
1856
|
+
if (block.type === "audio" || block.type === "video") {
|
|
1857
|
+
throw new Error(`Anthropic does not support ${block.type} content blocks`);
|
|
1858
|
+
}
|
|
1859
|
+
if (block.type === "provider_data") {
|
|
1860
|
+
return { ...block.data };
|
|
1861
|
+
}
|
|
1574
1862
|
return { text: block.content, type: "text" };
|
|
1575
1863
|
};
|
|
1576
1864
|
var mapMessage = (msg) => ({
|
|
@@ -1744,11 +2032,15 @@ var handleContentBlockStart = (parsed, state) => {
|
|
|
1744
2032
|
state.currentToolName = getString(block, "name");
|
|
1745
2033
|
state.toolInputJson = "";
|
|
1746
2034
|
state.isThinkingBlock = false;
|
|
2035
|
+
state.currentProviderBlock = undefined;
|
|
1747
2036
|
} else if (block && block.type === "thinking") {
|
|
1748
2037
|
state.isThinkingBlock = true;
|
|
1749
2038
|
state.thinkingSignature = "";
|
|
2039
|
+
state.currentProviderBlock = undefined;
|
|
1750
2040
|
} else {
|
|
1751
2041
|
state.isThinkingBlock = false;
|
|
2042
|
+
state.currentProviderBlock = block && block.type !== "text" ? { ...block } : undefined;
|
|
2043
|
+
state.providerBlockInputJson = "";
|
|
1752
2044
|
}
|
|
1753
2045
|
};
|
|
1754
2046
|
var handleContentBlockDelta = (parsed, state) => {
|
|
@@ -1769,7 +2061,11 @@ var handleContentBlockDelta = (parsed, state) => {
|
|
|
1769
2061
|
};
|
|
1770
2062
|
}
|
|
1771
2063
|
if (delta.type === "input_json_delta") {
|
|
1772
|
-
state.
|
|
2064
|
+
if (state.currentProviderBlock) {
|
|
2065
|
+
state.providerBlockInputJson += getString(delta, "partial_json");
|
|
2066
|
+
} else {
|
|
2067
|
+
state.toolInputJson += getString(delta, "partial_json");
|
|
2068
|
+
}
|
|
1773
2069
|
}
|
|
1774
2070
|
if (delta.type === "signature_delta") {
|
|
1775
2071
|
state.thinkingSignature += getString(delta, "signature");
|
|
@@ -1787,6 +2083,19 @@ var handleContentBlockStop = (state) => {
|
|
|
1787
2083
|
type: "thinking"
|
|
1788
2084
|
};
|
|
1789
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
|
+
}
|
|
1790
2099
|
if (!state.currentToolId) {
|
|
1791
2100
|
return;
|
|
1792
2101
|
}
|
|
@@ -1806,11 +2115,39 @@ var extractUsage4 = (usageRecord, existingUsage) => {
|
|
|
1806
2115
|
if (!usageRecord) {
|
|
1807
2116
|
return existingUsage;
|
|
1808
2117
|
}
|
|
1809
|
-
|
|
2118
|
+
const normalized = {
|
|
1810
2119
|
cacheReadInputTokens: getNumber(usageRecord, "cache_read_input_tokens") || existingUsage?.cacheReadInputTokens || 0,
|
|
1811
2120
|
cacheWriteInputTokens: getNumber(usageRecord, "cache_creation_input_tokens") || existingUsage?.cacheWriteInputTokens || 0,
|
|
1812
2121
|
inputTokens: getNumber(usageRecord, "input_tokens") || existingUsage?.inputTokens || 0,
|
|
1813
|
-
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
|
|
1814
2151
|
};
|
|
1815
2152
|
};
|
|
1816
2153
|
var handleMessageDelta = (parsed, state) => {
|
|
@@ -1828,15 +2165,18 @@ var handleMessageStart = (parsed, state) => {
|
|
|
1828
2165
|
}
|
|
1829
2166
|
const startUsage = getRecord(message, "usage");
|
|
1830
2167
|
state.usage = extractUsage4(startUsage, state.usage);
|
|
2168
|
+
mergeMetadata(message, state);
|
|
1831
2169
|
};
|
|
1832
|
-
var handleError = (parsed) => {
|
|
2170
|
+
var handleError = (parsed, state) => {
|
|
1833
2171
|
const error = getRecord(parsed, "error");
|
|
1834
2172
|
const errorMessage = error ? getString(error, "message") : "";
|
|
1835
|
-
const
|
|
1836
|
-
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";
|
|
1837
2176
|
throw new ProviderError({
|
|
1838
2177
|
message: errorMessage || "Anthropic API error",
|
|
1839
|
-
|
|
2178
|
+
metadata: error,
|
|
2179
|
+
provider: state.providerName,
|
|
1840
2180
|
retryable,
|
|
1841
2181
|
type: errorType || null
|
|
1842
2182
|
});
|
|
@@ -1855,6 +2195,7 @@ var processEvent = (eventType, parsed, state) => {
|
|
|
1855
2195
|
}
|
|
1856
2196
|
case "message_delta": {
|
|
1857
2197
|
handleMessageDelta(parsed, state);
|
|
2198
|
+
mergeMetadata(parsed, state);
|
|
1858
2199
|
return;
|
|
1859
2200
|
}
|
|
1860
2201
|
case "message_start": {
|
|
@@ -1862,14 +2203,16 @@ var processEvent = (eventType, parsed, state) => {
|
|
|
1862
2203
|
return;
|
|
1863
2204
|
}
|
|
1864
2205
|
case "message_stop": {
|
|
2206
|
+
mergeMetadata(parsed, state);
|
|
1865
2207
|
return {
|
|
1866
2208
|
stopReason: state.stopReason,
|
|
2209
|
+
metadata: state.metadata,
|
|
1867
2210
|
type: "done",
|
|
1868
2211
|
usage: state.usage
|
|
1869
2212
|
};
|
|
1870
2213
|
}
|
|
1871
2214
|
case "error": {
|
|
1872
|
-
handleError(parsed);
|
|
2215
|
+
handleError(parsed, state);
|
|
1873
2216
|
return;
|
|
1874
2217
|
}
|
|
1875
2218
|
default: {
|
|
@@ -1934,7 +2277,7 @@ async function* streamChunks(reader, decoder, state, signal) {
|
|
|
1934
2277
|
yield* result.chunks;
|
|
1935
2278
|
}
|
|
1936
2279
|
}
|
|
1937
|
-
async function* parseSSEStream4(body, signal) {
|
|
2280
|
+
async function* parseSSEStream4(body, providerName, signal) {
|
|
1938
2281
|
const reader = body.getReader();
|
|
1939
2282
|
const decoder = new TextDecoder;
|
|
1940
2283
|
const state = {
|
|
@@ -1945,7 +2288,9 @@ async function* parseSSEStream4(body, signal) {
|
|
|
1945
2288
|
stopReason: "",
|
|
1946
2289
|
thinkingSignature: "",
|
|
1947
2290
|
toolInputJson: "",
|
|
1948
|
-
usage: undefined
|
|
2291
|
+
usage: undefined,
|
|
2292
|
+
providerName,
|
|
2293
|
+
providerBlockInputJson: ""
|
|
1949
2294
|
};
|
|
1950
2295
|
try {
|
|
1951
2296
|
yield* streamChunks(reader, decoder, state, signal);
|
|
@@ -1953,41 +2298,51 @@ async function* parseSSEStream4(body, signal) {
|
|
|
1953
2298
|
reader.releaseLock();
|
|
1954
2299
|
}
|
|
1955
2300
|
}
|
|
1956
|
-
var fetchAndStream = async function* (baseUrl, config2, params, configuredMax, promptCaching) {
|
|
1957
|
-
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;
|
|
1958
2304
|
const target = `${baseUrl}/v1/messages`;
|
|
1959
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
|
+
}
|
|
1960
2316
|
const response = await fetchImpl(target, {
|
|
1961
2317
|
...h2IfHttps4(target),
|
|
1962
2318
|
body: JSON.stringify(body),
|
|
1963
|
-
headers:
|
|
1964
|
-
"anthropic-version": API_VERSION,
|
|
1965
|
-
"Content-Type": "application/json",
|
|
1966
|
-
"x-api-key": config2.apiKey
|
|
1967
|
-
},
|
|
2319
|
+
headers: requestHeaders,
|
|
1968
2320
|
method: "POST",
|
|
1969
2321
|
signal: params.signal
|
|
1970
2322
|
});
|
|
1971
2323
|
if (!response.ok) {
|
|
1972
2324
|
const errorText = await response.text();
|
|
1973
|
-
throw ProviderError.fromResponse(
|
|
2325
|
+
throw ProviderError.fromResponse(providerName, response.status, errorText);
|
|
1974
2326
|
}
|
|
1975
2327
|
if (!response.body) {
|
|
1976
2328
|
throw new ProviderError({
|
|
1977
|
-
message:
|
|
1978
|
-
provider:
|
|
2329
|
+
message: `${providerName} Messages API returned no response body`,
|
|
2330
|
+
provider: providerName,
|
|
1979
2331
|
retryable: true
|
|
1980
2332
|
});
|
|
1981
2333
|
}
|
|
1982
|
-
yield* parseSSEStream4(response.body, params.signal);
|
|
2334
|
+
yield* parseSSEStream4(response.body, providerName, params.signal);
|
|
1983
2335
|
};
|
|
1984
2336
|
var anthropic = (config2) => {
|
|
2337
|
+
if (!config2.apiKey && !config2.tokenSource)
|
|
2338
|
+
throw new Error("anthropic() requires either apiKey or tokenSource");
|
|
1985
2339
|
const baseUrl = config2.baseUrl ?? DEFAULT_BASE_URL4;
|
|
1986
2340
|
const configuredMax = config2.maxTokens ?? DEFAULT_MAX_TOKENS;
|
|
1987
2341
|
const promptCaching = config2.promptCaching ?? true;
|
|
2342
|
+
const providerName = config2.providerName ?? "anthropic";
|
|
1988
2343
|
return instrumentAIProvider({
|
|
1989
|
-
stream: (params) => fetchAndStream(baseUrl, config2, params, configuredMax, promptCaching)
|
|
1990
|
-
},
|
|
2344
|
+
stream: (params) => fetchAndStream(baseUrl, config2, params, configuredMax, promptCaching, providerName)
|
|
2345
|
+
}, providerName);
|
|
1991
2346
|
};
|
|
1992
2347
|
|
|
1993
2348
|
// src/ai/providers/ollama.ts
|
|
@@ -2228,6 +2583,612 @@ var ollama = (config2 = {}) => {
|
|
|
2228
2583
|
}, "ollama");
|
|
2229
2584
|
};
|
|
2230
2585
|
|
|
2586
|
+
// src/ai/providers/openrouterClient.ts
|
|
2587
|
+
var DEFAULT_BASE_URL6 = "https://openrouter.ai/api/v1";
|
|
2588
|
+
var withoutLatestPrefix = (model) => model.startsWith("~") ? model.slice(1) : model;
|
|
2589
|
+
var openRouterModelMatchesRule = (model, rule) => {
|
|
2590
|
+
const normalizedModel = withoutLatestPrefix(model);
|
|
2591
|
+
const normalizedRule = withoutLatestPrefix(rule);
|
|
2592
|
+
return normalizedRule.endsWith("/*") ? normalizedModel.startsWith(normalizedRule.slice(0, -1)) : normalizedModel === normalizedRule;
|
|
2593
|
+
};
|
|
2594
|
+
var assertAllowedModel = (model, allowedModels) => {
|
|
2595
|
+
if (!allowedModels)
|
|
2596
|
+
return;
|
|
2597
|
+
if (allowedModels.some((rule) => openRouterModelMatchesRule(model, rule)))
|
|
2598
|
+
return;
|
|
2599
|
+
throw new Error(`OpenRouter model "${model}" is not allowed`);
|
|
2600
|
+
};
|
|
2601
|
+
var normalizePath = (path) => path.startsWith("/") ? path : `/${path}`;
|
|
2602
|
+
var encodeModelPath = (model) => model.split("/").map(encodeURIComponent).join("/");
|
|
2603
|
+
var withQuery = (url, query) => {
|
|
2604
|
+
if (!query)
|
|
2605
|
+
return url;
|
|
2606
|
+
const result = new URL(url);
|
|
2607
|
+
for (const [key, value] of Object.entries(query)) {
|
|
2608
|
+
if (value !== undefined)
|
|
2609
|
+
result.searchParams.set(key, String(value));
|
|
2610
|
+
}
|
|
2611
|
+
return result.toString();
|
|
2612
|
+
};
|
|
2613
|
+
var parseImageSSE = async function* (response) {
|
|
2614
|
+
if (!response.body)
|
|
2615
|
+
throw new Error("OpenRouter image stream has no body");
|
|
2616
|
+
const reader = response.body.getReader();
|
|
2617
|
+
const decoder = new TextDecoder;
|
|
2618
|
+
let buffer = "";
|
|
2619
|
+
try {
|
|
2620
|
+
for (;; ) {
|
|
2621
|
+
const result = await reader.read();
|
|
2622
|
+
buffer += decoder.decode(result.value, { stream: !result.done });
|
|
2623
|
+
const lines = buffer.split(`
|
|
2624
|
+
`);
|
|
2625
|
+
buffer = lines.pop() ?? "";
|
|
2626
|
+
for (const line of lines) {
|
|
2627
|
+
if (!line.startsWith("data: "))
|
|
2628
|
+
continue;
|
|
2629
|
+
const data = line.slice(6);
|
|
2630
|
+
if (data === "[DONE]")
|
|
2631
|
+
return;
|
|
2632
|
+
try {
|
|
2633
|
+
const parsed = JSON.parse(data);
|
|
2634
|
+
if (parsed && typeof parsed === "object" && "type" in parsed)
|
|
2635
|
+
yield parsed;
|
|
2636
|
+
} catch {}
|
|
2637
|
+
}
|
|
2638
|
+
if (result.done)
|
|
2639
|
+
break;
|
|
2640
|
+
}
|
|
2641
|
+
if (buffer.startsWith("data: ")) {
|
|
2642
|
+
const parsed = JSON.parse(buffer.slice(6));
|
|
2643
|
+
if (parsed && typeof parsed === "object" && "type" in parsed)
|
|
2644
|
+
yield parsed;
|
|
2645
|
+
}
|
|
2646
|
+
} finally {
|
|
2647
|
+
reader.releaseLock();
|
|
2648
|
+
}
|
|
2649
|
+
};
|
|
2650
|
+
var toBytes = (value) => typeof value === "string" ? new TextEncoder().encode(value) : value;
|
|
2651
|
+
var hexToBytes = (hex) => {
|
|
2652
|
+
if (!/^[0-9a-f]+$/iu.test(hex) || hex.length % 2 !== 0)
|
|
2653
|
+
return;
|
|
2654
|
+
const bytes = new Uint8Array(hex.length / 2);
|
|
2655
|
+
for (let index = 0;index < bytes.length; index += 1) {
|
|
2656
|
+
bytes[index] = Number.parseInt(hex.slice(index * 2, index * 2 + 2), 16);
|
|
2657
|
+
}
|
|
2658
|
+
return bytes;
|
|
2659
|
+
};
|
|
2660
|
+
var constantTimeEqual = (left, right) => {
|
|
2661
|
+
if (left.length !== right.length)
|
|
2662
|
+
return false;
|
|
2663
|
+
let mismatch = 0;
|
|
2664
|
+
for (let index = 0;index < left.length; index += 1)
|
|
2665
|
+
mismatch |= (left[index] ?? 0) ^ (right[index] ?? 0);
|
|
2666
|
+
return mismatch === 0;
|
|
2667
|
+
};
|
|
2668
|
+
var verifyOpenRouterWebhookSignature = async (options) => {
|
|
2669
|
+
const fields = new Map(options.header.split(",").map((part) => {
|
|
2670
|
+
const [key2, ...rest] = part.trim().split("=");
|
|
2671
|
+
return [key2, rest.join("=")];
|
|
2672
|
+
}));
|
|
2673
|
+
const timestamp = fields.get("t");
|
|
2674
|
+
const supplied = fields.get("v1");
|
|
2675
|
+
if (!timestamp || !supplied)
|
|
2676
|
+
return false;
|
|
2677
|
+
const timestampNumber = Number(timestamp);
|
|
2678
|
+
const now = options.nowSeconds ?? Math.floor(Date.now() / 1000);
|
|
2679
|
+
const tolerance = options.toleranceSeconds ?? 300;
|
|
2680
|
+
if (!Number.isFinite(timestampNumber) || Math.abs(now - timestampNumber) > tolerance)
|
|
2681
|
+
return false;
|
|
2682
|
+
const key = await crypto.subtle.importKey("raw", Uint8Array.from(toBytes(options.secret)).buffer, { hash: "SHA-256", name: "HMAC" }, false, ["sign"]);
|
|
2683
|
+
const prefix = new TextEncoder().encode(`${timestamp},`);
|
|
2684
|
+
const body = toBytes(options.body);
|
|
2685
|
+
const payload = new Uint8Array(prefix.length + body.length);
|
|
2686
|
+
payload.set(prefix);
|
|
2687
|
+
payload.set(body, prefix.length);
|
|
2688
|
+
const expected = new Uint8Array(await crypto.subtle.sign("HMAC", key, payload.buffer));
|
|
2689
|
+
const suppliedBytes = hexToBytes(supplied);
|
|
2690
|
+
return suppliedBytes ? constantTimeEqual(expected, suppliedBytes) : false;
|
|
2691
|
+
};
|
|
2692
|
+
var createOpenRouterClient = (config2) => {
|
|
2693
|
+
if (!config2.apiKey && !config2.tokenSource)
|
|
2694
|
+
throw new Error("createOpenRouterClient() requires either apiKey or tokenSource");
|
|
2695
|
+
const baseUrl = (config2.baseUrl ?? DEFAULT_BASE_URL6).replace(/\/$/, "");
|
|
2696
|
+
const fetchImpl = config2.fetch ?? globalThis.fetch;
|
|
2697
|
+
const allowedModels = config2.allowedModels ? [...config2.allowedModels] : undefined;
|
|
2698
|
+
const requestRaw = async (path, options = {}) => {
|
|
2699
|
+
const token = config2.tokenSource ? await Promise.resolve(config2.tokenSource()) : config2.apiKey;
|
|
2700
|
+
const suppliedHeaders = typeof config2.headers === "function" ? await config2.headers() : config2.headers ?? {};
|
|
2701
|
+
const headers = new Headers(suppliedHeaders);
|
|
2702
|
+
new Headers(options.headers).forEach((value, key) => headers.set(key, value));
|
|
2703
|
+
headers.set("Authorization", `Bearer ${token}`);
|
|
2704
|
+
let body;
|
|
2705
|
+
if (options.body instanceof FormData || options.body instanceof Blob) {
|
|
2706
|
+
body = options.body;
|
|
2707
|
+
} else if (options.body !== undefined) {
|
|
2708
|
+
headers.set("Content-Type", "application/json");
|
|
2709
|
+
body = JSON.stringify(options.body);
|
|
2710
|
+
}
|
|
2711
|
+
const { query, ...requestInit } = options;
|
|
2712
|
+
const response = await fetchImpl(withQuery(`${baseUrl}${normalizePath(path)}`, options.query), { ...requestInit, body, headers });
|
|
2713
|
+
if (!response.ok) {
|
|
2714
|
+
throw ProviderError.fromResponse("openrouter", response.status, await response.text());
|
|
2715
|
+
}
|
|
2716
|
+
return response;
|
|
2717
|
+
};
|
|
2718
|
+
const request = async (path, options = {}) => (await requestRaw(path, options)).json();
|
|
2719
|
+
const listModels = async (query) => {
|
|
2720
|
+
const result = await request("/models", { query });
|
|
2721
|
+
if (!allowedModels)
|
|
2722
|
+
return result;
|
|
2723
|
+
return {
|
|
2724
|
+
...result,
|
|
2725
|
+
data: result.data.filter((model) => allowedModels.some((rule) => openRouterModelMatchesRule(model.id, rule)))
|
|
2726
|
+
};
|
|
2727
|
+
};
|
|
2728
|
+
const filterModelList = (result) => {
|
|
2729
|
+
if (!allowedModels)
|
|
2730
|
+
return result;
|
|
2731
|
+
return {
|
|
2732
|
+
...result,
|
|
2733
|
+
data: result.data.filter((model) => allowedModels.some((rule) => openRouterModelMatchesRule(model.id, rule)))
|
|
2734
|
+
};
|
|
2735
|
+
};
|
|
2736
|
+
return {
|
|
2737
|
+
cancelBatch: (id) => request(`/batches/${encodeURIComponent(id)}/cancel`, {
|
|
2738
|
+
method: "POST"
|
|
2739
|
+
}),
|
|
2740
|
+
createBatch: (body) => request("/batches", { body, method: "POST" }),
|
|
2741
|
+
createEmbedding: (body) => {
|
|
2742
|
+
assertAllowedModel(body.model, allowedModels);
|
|
2743
|
+
return request("/embeddings", {
|
|
2744
|
+
body,
|
|
2745
|
+
method: "POST"
|
|
2746
|
+
});
|
|
2747
|
+
},
|
|
2748
|
+
generateImage: (body) => {
|
|
2749
|
+
assertAllowedModel(body.model, allowedModels);
|
|
2750
|
+
return request("/images", {
|
|
2751
|
+
body,
|
|
2752
|
+
method: "POST"
|
|
2753
|
+
});
|
|
2754
|
+
},
|
|
2755
|
+
deleteFile: (id, workspaceId) => request(`/files/${encodeURIComponent(id)}`, { method: "DELETE", query: { workspace_id: workspaceId } }),
|
|
2756
|
+
downloadFile: (id, workspaceId) => requestRaw(`/files/${encodeURIComponent(id)}/content`, {
|
|
2757
|
+
query: { workspace_id: workspaceId }
|
|
2758
|
+
}),
|
|
2759
|
+
downloadVideo: (id, index = 0) => requestRaw(`/videos/${encodeURIComponent(id)}/content`, {
|
|
2760
|
+
query: { index }
|
|
2761
|
+
}),
|
|
2762
|
+
generateVideo: (body) => {
|
|
2763
|
+
assertAllowedModel(body.model, allowedModels);
|
|
2764
|
+
return request("/videos", {
|
|
2765
|
+
body,
|
|
2766
|
+
method: "POST"
|
|
2767
|
+
});
|
|
2768
|
+
},
|
|
2769
|
+
getBatch: (id) => request(`/batches/${encodeURIComponent(id)}`),
|
|
2770
|
+
getCredits: () => request("/credits"),
|
|
2771
|
+
getCurrentKey: () => request("/key"),
|
|
2772
|
+
getFile: (id, workspaceId) => request(`/files/${encodeURIComponent(id)}`, {
|
|
2773
|
+
query: { workspace_id: workspaceId }
|
|
2774
|
+
}),
|
|
2775
|
+
getGeneration: (id) => request("/generation", {
|
|
2776
|
+
query: { id }
|
|
2777
|
+
}),
|
|
2778
|
+
getModelEndpoints: (model) => {
|
|
2779
|
+
assertAllowedModel(model, allowedModels);
|
|
2780
|
+
return request(`/models/${encodeModelPath(model)}/endpoints`);
|
|
2781
|
+
},
|
|
2782
|
+
getModel: (model) => {
|
|
2783
|
+
assertAllowedModel(model, allowedModels);
|
|
2784
|
+
return request(`/model/${encodeModelPath(model)}`);
|
|
2785
|
+
},
|
|
2786
|
+
getImageModelEndpoints: (model) => {
|
|
2787
|
+
assertAllowedModel(model, allowedModels);
|
|
2788
|
+
return request(`/images/models/${encodeModelPath(model)}/endpoints`);
|
|
2789
|
+
},
|
|
2790
|
+
getVideo: (id) => request(`/videos/${encodeURIComponent(id)}`),
|
|
2791
|
+
listImageModels: async () => filterModelList(await request("/images/models")),
|
|
2792
|
+
listFiles: (query) => request("/files", { query }),
|
|
2793
|
+
listModels,
|
|
2794
|
+
listUserModels: async () => filterModelList(await request("/models/user")),
|
|
2795
|
+
listZdrEndpoints: async () => {
|
|
2796
|
+
const result = await request("/endpoints/zdr");
|
|
2797
|
+
if (!allowedModels)
|
|
2798
|
+
return result;
|
|
2799
|
+
return {
|
|
2800
|
+
...result,
|
|
2801
|
+
data: result.data.filter((endpoint) => allowedModels.some((rule) => openRouterModelMatchesRule(endpoint.model_id, rule)))
|
|
2802
|
+
};
|
|
2803
|
+
},
|
|
2804
|
+
countModels: (outputModalities) => request("/models/count", {
|
|
2805
|
+
query: { output_modalities: outputModalities }
|
|
2806
|
+
}),
|
|
2807
|
+
listPresets: (offset = 0, limit = 100) => request("/presets", { query: { limit, offset } }),
|
|
2808
|
+
listPresetVersions: (slug, offset = 0, limit = 100) => request(`/presets/${encodeURIComponent(slug)}/versions`, { query: { limit, offset } }),
|
|
2809
|
+
listProviders: () => request("/providers"),
|
|
2810
|
+
listRerankModels: async () => filterModelList(await request("/rerank/models")),
|
|
2811
|
+
listVideoModels: async () => filterModelList(await request("/videos/models")),
|
|
2812
|
+
request,
|
|
2813
|
+
requestRaw,
|
|
2814
|
+
streamImage: async function* (body, options = {}) {
|
|
2815
|
+
assertAllowedModel(body.model, allowedModels);
|
|
2816
|
+
const response = await requestRaw("/images", {
|
|
2817
|
+
...options,
|
|
2818
|
+
body: { ...body, stream: true },
|
|
2819
|
+
method: "POST"
|
|
2820
|
+
});
|
|
2821
|
+
yield* parseImageSSE(response);
|
|
2822
|
+
},
|
|
2823
|
+
respond: (body) => {
|
|
2824
|
+
assertAllowedModel(body.model, allowedModels);
|
|
2825
|
+
return body.stream ? requestRaw("/responses", { body, method: "POST" }) : request("/responses", {
|
|
2826
|
+
body,
|
|
2827
|
+
method: "POST"
|
|
2828
|
+
});
|
|
2829
|
+
},
|
|
2830
|
+
rerank: (body) => {
|
|
2831
|
+
assertAllowedModel(body.model, allowedModels);
|
|
2832
|
+
return request("/rerank", {
|
|
2833
|
+
body,
|
|
2834
|
+
method: "POST"
|
|
2835
|
+
});
|
|
2836
|
+
},
|
|
2837
|
+
speak: (body) => {
|
|
2838
|
+
assertAllowedModel(body.model, allowedModels);
|
|
2839
|
+
return requestRaw("/audio/speech", { body, method: "POST" });
|
|
2840
|
+
},
|
|
2841
|
+
transcribe: (body) => {
|
|
2842
|
+
if (body instanceof FormData) {
|
|
2843
|
+
const model = body.get("model");
|
|
2844
|
+
if (typeof model !== "string")
|
|
2845
|
+
throw new Error("OpenRouter transcription FormData requires model");
|
|
2846
|
+
assertAllowedModel(model, allowedModels);
|
|
2847
|
+
} else {
|
|
2848
|
+
assertAllowedModel(body.model, allowedModels);
|
|
2849
|
+
}
|
|
2850
|
+
return request("/audio/transcriptions", {
|
|
2851
|
+
body,
|
|
2852
|
+
method: "POST"
|
|
2853
|
+
});
|
|
2854
|
+
},
|
|
2855
|
+
uploadFile: (file, options = {}) => {
|
|
2856
|
+
const body = new FormData;
|
|
2857
|
+
if (options.filename)
|
|
2858
|
+
body.append("file", file, options.filename);
|
|
2859
|
+
else
|
|
2860
|
+
body.append("file", file);
|
|
2861
|
+
return request("/files", {
|
|
2862
|
+
body,
|
|
2863
|
+
method: "POST",
|
|
2864
|
+
query: { workspace_id: options.workspaceId }
|
|
2865
|
+
});
|
|
2866
|
+
}
|
|
2867
|
+
};
|
|
2868
|
+
};
|
|
2869
|
+
|
|
2870
|
+
// src/ai/providers/openrouter.ts
|
|
2871
|
+
var DEFAULT_BASE_URL7 = "https://openrouter.ai/api";
|
|
2872
|
+
var MAX_APP_CATEGORIES = 2;
|
|
2873
|
+
var withoutLatestPrefix2 = (model) => model.startsWith("~") ? model.slice(1) : model;
|
|
2874
|
+
var modelForOpenAICapabilities = (model) => {
|
|
2875
|
+
const normalized = withoutLatestPrefix2(model);
|
|
2876
|
+
return normalized.startsWith("openai/") ? normalized.slice("openai/".length) : normalized;
|
|
2877
|
+
};
|
|
2878
|
+
var modelMatchesRule = (model, rule) => {
|
|
2879
|
+
const normalizedModel = withoutLatestPrefix2(model);
|
|
2880
|
+
const normalizedRule = withoutLatestPrefix2(rule);
|
|
2881
|
+
if (normalizedRule.endsWith("/*")) {
|
|
2882
|
+
return normalizedModel.startsWith(normalizedRule.slice(0, -1));
|
|
2883
|
+
}
|
|
2884
|
+
return normalizedModel === normalizedRule;
|
|
2885
|
+
};
|
|
2886
|
+
var providerMatchesRule = (provider, rule) => provider === rule || provider.startsWith(`${rule}/`);
|
|
2887
|
+
var assertNonEmptyPolicy = (label, value) => {
|
|
2888
|
+
if (value && value.length === 0) {
|
|
2889
|
+
throw new Error(`openrouter() ${label} must not be empty`);
|
|
2890
|
+
}
|
|
2891
|
+
};
|
|
2892
|
+
var assertRoutingPolicy = (config2) => {
|
|
2893
|
+
assertNonEmptyPolicy("allowedProviders", config2.allowedProviders);
|
|
2894
|
+
assertNonEmptyPolicy("routing.only", config2.routing?.only);
|
|
2895
|
+
assertNonEmptyPolicy("allowedPresets", config2.allowedPresets);
|
|
2896
|
+
if (config2.appCategories && config2.appCategories.length > MAX_APP_CATEGORIES) {
|
|
2897
|
+
throw new Error("openrouter() appCategories supports at most 2 entries");
|
|
2898
|
+
}
|
|
2899
|
+
if (!config2.allowedProviders)
|
|
2900
|
+
return;
|
|
2901
|
+
const selected = [
|
|
2902
|
+
...config2.routing?.only ?? [],
|
|
2903
|
+
...config2.routing?.order ?? []
|
|
2904
|
+
];
|
|
2905
|
+
const denied = selected.find((provider) => !config2.allowedProviders.some((rule) => providerMatchesRule(provider, rule)));
|
|
2906
|
+
if (denied) {
|
|
2907
|
+
throw new Error(`openrouter() provider "${denied}" is outside allowedProviders`);
|
|
2908
|
+
}
|
|
2909
|
+
};
|
|
2910
|
+
var assertRequestRoutingPolicy = (routing, allowedProviders) => {
|
|
2911
|
+
assertRoutingPolicy({
|
|
2912
|
+
allowedProviders,
|
|
2913
|
+
apiKey: "policy-validation",
|
|
2914
|
+
routing
|
|
2915
|
+
});
|
|
2916
|
+
};
|
|
2917
|
+
var mapRouting = (routing, allowedProviders) => {
|
|
2918
|
+
const only = routing?.only ?? allowedProviders;
|
|
2919
|
+
const wire = {};
|
|
2920
|
+
if (typeof routing?.allowFallbacks === "boolean")
|
|
2921
|
+
wire.allow_fallbacks = routing.allowFallbacks;
|
|
2922
|
+
if (routing?.dataCollection)
|
|
2923
|
+
wire.data_collection = routing.dataCollection;
|
|
2924
|
+
if (typeof routing?.enforceDistillableText === "boolean")
|
|
2925
|
+
wire.enforce_distillable_text = routing.enforceDistillableText;
|
|
2926
|
+
if (routing?.ignore)
|
|
2927
|
+
wire.ignore = [...routing.ignore];
|
|
2928
|
+
if (routing?.maxPrice)
|
|
2929
|
+
wire.max_price = { ...routing.maxPrice };
|
|
2930
|
+
if (only)
|
|
2931
|
+
wire.only = [...only];
|
|
2932
|
+
if (routing?.order)
|
|
2933
|
+
wire.order = [...routing.order];
|
|
2934
|
+
if (routing?.preferredMaxLatency !== undefined)
|
|
2935
|
+
wire.preferred_max_latency = routing.preferredMaxLatency;
|
|
2936
|
+
if (routing?.preferredMinThroughput !== undefined)
|
|
2937
|
+
wire.preferred_min_throughput = routing.preferredMinThroughput;
|
|
2938
|
+
if (routing?.quantizations)
|
|
2939
|
+
wire.quantizations = [...routing.quantizations];
|
|
2940
|
+
if (typeof routing?.requireParameters === "boolean")
|
|
2941
|
+
wire.require_parameters = routing.requireParameters;
|
|
2942
|
+
if (routing?.sort)
|
|
2943
|
+
wire.sort = routing.sort;
|
|
2944
|
+
if (typeof routing?.zdr === "boolean")
|
|
2945
|
+
wire.zdr = routing.zdr;
|
|
2946
|
+
return wire;
|
|
2947
|
+
};
|
|
2948
|
+
var requestOptionsFor = (params, defaults) => {
|
|
2949
|
+
const supplied = params.providerOptions?.openrouter;
|
|
2950
|
+
if (supplied !== undefined && (typeof supplied !== "object" || !supplied)) {
|
|
2951
|
+
throw new Error("providerOptions.openrouter must be an object");
|
|
2952
|
+
}
|
|
2953
|
+
return {
|
|
2954
|
+
...defaults,
|
|
2955
|
+
...supplied
|
|
2956
|
+
};
|
|
2957
|
+
};
|
|
2958
|
+
var resolveAttributionHeaders = async (config2, params) => {
|
|
2959
|
+
const supplied = typeof config2.headers === "function" ? await config2.headers() : config2.headers ?? {};
|
|
2960
|
+
const headers = new Headers(supplied);
|
|
2961
|
+
if (config2.appUrl)
|
|
2962
|
+
headers.set("HTTP-Referer", config2.appUrl);
|
|
2963
|
+
if (config2.appName)
|
|
2964
|
+
headers.set("X-OpenRouter-Title", config2.appName);
|
|
2965
|
+
if (config2.appCategories?.length) {
|
|
2966
|
+
headers.set("X-OpenRouter-Categories", config2.appCategories.join(","));
|
|
2967
|
+
}
|
|
2968
|
+
const options = requestOptionsFor(params, config2.requestOptions);
|
|
2969
|
+
if (options.routerMetadata ?? true)
|
|
2970
|
+
headers.set("X-OpenRouter-Metadata", "enabled");
|
|
2971
|
+
if (options.sessionId)
|
|
2972
|
+
headers.set("X-Session-Id", options.sessionId);
|
|
2973
|
+
if (options.responseCache) {
|
|
2974
|
+
headers.set("X-OpenRouter-Cache", options.responseCache.enabled ? "true" : "false");
|
|
2975
|
+
if (options.responseCache.ttlSeconds !== undefined)
|
|
2976
|
+
headers.set("X-OpenRouter-Cache-TTL", String(options.responseCache.ttlSeconds));
|
|
2977
|
+
if (options.responseCache.clear)
|
|
2978
|
+
headers.set("X-OpenRouter-Cache-Clear", "true");
|
|
2979
|
+
}
|
|
2980
|
+
return headers;
|
|
2981
|
+
};
|
|
2982
|
+
var SECURITY_SENSITIVE_EXTRA_BODY_FIELDS = new Set([
|
|
2983
|
+
"messages",
|
|
2984
|
+
"model",
|
|
2985
|
+
"models",
|
|
2986
|
+
"plugins",
|
|
2987
|
+
"preset",
|
|
2988
|
+
"provider",
|
|
2989
|
+
"stream",
|
|
2990
|
+
"tools"
|
|
2991
|
+
]);
|
|
2992
|
+
var assertAllowedPreset = (preset, allowedPresets) => {
|
|
2993
|
+
if (!preset)
|
|
2994
|
+
return;
|
|
2995
|
+
if (allowedPresets?.includes(preset))
|
|
2996
|
+
return;
|
|
2997
|
+
throw new Error(`OpenRouter preset "${preset}" is not allowed`);
|
|
2998
|
+
};
|
|
2999
|
+
var assertIndirectModels = (value, allowedModels, key = "") => {
|
|
3000
|
+
if (key === "model" && typeof value === "string")
|
|
3001
|
+
assertAllowedModel2(value, allowedModels);
|
|
3002
|
+
if ((key === "models" || key === "analysis_models") && Array.isArray(value)) {
|
|
3003
|
+
for (const model of value) {
|
|
3004
|
+
if (typeof model === "string")
|
|
3005
|
+
assertAllowedModel2(model, allowedModels);
|
|
3006
|
+
}
|
|
3007
|
+
}
|
|
3008
|
+
if (Array.isArray(value)) {
|
|
3009
|
+
for (const item of value)
|
|
3010
|
+
assertIndirectModels(item, allowedModels);
|
|
3011
|
+
} else if (value && typeof value === "object") {
|
|
3012
|
+
for (const [childKey, child] of Object.entries(value))
|
|
3013
|
+
assertIndirectModels(child, allowedModels, childKey);
|
|
3014
|
+
}
|
|
3015
|
+
};
|
|
3016
|
+
var assertRequestOptions = (options, allowedModels, allowedPresets, allowedProviders) => {
|
|
3017
|
+
assertAllowedPreset(options.preset, allowedPresets);
|
|
3018
|
+
assertRequestRoutingPolicy(options.routing, allowedProviders);
|
|
3019
|
+
if (options.sessionId && options.sessionId.length > 256)
|
|
3020
|
+
throw new Error("OpenRouter sessionId must be at most 256 characters");
|
|
3021
|
+
const ttl = options.responseCache?.ttlSeconds;
|
|
3022
|
+
if (ttl !== undefined && (!Number.isInteger(ttl) || ttl < 1 || ttl > 86400))
|
|
3023
|
+
throw new Error("OpenRouter response-cache TTL must be 1-86400 seconds");
|
|
3024
|
+
if (options.responseCache?.clear && !options.responseCache.enabled)
|
|
3025
|
+
throw new Error("OpenRouter cache clear requires response caching enabled");
|
|
3026
|
+
if (options.fallbackModels) {
|
|
3027
|
+
if (options.fallbackModels.length === 0)
|
|
3028
|
+
throw new Error("OpenRouter fallbackModels must not be empty");
|
|
3029
|
+
for (const model of options.fallbackModels)
|
|
3030
|
+
assertAllowedModel2(model, allowedModels);
|
|
3031
|
+
}
|
|
3032
|
+
assertIndirectModels(options.serverTools, allowedModels);
|
|
3033
|
+
assertIndirectModels(options.messagesTools, allowedModels);
|
|
3034
|
+
assertIndirectModels(options.plugins, allowedModels);
|
|
3035
|
+
if (options.extraBody) {
|
|
3036
|
+
const unsafe = Object.keys(options.extraBody).find((key) => SECURITY_SENSITIVE_EXTRA_BODY_FIELDS.has(key));
|
|
3037
|
+
if (unsafe)
|
|
3038
|
+
throw new Error(`OpenRouter extraBody cannot override "${unsafe}"`);
|
|
3039
|
+
}
|
|
3040
|
+
};
|
|
3041
|
+
var assertAllowedModel2 = (model, allowedModels) => {
|
|
3042
|
+
if (!allowedModels)
|
|
3043
|
+
return;
|
|
3044
|
+
if (allowedModels.some((rule) => modelMatchesRule(model, rule)))
|
|
3045
|
+
return;
|
|
3046
|
+
throw new Error(`OpenRouter model "${model}" is not allowed`);
|
|
3047
|
+
};
|
|
3048
|
+
var snapshotPolicy = (config2) => ({
|
|
3049
|
+
allowedModels: config2.allowedModels ? [...config2.allowedModels] : undefined,
|
|
3050
|
+
allowedPresets: config2.allowedPresets ? [...config2.allowedPresets] : undefined
|
|
3051
|
+
});
|
|
3052
|
+
var transformOpenRouterRequest = (config2, allowedModels, allowedPresets, body, params, skin = "openai") => {
|
|
3053
|
+
const options = requestOptionsFor(params, config2.requestOptions);
|
|
3054
|
+
assertRequestOptions(options, allowedModels, allowedPresets, config2.allowedProviders);
|
|
3055
|
+
const transformed = { ...body, ...options.extraBody };
|
|
3056
|
+
if (options.audioOutput) {
|
|
3057
|
+
transformed.audio = options.audioOutput;
|
|
3058
|
+
transformed.modalities = ["text", "audio"];
|
|
3059
|
+
}
|
|
3060
|
+
if (skin === "openai") {
|
|
3061
|
+
const requestedReasoning = {};
|
|
3062
|
+
if (params.reasoning?.budgetTokens !== undefined) {
|
|
3063
|
+
requestedReasoning.max_tokens = params.reasoning.budgetTokens;
|
|
3064
|
+
delete transformed.reasoning_effort;
|
|
3065
|
+
} else if (params.reasoning?.effort) {
|
|
3066
|
+
requestedReasoning.effort = params.reasoning.effort;
|
|
3067
|
+
delete transformed.reasoning_effort;
|
|
3068
|
+
}
|
|
3069
|
+
if (options.reasoning) {
|
|
3070
|
+
Object.assign(requestedReasoning, options.reasoning);
|
|
3071
|
+
if (options.reasoning.maxTokens !== undefined) {
|
|
3072
|
+
requestedReasoning.max_tokens = options.reasoning.maxTokens;
|
|
3073
|
+
delete requestedReasoning.maxTokens;
|
|
3074
|
+
}
|
|
3075
|
+
}
|
|
3076
|
+
if (Object.keys(requestedReasoning).length > 0)
|
|
3077
|
+
transformed.reasoning = requestedReasoning;
|
|
3078
|
+
}
|
|
3079
|
+
const automaticCacheControl = params.promptCaching === true || params.cacheSystemPrompt === true ? { type: "ephemeral" } : undefined;
|
|
3080
|
+
const cacheControl = options.cacheControl ?? automaticCacheControl;
|
|
3081
|
+
if (cacheControl)
|
|
3082
|
+
transformed.cache_control = cacheControl;
|
|
3083
|
+
if (options.promptCacheKey)
|
|
3084
|
+
transformed.prompt_cache_key = options.promptCacheKey;
|
|
3085
|
+
if (options.promptCacheOptions)
|
|
3086
|
+
transformed.prompt_cache_options = options.promptCacheOptions;
|
|
3087
|
+
const routing = mapRouting({ ...config2.routing, ...options.routing }, config2.allowedProviders);
|
|
3088
|
+
if (Object.keys(routing).length > 0)
|
|
3089
|
+
transformed.provider = routing;
|
|
3090
|
+
if (options.fallbackModels)
|
|
3091
|
+
transformed.models = [...options.fallbackModels];
|
|
3092
|
+
if (options.includeReasoning !== undefined)
|
|
3093
|
+
transformed.include_reasoning = options.includeReasoning;
|
|
3094
|
+
if (options.maxToolCalls !== undefined)
|
|
3095
|
+
transformed.max_tool_calls = options.maxToolCalls;
|
|
3096
|
+
if (options.plugins)
|
|
3097
|
+
transformed.plugins = [...options.plugins];
|
|
3098
|
+
if (options.preset)
|
|
3099
|
+
transformed.preset = options.preset;
|
|
3100
|
+
if (options.serverTools) {
|
|
3101
|
+
transformed.tools = [
|
|
3102
|
+
...Array.isArray(transformed.tools) ? transformed.tools : [],
|
|
3103
|
+
...options.serverTools
|
|
3104
|
+
];
|
|
3105
|
+
}
|
|
3106
|
+
if (options.messagesTools) {
|
|
3107
|
+
if (skin !== "messages")
|
|
3108
|
+
throw new Error("OpenRouter messagesTools requires openrouterMessages()");
|
|
3109
|
+
transformed.tools = [
|
|
3110
|
+
...Array.isArray(transformed.tools) ? transformed.tools : [],
|
|
3111
|
+
...options.messagesTools
|
|
3112
|
+
];
|
|
3113
|
+
}
|
|
3114
|
+
if (options.serviceTier)
|
|
3115
|
+
transformed.service_tier = options.serviceTier;
|
|
3116
|
+
if (options.sessionId)
|
|
3117
|
+
transformed.session_id = options.sessionId;
|
|
3118
|
+
if (options.stopServerToolsWhen)
|
|
3119
|
+
transformed.stop_server_tools_when = options.stopServerToolsWhen;
|
|
3120
|
+
if (options.trace)
|
|
3121
|
+
transformed.trace = options.trace;
|
|
3122
|
+
if (options.transforms)
|
|
3123
|
+
transformed.transforms = [...options.transforms];
|
|
3124
|
+
if (options.user)
|
|
3125
|
+
transformed.user = options.user;
|
|
3126
|
+
if (options.verbosity)
|
|
3127
|
+
transformed.verbosity = options.verbosity;
|
|
3128
|
+
return transformed;
|
|
3129
|
+
};
|
|
3130
|
+
var withOpenRouterPolicy = (provider, allowedModels, allowedPresets) => ({
|
|
3131
|
+
stream: (params) => {
|
|
3132
|
+
if (params.model.startsWith("@preset/")) {
|
|
3133
|
+
assertAllowedPreset(params.model.slice("@preset/".length), allowedPresets);
|
|
3134
|
+
} else {
|
|
3135
|
+
const presetSeparator = params.model.indexOf("@preset/");
|
|
3136
|
+
if (presetSeparator >= 0) {
|
|
3137
|
+
assertAllowedModel2(params.model.slice(0, presetSeparator), allowedModels);
|
|
3138
|
+
assertAllowedPreset(params.model.slice(presetSeparator + "@preset/".length), allowedPresets);
|
|
3139
|
+
} else {
|
|
3140
|
+
assertAllowedModel2(params.model, allowedModels);
|
|
3141
|
+
}
|
|
3142
|
+
}
|
|
3143
|
+
return provider.stream(params);
|
|
3144
|
+
}
|
|
3145
|
+
});
|
|
3146
|
+
var openrouter = (config2) => {
|
|
3147
|
+
assertRoutingPolicy(config2);
|
|
3148
|
+
const { allowedModels, allowedPresets } = snapshotPolicy(config2);
|
|
3149
|
+
const provider = openai({
|
|
3150
|
+
apiKey: config2.apiKey,
|
|
3151
|
+
baseUrl: config2.baseUrl ?? DEFAULT_BASE_URL7,
|
|
3152
|
+
fetch: config2.fetch,
|
|
3153
|
+
headers: (params) => resolveAttributionHeaders(config2, params),
|
|
3154
|
+
modelForCapabilities: modelForOpenAICapabilities,
|
|
3155
|
+
providerName: "openrouter",
|
|
3156
|
+
tokenSource: config2.tokenSource,
|
|
3157
|
+
transformRequestBody: (body, params) => transformOpenRouterRequest(config2, allowedModels, allowedPresets, body, params)
|
|
3158
|
+
});
|
|
3159
|
+
return withOpenRouterPolicy(provider, allowedModels, allowedPresets);
|
|
3160
|
+
};
|
|
3161
|
+
var openrouterResponses = (config2) => {
|
|
3162
|
+
assertRoutingPolicy(config2);
|
|
3163
|
+
const { allowedModels, allowedPresets } = snapshotPolicy(config2);
|
|
3164
|
+
const provider = openaiResponses({
|
|
3165
|
+
apiKey: config2.apiKey,
|
|
3166
|
+
baseUrl: config2.baseUrl ?? DEFAULT_BASE_URL7,
|
|
3167
|
+
fetch: config2.fetch,
|
|
3168
|
+
headers: (params) => resolveAttributionHeaders(config2, params),
|
|
3169
|
+
modelForCapabilities: modelForOpenAICapabilities,
|
|
3170
|
+
providerName: "openrouter",
|
|
3171
|
+
tokenSource: config2.tokenSource,
|
|
3172
|
+
transformRequestBody: (body, params) => transformOpenRouterRequest(config2, allowedModels, allowedPresets, body, params)
|
|
3173
|
+
});
|
|
3174
|
+
return withOpenRouterPolicy(provider, allowedModels, allowedPresets);
|
|
3175
|
+
};
|
|
3176
|
+
var openrouterMessages = (config2) => {
|
|
3177
|
+
assertRoutingPolicy(config2);
|
|
3178
|
+
const { allowedModels, allowedPresets } = snapshotPolicy(config2);
|
|
3179
|
+
const provider = anthropic({
|
|
3180
|
+
apiKey: config2.apiKey,
|
|
3181
|
+
authStyle: "bearer",
|
|
3182
|
+
baseUrl: config2.baseUrl ?? DEFAULT_BASE_URL7,
|
|
3183
|
+
fetch: config2.fetch,
|
|
3184
|
+
headers: (params) => resolveAttributionHeaders(config2, params),
|
|
3185
|
+
providerName: "openrouter",
|
|
3186
|
+
tokenSource: config2.tokenSource,
|
|
3187
|
+
transformRequestBody: (body, params) => transformOpenRouterRequest(config2, allowedModels, allowedPresets, body, params, "messages")
|
|
3188
|
+
});
|
|
3189
|
+
return withOpenRouterPolicy(provider, allowedModels, allowedPresets);
|
|
3190
|
+
};
|
|
3191
|
+
|
|
2231
3192
|
// src/plugins/aiChat.ts
|
|
2232
3193
|
import { Elysia } from "elysia";
|
|
2233
3194
|
|
|
@@ -2403,6 +3364,8 @@ var isValidAIServerMessage = (data) => {
|
|
|
2403
3364
|
return "name" in data && "status" in data && "messageId" in data && "conversationId" in data;
|
|
2404
3365
|
case "image":
|
|
2405
3366
|
return "data" in data && typeof data.data === "string" && "format" in data && typeof data.format === "string" && "isPartial" in data && typeof data.isPartial === "boolean" && "messageId" in data && "conversationId" in data;
|
|
3367
|
+
case "audio":
|
|
3368
|
+
return "data" in data && typeof data.data === "string" && "format" in data && typeof data.format === "string" && "messageId" in data && "conversationId" in data;
|
|
2406
3369
|
case "complete":
|
|
2407
3370
|
return "messageId" in data && "conversationId" in data;
|
|
2408
3371
|
case "turn_queued":
|
|
@@ -2502,6 +3465,15 @@ var sendImageMessage = async (socket, chunk, messageId, conversationId) => sendM
|
|
|
2502
3465
|
revisedPrompt: chunk.revisedPrompt,
|
|
2503
3466
|
type: "image"
|
|
2504
3467
|
});
|
|
3468
|
+
var sendAudioMessage = async (socket, chunk, messageId, conversationId) => sendMessage(socket, {
|
|
3469
|
+
audioId: chunk.audioId,
|
|
3470
|
+
conversationId,
|
|
3471
|
+
data: chunk.data,
|
|
3472
|
+
format: chunk.format,
|
|
3473
|
+
messageId,
|
|
3474
|
+
transcript: chunk.transcript,
|
|
3475
|
+
type: "audio"
|
|
3476
|
+
});
|
|
2505
3477
|
var sendToolRunning = async (socket, toolName, toolInput, messageId, conversationId) => sendMessage(socket, {
|
|
2506
3478
|
conversationId,
|
|
2507
3479
|
input: toolInput,
|
|
@@ -2595,6 +3567,15 @@ var processToolChunk = (chunk, state, options, socket, messageId, conversationId
|
|
|
2595
3567
|
revisedPrompt: chunk.revisedPrompt
|
|
2596
3568
|
});
|
|
2597
3569
|
break;
|
|
3570
|
+
case "audio":
|
|
3571
|
+
sendAudioMessage(socket, chunk, messageId, conversationId);
|
|
3572
|
+
options.onAudio?.({
|
|
3573
|
+
audioId: chunk.audioId,
|
|
3574
|
+
data: chunk.data,
|
|
3575
|
+
format: chunk.format,
|
|
3576
|
+
transcript: chunk.transcript
|
|
3577
|
+
});
|
|
3578
|
+
break;
|
|
2598
3579
|
case "tool_use":
|
|
2599
3580
|
flushThinking(state);
|
|
2600
3581
|
handleToolChunkToolUse(chunk, state);
|
|
@@ -2602,6 +3583,7 @@ var processToolChunk = (chunk, state, options, socket, messageId, conversationId
|
|
|
2602
3583
|
id: chunk.id,
|
|
2603
3584
|
input: typeof chunk.input === "object" && chunk.input !== null ? chunk.input : {},
|
|
2604
3585
|
name: chunk.name,
|
|
3586
|
+
providerData: chunk.providerData,
|
|
2605
3587
|
type: "tool_use"
|
|
2606
3588
|
});
|
|
2607
3589
|
hitAnotherTool = true;
|
|
@@ -2644,6 +3626,7 @@ var processToolTurn = async (socket, options, state, messageId, conversationId,
|
|
|
2644
3626
|
messages: state.currentMessages,
|
|
2645
3627
|
model: options.model,
|
|
2646
3628
|
promptCaching: options.promptCaching,
|
|
3629
|
+
providerOptions: options.providerOptions,
|
|
2647
3630
|
reasoning: options.reasoning,
|
|
2648
3631
|
signal,
|
|
2649
3632
|
systemPrompt: options.systemPrompt,
|
|
@@ -2730,6 +3713,7 @@ var processStream = async (socket, options, messages, messageId, conversationId,
|
|
|
2730
3713
|
messages,
|
|
2731
3714
|
model: options.model,
|
|
2732
3715
|
promptCaching: options.promptCaching,
|
|
3716
|
+
providerOptions: options.providerOptions,
|
|
2733
3717
|
reasoning: options.reasoning,
|
|
2734
3718
|
signal,
|
|
2735
3719
|
systemPrompt: options.systemPrompt,
|
|
@@ -2787,6 +3771,15 @@ var consumeStreamChunk = async (chunk, options, socket, state, messageId, conver
|
|
|
2787
3771
|
revisedPrompt: chunk.revisedPrompt
|
|
2788
3772
|
});
|
|
2789
3773
|
break;
|
|
3774
|
+
case "audio":
|
|
3775
|
+
await sendAudioMessage(socket, chunk, messageId, conversationId);
|
|
3776
|
+
options.onAudio?.({
|
|
3777
|
+
audioId: chunk.audioId,
|
|
3778
|
+
data: chunk.data,
|
|
3779
|
+
format: chunk.format,
|
|
3780
|
+
transcript: chunk.transcript
|
|
3781
|
+
});
|
|
3782
|
+
break;
|
|
2790
3783
|
case "tool_use":
|
|
2791
3784
|
flushStreamThinking(state);
|
|
2792
3785
|
state.pendingToolCalls.push({
|
|
@@ -2798,6 +3791,7 @@ var consumeStreamChunk = async (chunk, options, socket, state, messageId, conver
|
|
|
2798
3791
|
id: chunk.id,
|
|
2799
3792
|
input: typeof chunk.input === "object" && chunk.input !== null ? chunk.input : {},
|
|
2800
3793
|
name: chunk.name,
|
|
3794
|
+
providerData: chunk.providerData,
|
|
2801
3795
|
type: "tool_use"
|
|
2802
3796
|
});
|
|
2803
3797
|
break;
|
|
@@ -2898,6 +3892,10 @@ var serializeToolCall2 = (name, input) => `${name}:${JSON.stringify(input)}`;
|
|
|
2898
3892
|
var contentEvent = (options, renderers, delta, full) => options.structuredEvents ? { data: JSON.stringify({ delta, full }), event: "content" } : { data: renderers.chunk(delta, full), event: "content" };
|
|
2899
3893
|
var thinkingEvent = (options, renderers, text) => options.structuredEvents ? { data: JSON.stringify({ text }), event: "thinking" } : { data: renderers.thinking(text), event: "thinking" };
|
|
2900
3894
|
var imageEvent = (options, renderers, data, format, revisedPrompt) => options.structuredEvents ? { data: JSON.stringify({ data, format, revisedPrompt }), event: "images" } : { data: renderers.image(data, format, revisedPrompt), event: "images" };
|
|
3895
|
+
var audioEvent = (data, format, transcript, audioId) => ({
|
|
3896
|
+
data: JSON.stringify({ audioId, data, format, transcript }),
|
|
3897
|
+
event: "audio"
|
|
3898
|
+
});
|
|
2901
3899
|
var completeEvent = (options, renderers, fullResponse, usage, durationMs) => {
|
|
2902
3900
|
options.onComplete?.(fullResponse, usage);
|
|
2903
3901
|
return options.structuredEvents ? {
|
|
@@ -2967,6 +3965,15 @@ var processImageChunk = function* (chunk, renderers, options) {
|
|
|
2967
3965
|
revisedPrompt: chunk.revisedPrompt
|
|
2968
3966
|
});
|
|
2969
3967
|
};
|
|
3968
|
+
var processAudioChunk = function* (chunk, options) {
|
|
3969
|
+
yield audioEvent(chunk.data, chunk.format, chunk.transcript, chunk.audioId);
|
|
3970
|
+
options.onAudio?.({
|
|
3971
|
+
audioId: chunk.audioId,
|
|
3972
|
+
data: chunk.data,
|
|
3973
|
+
format: chunk.format,
|
|
3974
|
+
transcript: chunk.transcript
|
|
3975
|
+
});
|
|
3976
|
+
};
|
|
2970
3977
|
var processToolUseChunk = (chunk, chunkState) => {
|
|
2971
3978
|
maybeFlushThinking(chunkState);
|
|
2972
3979
|
chunkState.pendingToolCalls.push({
|
|
@@ -2978,6 +3985,7 @@ var processToolUseChunk = (chunk, chunkState) => {
|
|
|
2978
3985
|
id: chunk.id,
|
|
2979
3986
|
input: typeof chunk.input === "object" && chunk.input !== null ? chunk.input : {},
|
|
2980
3987
|
name: chunk.name,
|
|
3988
|
+
providerData: chunk.providerData,
|
|
2981
3989
|
type: "tool_use"
|
|
2982
3990
|
});
|
|
2983
3991
|
};
|
|
@@ -2992,6 +4000,9 @@ var processChunk2 = function* (chunk, chunkState, renderers, options, fullRespon
|
|
|
2992
4000
|
case "image":
|
|
2993
4001
|
yield* processImageChunk(chunk, renderers, options);
|
|
2994
4002
|
break;
|
|
4003
|
+
case "audio":
|
|
4004
|
+
yield* processAudioChunk(chunk, options);
|
|
4005
|
+
break;
|
|
2995
4006
|
case "tool_use":
|
|
2996
4007
|
processToolUseChunk(chunk, chunkState);
|
|
2997
4008
|
break;
|
|
@@ -3111,6 +4122,7 @@ var streamTurns = async function* (options, renderers, messages, signal, startTi
|
|
|
3111
4122
|
messages: turnState.currentMessages,
|
|
3112
4123
|
model: options.model,
|
|
3113
4124
|
promptCaching: options.promptCaching,
|
|
4125
|
+
providerOptions: options.providerOptions,
|
|
3114
4126
|
reasoning: options.reasoning,
|
|
3115
4127
|
signal,
|
|
3116
4128
|
systemPrompt: options.systemPrompt,
|
|
@@ -3123,6 +4135,13 @@ var streamTurns = async function* (options, renderers, messages, signal, startTi
|
|
|
3123
4135
|
aggregateUsage.outputTokens += chunkState.usage?.outputTokens ?? 0;
|
|
3124
4136
|
aggregateUsage.cacheReadInputTokens = (aggregateUsage.cacheReadInputTokens ?? 0) + (chunkState.usage?.cacheReadInputTokens ?? 0);
|
|
3125
4137
|
aggregateUsage.cacheWriteInputTokens = (aggregateUsage.cacheWriteInputTokens ?? 0) + (chunkState.usage?.cacheWriteInputTokens ?? 0);
|
|
4138
|
+
aggregateUsage.costCredits = (aggregateUsage.costCredits ?? 0) + (chunkState.usage?.costCredits ?? 0);
|
|
4139
|
+
aggregateUsage.reasoningTokens = (aggregateUsage.reasoningTokens ?? 0) + (chunkState.usage?.reasoningTokens ?? 0);
|
|
4140
|
+
for (const [key, value] of Object.entries(chunkState.usage?.serverToolUse ?? {})) {
|
|
4141
|
+
aggregateUsage.serverToolUse ??= {};
|
|
4142
|
+
aggregateUsage.serverToolUse[key] = (aggregateUsage.serverToolUse[key] ?? 0) + value;
|
|
4143
|
+
}
|
|
4144
|
+
aggregateUsage.upstreamInferenceCostCredits = (aggregateUsage.upstreamInferenceCostCredits ?? 0) + (chunkState.usage?.upstreamInferenceCostCredits ?? 0);
|
|
3126
4145
|
const runningTotalTokens = aggregateUsage.inputTokens + aggregateUsage.outputTokens;
|
|
3127
4146
|
if (chunkState.stopReason === "max_tokens") {
|
|
3128
4147
|
finishReason = "max_tokens";
|
|
@@ -3606,6 +4625,7 @@ var generateAI = async (options) => {
|
|
|
3606
4625
|
messages: options.messages,
|
|
3607
4626
|
model: options.model,
|
|
3608
4627
|
promptCaching: options.promptCaching,
|
|
4628
|
+
providerOptions: options.providerOptions,
|
|
3609
4629
|
reasoning: options.reasoning,
|
|
3610
4630
|
responseFormat: options.responseFormat,
|
|
3611
4631
|
signal: options.signal,
|
|
@@ -3618,17 +4638,22 @@ var generateAI = async (options) => {
|
|
|
3618
4638
|
});
|
|
3619
4639
|
let text = "";
|
|
3620
4640
|
const toolCalls = [];
|
|
4641
|
+
const citations = [];
|
|
3621
4642
|
let usage;
|
|
4643
|
+
let metadata;
|
|
3622
4644
|
for await (const chunk of stream) {
|
|
3623
4645
|
if (chunk.type === "text") {
|
|
3624
4646
|
text += chunk.content;
|
|
3625
4647
|
} else if (chunk.type === "tool_use") {
|
|
3626
4648
|
toolCalls.push({ id: chunk.id, input: chunk.input, name: chunk.name });
|
|
4649
|
+
} else if (chunk.type === "citation") {
|
|
4650
|
+
citations.push(chunk);
|
|
3627
4651
|
} else if (chunk.type === "done") {
|
|
3628
4652
|
usage = chunk.usage;
|
|
4653
|
+
metadata = chunk.metadata;
|
|
3629
4654
|
}
|
|
3630
4655
|
}
|
|
3631
|
-
return { text, toolCalls, usage };
|
|
4656
|
+
return { citations, metadata, text, toolCalls, usage };
|
|
3632
4657
|
};
|
|
3633
4658
|
var DEFAULT_TOOL_MAX_TURNS = 6;
|
|
3634
4659
|
var mergeUsage = (left, right) => {
|
|
@@ -3637,11 +4662,18 @@ var mergeUsage = (left, right) => {
|
|
|
3637
4662
|
if (!right)
|
|
3638
4663
|
return left;
|
|
3639
4664
|
const add = (a, b) => a === undefined && b === undefined ? undefined : (a ?? 0) + (b ?? 0);
|
|
4665
|
+
const serverToolUse = { ...left.serverToolUse };
|
|
4666
|
+
for (const [key, value] of Object.entries(right.serverToolUse ?? {}))
|
|
4667
|
+
serverToolUse[key] = (serverToolUse[key] ?? 0) + value;
|
|
3640
4668
|
return {
|
|
3641
4669
|
cacheReadInputTokens: add(left.cacheReadInputTokens, right.cacheReadInputTokens),
|
|
3642
4670
|
cacheWriteInputTokens: add(left.cacheWriteInputTokens, right.cacheWriteInputTokens),
|
|
4671
|
+
costCredits: add(left.costCredits, right.costCredits),
|
|
3643
4672
|
inputTokens: (left.inputTokens ?? 0) + (right.inputTokens ?? 0),
|
|
3644
|
-
outputTokens: (left.outputTokens ?? 0) + (right.outputTokens ?? 0)
|
|
4673
|
+
outputTokens: (left.outputTokens ?? 0) + (right.outputTokens ?? 0),
|
|
4674
|
+
reasoningTokens: add(left.reasoningTokens, right.reasoningTokens),
|
|
4675
|
+
serverToolUse: Object.keys(serverToolUse).length > 0 ? serverToolUse : undefined,
|
|
4676
|
+
upstreamInferenceCostCredits: add(left.upstreamInferenceCostCredits, right.upstreamInferenceCostCredits)
|
|
3645
4677
|
};
|
|
3646
4678
|
};
|
|
3647
4679
|
var toProviderTools = (tools) => Object.entries(tools).map(([name, definition]) => ({
|
|
@@ -3825,6 +4857,7 @@ var streamAIWithTools = async function* (options) {
|
|
|
3825
4857
|
messages,
|
|
3826
4858
|
model: base.model,
|
|
3827
4859
|
promptCaching: base.promptCaching,
|
|
4860
|
+
providerOptions: base.providerOptions,
|
|
3828
4861
|
reasoning: base.reasoning,
|
|
3829
4862
|
signal: base.signal,
|
|
3830
4863
|
stopSequences: base.stopSequences,
|
|
@@ -3853,6 +4886,14 @@ var streamAIWithTools = async function* (options) {
|
|
|
3853
4886
|
fullText += chunk.content;
|
|
3854
4887
|
pushText(blocks, chunk.content);
|
|
3855
4888
|
yield { content: chunk.content, type: "text" };
|
|
4889
|
+
} else if (chunk.type === "audio") {
|
|
4890
|
+
yield {
|
|
4891
|
+
audioId: chunk.audioId,
|
|
4892
|
+
data: chunk.data,
|
|
4893
|
+
format: chunk.format,
|
|
4894
|
+
transcript: chunk.transcript,
|
|
4895
|
+
type: "audio"
|
|
4896
|
+
};
|
|
3856
4897
|
} else if (chunk.type === "tool_use") {
|
|
3857
4898
|
thinking = flushThinking3(blocks, thinking);
|
|
3858
4899
|
pending.push({ id: chunk.id, input: chunk.input, name: chunk.name });
|
|
@@ -3860,8 +4901,16 @@ var streamAIWithTools = async function* (options) {
|
|
|
3860
4901
|
id: chunk.id,
|
|
3861
4902
|
input: chunk.input && typeof chunk.input === "object" ? chunk.input : {},
|
|
3862
4903
|
name: chunk.name,
|
|
4904
|
+
providerData: chunk.providerData,
|
|
3863
4905
|
type: "tool_use"
|
|
3864
4906
|
});
|
|
4907
|
+
} else if (chunk.type === "provider_event") {
|
|
4908
|
+
thinking = flushThinking3(blocks, thinking);
|
|
4909
|
+
blocks.push({
|
|
4910
|
+
data: chunk.data,
|
|
4911
|
+
provider: chunk.provider,
|
|
4912
|
+
type: "provider_data"
|
|
4913
|
+
});
|
|
3865
4914
|
} else if (chunk.type === "done") {
|
|
3866
4915
|
thinking = flushThinking3(blocks, thinking);
|
|
3867
4916
|
turnUsage = chunk.usage;
|
|
@@ -5406,6 +6455,16 @@ var serverMessageToAction = (message) => {
|
|
|
5406
6455
|
revisedPrompt: message.revisedPrompt,
|
|
5407
6456
|
type: "image"
|
|
5408
6457
|
};
|
|
6458
|
+
case "audio":
|
|
6459
|
+
return {
|
|
6460
|
+
audioId: message.audioId,
|
|
6461
|
+
conversationId: message.conversationId,
|
|
6462
|
+
data: message.data,
|
|
6463
|
+
format: message.format,
|
|
6464
|
+
messageId: message.messageId,
|
|
6465
|
+
transcript: message.transcript,
|
|
6466
|
+
type: "audio"
|
|
6467
|
+
};
|
|
5409
6468
|
case "complete":
|
|
5410
6469
|
return {
|
|
5411
6470
|
conversationId: message.conversationId,
|
|
@@ -5808,6 +6867,20 @@ var handleImage = (state, action) => {
|
|
|
5808
6867
|
});
|
|
5809
6868
|
conversation.messages = [...conversation.messages];
|
|
5810
6869
|
};
|
|
6870
|
+
var handleAudio = (state, action) => {
|
|
6871
|
+
const conversation = getOrCreate(state, action.conversationId);
|
|
6872
|
+
const message = getOrCreateAssistantMessage(conversation, action.messageId, action.conversationId);
|
|
6873
|
+
message.audio = [
|
|
6874
|
+
...message.audio ?? [],
|
|
6875
|
+
{
|
|
6876
|
+
audioId: action.audioId,
|
|
6877
|
+
data: action.data,
|
|
6878
|
+
format: action.format,
|
|
6879
|
+
transcript: action.transcript
|
|
6880
|
+
}
|
|
6881
|
+
];
|
|
6882
|
+
conversation.messages = [...conversation.messages];
|
|
6883
|
+
};
|
|
5811
6884
|
var handleComplete = (state, action) => {
|
|
5812
6885
|
const conversation = state.conversations.get(action.conversationId);
|
|
5813
6886
|
if (conversation) {
|
|
@@ -5878,6 +6951,9 @@ var applyAction = (state, action) => {
|
|
|
5878
6951
|
case "image":
|
|
5879
6952
|
handleImage(state, action);
|
|
5880
6953
|
break;
|
|
6954
|
+
case "audio":
|
|
6955
|
+
handleAudio(state, action);
|
|
6956
|
+
break;
|
|
5881
6957
|
case "complete":
|
|
5882
6958
|
handleComplete(state, action);
|
|
5883
6959
|
break;
|
|
@@ -6197,6 +7273,7 @@ var startProviderStatusMonitor = (options) => {
|
|
|
6197
7273
|
export {
|
|
6198
7274
|
xai,
|
|
6199
7275
|
withResilience,
|
|
7276
|
+
verifyOpenRouterWebhookSignature,
|
|
6200
7277
|
tableCard,
|
|
6201
7278
|
streamAIWithTools,
|
|
6202
7279
|
streamAIToSSE,
|
|
@@ -6223,9 +7300,13 @@ export {
|
|
|
6223
7300
|
parseChoiceSpec,
|
|
6224
7301
|
parseChartSpec,
|
|
6225
7302
|
parseAIMessage,
|
|
7303
|
+
openrouterResponses,
|
|
7304
|
+
openrouterMessages,
|
|
7305
|
+
openrouter,
|
|
6226
7306
|
openaiResponses,
|
|
6227
7307
|
openaiCompatible,
|
|
6228
7308
|
openai,
|
|
7309
|
+
openRouterModelMatchesRule,
|
|
6229
7310
|
ollama,
|
|
6230
7311
|
moonshot,
|
|
6231
7312
|
mistralai,
|
|
@@ -6245,6 +7326,7 @@ export {
|
|
|
6245
7326
|
createUiCards,
|
|
6246
7327
|
createSyncConversationStore,
|
|
6247
7328
|
createProviderProxyResponse,
|
|
7329
|
+
createOpenRouterClient,
|
|
6248
7330
|
createOAuth2ClientCredentialsTokenSource,
|
|
6249
7331
|
createMemoryStore,
|
|
6250
7332
|
createConversationManager,
|
|
@@ -6282,5 +7364,5 @@ export {
|
|
|
6282
7364
|
BUILTIN_UI_CARDS
|
|
6283
7365
|
};
|
|
6284
7366
|
|
|
6285
|
-
//# debugId=
|
|
7367
|
+
//# debugId=3B5ACA11D1118B9564756E2164756E21
|
|
6286
7368
|
//# sourceMappingURL=index.js.map
|