@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
|
@@ -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
|
};
|
|
@@ -398,7 +402,7 @@ var mapBlockToResponsesFormat = (block) => {
|
|
|
398
402
|
if (block.type === "image") {
|
|
399
403
|
return {
|
|
400
404
|
image_url: {
|
|
401
|
-
url: `data:${block.source.media_type};base64,${block.source.data}`
|
|
405
|
+
url: block.source.type === "url" ? block.source.url : `data:${block.source.media_type};base64,${block.source.data}`
|
|
402
406
|
},
|
|
403
407
|
type: "input_image"
|
|
404
408
|
};
|
|
@@ -406,12 +410,24 @@ var mapBlockToResponsesFormat = (block) => {
|
|
|
406
410
|
if (block.type === "document") {
|
|
407
411
|
return {
|
|
408
412
|
file: {
|
|
409
|
-
file_data: `data:${block.source.media_type};base64,${block.source.data}`,
|
|
413
|
+
file_data: block.source.type === "url" ? block.source.url : `data:${block.source.media_type};base64,${block.source.data}`,
|
|
410
414
|
filename: block.name ?? "document.pdf"
|
|
411
415
|
},
|
|
412
416
|
type: "input_file"
|
|
413
417
|
};
|
|
414
418
|
}
|
|
419
|
+
if (block.type === "audio") {
|
|
420
|
+
return {
|
|
421
|
+
input_audio: { data: block.source.data, format: block.source.format },
|
|
422
|
+
type: "input_audio"
|
|
423
|
+
};
|
|
424
|
+
}
|
|
425
|
+
if (block.type === "video") {
|
|
426
|
+
return {
|
|
427
|
+
type: "input_video",
|
|
428
|
+
video_url: block.source.type === "url" ? block.source.url : `data:${block.source.media_type};base64,${block.source.data}`
|
|
429
|
+
};
|
|
430
|
+
}
|
|
415
431
|
return null;
|
|
416
432
|
};
|
|
417
433
|
var mapContentToResponsesFormat = (content) => {
|
|
@@ -423,7 +439,12 @@ var mapContentToResponsesFormat = (content) => {
|
|
|
423
439
|
};
|
|
424
440
|
var hasToolBlocks = (content) => content.some((block) => block.type === "tool_use" || block.type === "tool_result");
|
|
425
441
|
var convertToolBlock = (block) => {
|
|
442
|
+
if (block.type === "provider_data" && block.provider === "openrouter") {
|
|
443
|
+
return { ...block.data };
|
|
444
|
+
}
|
|
426
445
|
if (block.type === "tool_use") {
|
|
446
|
+
if (block.providerData)
|
|
447
|
+
return { ...block.providerData };
|
|
427
448
|
return {
|
|
428
449
|
arguments: typeof block.input === "string" ? block.input : JSON.stringify(block.input),
|
|
429
450
|
call_id: block.id,
|
|
@@ -475,7 +496,7 @@ var buildTools = (tools, isImageModel) => {
|
|
|
475
496
|
}
|
|
476
497
|
return result.length > 0 ? result : undefined;
|
|
477
498
|
};
|
|
478
|
-
var buildRequestBody = (params, isImageModel) => {
|
|
499
|
+
var buildRequestBody = (params, isImageModel, capabilityModel = params.model) => {
|
|
479
500
|
const body = {
|
|
480
501
|
input: buildInput(params.messages),
|
|
481
502
|
model: params.model,
|
|
@@ -527,8 +548,8 @@ var buildRequestBody = (params, isImageModel) => {
|
|
|
527
548
|
};
|
|
528
549
|
}
|
|
529
550
|
}
|
|
530
|
-
if (params.reasoning && isOpenAIReasoningModel(
|
|
531
|
-
const effort = openaiEffortValue(
|
|
551
|
+
if (params.reasoning && isOpenAIReasoningModel(capabilityModel)) {
|
|
552
|
+
const effort = openaiEffortValue(capabilityModel, params.reasoning);
|
|
532
553
|
if (effort) {
|
|
533
554
|
body.reasoning = {
|
|
534
555
|
effort,
|
|
@@ -559,11 +580,32 @@ var extractUsage = (response) => {
|
|
|
559
580
|
const { usage } = response;
|
|
560
581
|
const input = typeof usage.input_tokens === "number" ? usage.input_tokens : 0;
|
|
561
582
|
const cached = isRecord(usage.input_tokens_details) && typeof usage.input_tokens_details.cached_tokens === "number" ? usage.input_tokens_details.cached_tokens : 0;
|
|
562
|
-
|
|
583
|
+
const outputDetails = isRecord(usage.output_tokens_details) ? usage.output_tokens_details : undefined;
|
|
584
|
+
const inputDetails = isRecord(usage.input_tokens_details) ? usage.input_tokens_details : undefined;
|
|
585
|
+
const costDetails = isRecord(usage.cost_details) ? usage.cost_details : undefined;
|
|
586
|
+
const normalized = {
|
|
563
587
|
cacheReadInputTokens: cached,
|
|
588
|
+
cacheWriteInputTokens: inputDetails && typeof inputDetails.cache_write_tokens === "number" ? inputDetails.cache_write_tokens : undefined,
|
|
589
|
+
costCredits: typeof usage.cost === "number" ? usage.cost : undefined,
|
|
564
590
|
inputTokens: Math.max(0, input - cached),
|
|
565
|
-
outputTokens: typeof usage.output_tokens === "number" ? usage.output_tokens : 0
|
|
591
|
+
outputTokens: typeof usage.output_tokens === "number" ? usage.output_tokens : 0,
|
|
592
|
+
reasoningTokens: outputDetails && typeof outputDetails.reasoning_tokens === "number" ? outputDetails.reasoning_tokens : undefined,
|
|
593
|
+
upstreamInferenceCostCredits: costDetails && typeof costDetails.upstream_inference_cost === "number" ? costDetails.upstream_inference_cost : undefined
|
|
566
594
|
};
|
|
595
|
+
if (isRecord(usage.server_tool_use)) {
|
|
596
|
+
normalized.serverToolUse = Object.fromEntries(Object.entries(usage.server_tool_use).filter((entry) => typeof entry[1] === "number"));
|
|
597
|
+
}
|
|
598
|
+
return normalized;
|
|
599
|
+
};
|
|
600
|
+
var extractResponseMetadata = (response) => {
|
|
601
|
+
const providerMetadata = isRecord(response.openrouter_metadata) ? response.openrouter_metadata : undefined;
|
|
602
|
+
const generationId = typeof response.id === "string" ? response.id : undefined;
|
|
603
|
+
const model = typeof response.model === "string" ? response.model : undefined;
|
|
604
|
+
const provider = typeof response.provider === "string" ? response.provider : undefined;
|
|
605
|
+
const serviceTier = typeof response.service_tier === "string" ? response.service_tier : undefined;
|
|
606
|
+
if (!providerMetadata && !generationId && !model && !provider && !serviceTier)
|
|
607
|
+
return;
|
|
608
|
+
return { generationId, model, provider, providerMetadata, serviceTier };
|
|
567
609
|
};
|
|
568
610
|
var extractMimeFormat = (mimeType) => {
|
|
569
611
|
if (typeof mimeType !== "string") {
|
|
@@ -620,6 +662,7 @@ var processFunctionCallArgumentsDone = function* (parsed, pendingCalls) {
|
|
|
620
662
|
id: callId || pending?.callId || itemId,
|
|
621
663
|
input: parseToolInput(args),
|
|
622
664
|
name,
|
|
665
|
+
providerData: pending?.providerData ? { ...pending.providerData, arguments: args } : undefined,
|
|
623
666
|
type: "tool_use"
|
|
624
667
|
};
|
|
625
668
|
};
|
|
@@ -638,9 +681,21 @@ var processOutputItemAdded = (parsed, pendingCalls) => {
|
|
|
638
681
|
pendingCalls.set(itemId, {
|
|
639
682
|
arguments: "",
|
|
640
683
|
callId,
|
|
641
|
-
name
|
|
684
|
+
name,
|
|
685
|
+
providerData: { ...item }
|
|
642
686
|
});
|
|
643
687
|
};
|
|
688
|
+
var processOutputItemDone = function* (parsed) {
|
|
689
|
+
if (!isRecord(parsed.item) || typeof parsed.item.type !== "string")
|
|
690
|
+
return;
|
|
691
|
+
if (!parsed.item.type.startsWith("openrouter:"))
|
|
692
|
+
return;
|
|
693
|
+
yield {
|
|
694
|
+
data: { ...parsed.item },
|
|
695
|
+
provider: "openrouter",
|
|
696
|
+
type: "provider_event"
|
|
697
|
+
};
|
|
698
|
+
};
|
|
644
699
|
var isCompletedImageGeneration = (item) => item.type === "image_generation_call" && item.status === "completed" && typeof item.result === "string" && item.result !== "";
|
|
645
700
|
var buildImageChunk = (item) => ({
|
|
646
701
|
data: typeof item.result === "string" ? item.result : "",
|
|
@@ -656,6 +711,30 @@ var extractImageFromOutput = function* (output) {
|
|
|
656
711
|
yield buildImageChunk(item);
|
|
657
712
|
}
|
|
658
713
|
};
|
|
714
|
+
var extractCitationsFromOutput = function* (output) {
|
|
715
|
+
for (const item of output) {
|
|
716
|
+
if (!isRecordArray(item.content))
|
|
717
|
+
continue;
|
|
718
|
+
for (const content of item.content) {
|
|
719
|
+
if (!Array.isArray(content.annotations))
|
|
720
|
+
continue;
|
|
721
|
+
for (const annotation of content.annotations) {
|
|
722
|
+
if (!isRecord(annotation) || annotation.type !== "url_citation")
|
|
723
|
+
continue;
|
|
724
|
+
if (typeof annotation.url !== "string")
|
|
725
|
+
continue;
|
|
726
|
+
yield {
|
|
727
|
+
content: typeof annotation.content === "string" ? annotation.content : undefined,
|
|
728
|
+
endIndex: typeof annotation.end_index === "number" ? annotation.end_index : undefined,
|
|
729
|
+
startIndex: typeof annotation.start_index === "number" ? annotation.start_index : undefined,
|
|
730
|
+
title: typeof annotation.title === "string" ? annotation.title : undefined,
|
|
731
|
+
type: "citation",
|
|
732
|
+
url: annotation.url
|
|
733
|
+
};
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
};
|
|
659
738
|
var processCompleted = function* (parsed) {
|
|
660
739
|
if (!isRecord(parsed.response)) {
|
|
661
740
|
yield { type: "done", usage: undefined };
|
|
@@ -663,12 +742,26 @@ var processCompleted = function* (parsed) {
|
|
|
663
742
|
}
|
|
664
743
|
const { response } = parsed;
|
|
665
744
|
const usage = extractUsage(response);
|
|
745
|
+
const metadata = extractResponseMetadata(response);
|
|
666
746
|
if (isRecordArray(response.output)) {
|
|
747
|
+
yield* extractCitationsFromOutput(response.output);
|
|
667
748
|
yield* extractImageFromOutput(response.output);
|
|
668
749
|
}
|
|
669
|
-
yield { type: "done", usage };
|
|
750
|
+
yield { metadata, type: "done", usage };
|
|
670
751
|
};
|
|
671
|
-
var
|
|
752
|
+
var responseFailure = (eventType, parsed, providerName) => {
|
|
753
|
+
const response = isRecord(parsed.response) ? parsed.response : parsed;
|
|
754
|
+
const error = isRecord(response.error) ? response.error : undefined;
|
|
755
|
+
const type = typeof response.error_type === "string" ? response.error_type : error && typeof error.code === "string" ? error.code : eventType;
|
|
756
|
+
return new ProviderError({
|
|
757
|
+
message: error && typeof error.message === "string" ? error.message : `OpenRouter Responses API: ${eventType}`,
|
|
758
|
+
metadata: response,
|
|
759
|
+
provider: providerName,
|
|
760
|
+
retryable: type === "rate_limit_exceeded" || type === "provider_overloaded" || type === "provider_unavailable" || type === "server",
|
|
761
|
+
type
|
|
762
|
+
});
|
|
763
|
+
};
|
|
764
|
+
var processSSEEvent = function* (eventType, parsed, pendingCalls, providerName) {
|
|
672
765
|
switch (eventType) {
|
|
673
766
|
case "response.reasoning_summary_text.delta": {
|
|
674
767
|
const delta = typeof parsed.delta === "string" ? parsed.delta : "";
|
|
@@ -689,6 +782,9 @@ var processSSEEvent = function* (eventType, parsed, pendingCalls) {
|
|
|
689
782
|
case "response.output_item.added":
|
|
690
783
|
processOutputItemAdded(parsed, pendingCalls);
|
|
691
784
|
break;
|
|
785
|
+
case "response.output_item.done":
|
|
786
|
+
yield* processOutputItemDone(parsed);
|
|
787
|
+
break;
|
|
692
788
|
case "response.function_call_arguments.delta":
|
|
693
789
|
processFunctionCallArgumentsDelta(parsed, pendingCalls);
|
|
694
790
|
break;
|
|
@@ -699,11 +795,10 @@ var processSSEEvent = function* (eventType, parsed, pendingCalls) {
|
|
|
699
795
|
yield* processCompleted(parsed);
|
|
700
796
|
break;
|
|
701
797
|
case "response.failed":
|
|
702
|
-
case "response.incomplete":
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
throw
|
|
706
|
-
}
|
|
798
|
+
case "response.incomplete":
|
|
799
|
+
case "response.error":
|
|
800
|
+
case "error":
|
|
801
|
+
throw responseFailure(eventType, parsed, providerName);
|
|
707
802
|
}
|
|
708
803
|
};
|
|
709
804
|
var flushSSEBuffer = function* (state) {
|
|
@@ -712,7 +807,7 @@ var flushSSEBuffer = function* (state) {
|
|
|
712
807
|
}
|
|
713
808
|
const parsed = parseJSON(state.buffer);
|
|
714
809
|
if (parsed) {
|
|
715
|
-
yield* processSSEEvent(state.currentEvent, parsed, state.pendingCalls);
|
|
810
|
+
yield* processSSEEvent(state.currentEvent, parsed, state.pendingCalls, state.providerName);
|
|
716
811
|
}
|
|
717
812
|
state.currentEvent = "";
|
|
718
813
|
state.buffer = "";
|
|
@@ -750,41 +845,47 @@ var drainReader = async function* (reader, decoder, state, signal) {
|
|
|
750
845
|
yield* processSSELines([textBuffer, ""], state);
|
|
751
846
|
}
|
|
752
847
|
};
|
|
753
|
-
var parseSSEStream = async function* (body, signal) {
|
|
848
|
+
var parseSSEStream = async function* (body, providerName, signal) {
|
|
754
849
|
const reader = body.getReader();
|
|
755
850
|
const decoder = new TextDecoder;
|
|
756
851
|
const state = {
|
|
757
852
|
buffer: "",
|
|
758
853
|
currentEvent: "",
|
|
759
854
|
pendingCalls: new Map,
|
|
760
|
-
usage: undefined
|
|
855
|
+
usage: undefined,
|
|
856
|
+
providerName
|
|
761
857
|
};
|
|
762
858
|
try {
|
|
763
859
|
yield* drainReader(reader, decoder, state, signal);
|
|
860
|
+
yield* flushSSEBuffer(state);
|
|
764
861
|
} finally {
|
|
765
862
|
reader.releaseLock();
|
|
766
863
|
}
|
|
767
864
|
};
|
|
768
|
-
var fetchResponsesStream = async function* (baseUrl, apiKey, body, signal) {
|
|
865
|
+
var fetchResponsesStream = async function* (baseUrl, apiKey, body, fetchImpl, headers, providerName, signal) {
|
|
769
866
|
const target = `${baseUrl}/v1/responses`;
|
|
770
|
-
const
|
|
867
|
+
const requestHeaders = new Headers(headers);
|
|
868
|
+
requestHeaders.set("Authorization", `Bearer ${apiKey}`);
|
|
869
|
+
requestHeaders.set("Content-Type", "application/json");
|
|
870
|
+
const response = await fetchImpl(target, {
|
|
771
871
|
...h2IfHttps(target),
|
|
772
872
|
body: JSON.stringify(body),
|
|
773
|
-
headers:
|
|
774
|
-
Authorization: `Bearer ${apiKey}`,
|
|
775
|
-
"Content-Type": "application/json"
|
|
776
|
-
},
|
|
873
|
+
headers: requestHeaders,
|
|
777
874
|
method: "POST",
|
|
778
875
|
signal
|
|
779
876
|
});
|
|
780
877
|
if (!response.ok) {
|
|
781
878
|
const errorText = await response.text();
|
|
782
|
-
throw
|
|
879
|
+
throw ProviderError.fromResponse(providerName, response.status, errorText);
|
|
783
880
|
}
|
|
784
881
|
if (!response.body) {
|
|
785
|
-
throw new
|
|
882
|
+
throw new ProviderError({
|
|
883
|
+
message: `${providerName} Responses API returned no response body`,
|
|
884
|
+
provider: providerName,
|
|
885
|
+
retryable: true
|
|
886
|
+
});
|
|
786
887
|
}
|
|
787
|
-
yield* parseSSEStream(response.body, signal);
|
|
888
|
+
yield* parseSSEStream(response.body, providerName, signal);
|
|
788
889
|
};
|
|
789
890
|
var resolveImageModels = (imageModels) => {
|
|
790
891
|
if (!imageModels) {
|
|
@@ -796,19 +897,32 @@ var resolveImageModels = (imageModels) => {
|
|
|
796
897
|
return new Set(imageModels);
|
|
797
898
|
};
|
|
798
899
|
var openaiResponses = (config2) => {
|
|
900
|
+
if (!config2.apiKey && !config2.tokenSource)
|
|
901
|
+
throw new Error("openaiResponses() requires either apiKey or tokenSource");
|
|
799
902
|
const baseUrl = config2.baseUrl ?? DEFAULT_BASE_URL;
|
|
903
|
+
const fetchImpl = config2.fetch ?? globalThis.fetch;
|
|
800
904
|
const imageModels = resolveImageModels(config2.imageModels);
|
|
905
|
+
const providerName = config2.providerName ?? "openai-responses";
|
|
906
|
+
const resolveKey = async () => config2.tokenSource ? await Promise.resolve(config2.tokenSource()) : config2.apiKey;
|
|
907
|
+
const resolveHeaders = async (params) => typeof config2.headers === "function" ? await config2.headers(params) : config2.headers ?? {};
|
|
801
908
|
return instrumentAIProvider({
|
|
802
909
|
stream: (params) => {
|
|
803
910
|
const isImageModel = imageModels.has(params.model);
|
|
804
|
-
const
|
|
805
|
-
|
|
911
|
+
const builtBody = buildRequestBody(params, isImageModel, config2.modelForCapabilities?.(params.model) ?? params.model);
|
|
912
|
+
const body = config2.transformRequestBody ? config2.transformRequestBody(builtBody, params) : builtBody;
|
|
913
|
+
return async function* () {
|
|
914
|
+
const [apiKey, headers] = await Promise.all([
|
|
915
|
+
resolveKey(),
|
|
916
|
+
resolveHeaders(params)
|
|
917
|
+
]);
|
|
918
|
+
yield* fetchResponsesStream(baseUrl, apiKey, body, fetchImpl, headers, providerName, params.signal);
|
|
919
|
+
}();
|
|
806
920
|
}
|
|
807
|
-
},
|
|
921
|
+
}, providerName);
|
|
808
922
|
};
|
|
809
923
|
export {
|
|
810
924
|
openaiResponses
|
|
811
925
|
};
|
|
812
926
|
|
|
813
|
-
//# debugId=
|
|
927
|
+
//# debugId=6FB19A09EAF4F0BB64756E2164756E21
|
|
814
928
|
//# sourceMappingURL=openaiResponses.js.map
|