@broberg/ai-sdk 0.21.1 → 0.22.0
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/dist/index.d.ts +45 -7
- package/dist/index.js +283 -30
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -335,6 +335,11 @@ interface ProviderAdapter {
|
|
|
335
335
|
/** Streaming chat (F8). Optional — absence is a typed "no streaming support".
|
|
336
336
|
* Same request shape as chat; yields ChatStreamEvents as the turn unfolds. */
|
|
337
337
|
chatStream?(req: ChatRequest): AsyncIterable<ChatStreamEvent>;
|
|
338
|
+
/** Dedicated translation engine (F032) — e.g. DeepL. When absent, `ai.translate`
|
|
339
|
+
* falls back to a chat prompt-contract (the historical default for every
|
|
340
|
+
* other provider). `to`/`from` are provider-specific: a chat-routed call
|
|
341
|
+
* accepts free-form names ("Danish"); DeepL requires real codes ("DA"). */
|
|
342
|
+
translate?(req: TranslateRequest): Promise<TranslateResult>;
|
|
338
343
|
vision?(req: ChatRequest): Promise<ChatResult>;
|
|
339
344
|
image?(req: ImageRequest): Promise<ImageResult>;
|
|
340
345
|
/** Image-to-video generation (F024) — animate a still into a short clip. fal. */
|
|
@@ -367,6 +372,15 @@ interface TranslateResult {
|
|
|
367
372
|
text: string;
|
|
368
373
|
usage: Usage;
|
|
369
374
|
}
|
|
375
|
+
/** Dedicated-engine translation request (F032) — used only by adapters that
|
|
376
|
+
* implement `ProviderAdapter.translate` directly (e.g. DeepL); chat-routed
|
|
377
|
+
* providers never see this shape, they get a built prompt instead. */
|
|
378
|
+
interface TranslateRequest {
|
|
379
|
+
text: string;
|
|
380
|
+
to: string;
|
|
381
|
+
from?: string;
|
|
382
|
+
spec: TierSpec;
|
|
383
|
+
}
|
|
370
384
|
|
|
371
385
|
interface MockupInput {
|
|
372
386
|
description: string;
|
|
@@ -1010,12 +1024,12 @@ declare const imageInputSchema: z.ZodObject<{
|
|
|
1010
1024
|
retryOnBlack: z.ZodOptional<z.ZodBoolean>;
|
|
1011
1025
|
}, "strip", z.ZodTypeAny, {
|
|
1012
1026
|
prompt: string;
|
|
1027
|
+
seed?: number | undefined;
|
|
1013
1028
|
purpose?: string | undefined;
|
|
1014
1029
|
loras?: {
|
|
1015
1030
|
path: string;
|
|
1016
1031
|
scale?: number | undefined;
|
|
1017
1032
|
}[] | undefined;
|
|
1018
|
-
seed?: number | undefined;
|
|
1019
1033
|
lora?: string | undefined;
|
|
1020
1034
|
width?: number | undefined;
|
|
1021
1035
|
height?: number | undefined;
|
|
@@ -1039,12 +1053,12 @@ declare const imageInputSchema: z.ZodObject<{
|
|
|
1039
1053
|
retryOnBlack?: boolean | undefined;
|
|
1040
1054
|
}, {
|
|
1041
1055
|
prompt: string;
|
|
1056
|
+
seed?: number | undefined;
|
|
1042
1057
|
purpose?: string | undefined;
|
|
1043
1058
|
loras?: {
|
|
1044
1059
|
path: string;
|
|
1045
1060
|
scale?: number | undefined;
|
|
1046
1061
|
}[] | undefined;
|
|
1047
|
-
seed?: number | undefined;
|
|
1048
1062
|
lora?: string | undefined;
|
|
1049
1063
|
width?: number | undefined;
|
|
1050
1064
|
height?: number | undefined;
|
|
@@ -1804,12 +1818,16 @@ declare function deepinfraAdapter(config?: {
|
|
|
1804
1818
|
baseUrl?: string;
|
|
1805
1819
|
}): ProviderAdapter;
|
|
1806
1820
|
|
|
1807
|
-
|
|
1821
|
+
interface OpenRouterAdapterConfig {
|
|
1808
1822
|
apiKey?: string;
|
|
1809
1823
|
baseUrl?: string;
|
|
1810
1824
|
referer?: string;
|
|
1811
1825
|
title?: string;
|
|
1812
|
-
|
|
1826
|
+
fetch?: typeof fetch;
|
|
1827
|
+
/** Override the per-image USD price (else OPENROUTER_IMAGE_PRICE_ESTIMATE, else 0). */
|
|
1828
|
+
pricePerImage?: number;
|
|
1829
|
+
}
|
|
1830
|
+
declare function openrouterAdapter(config?: OpenRouterAdapterConfig): ProviderAdapter;
|
|
1813
1831
|
|
|
1814
1832
|
declare function requestyAdapter(config?: {
|
|
1815
1833
|
apiKey?: string;
|
|
@@ -1896,6 +1914,26 @@ declare function azureAdapter(config?: {
|
|
|
1896
1914
|
sttBiasingWeight?: number;
|
|
1897
1915
|
}): ProviderAdapter;
|
|
1898
1916
|
|
|
1917
|
+
declare function vertexAdapter(config?: {
|
|
1918
|
+
/** Inline service-account JSON; else env GOOGLE_VERTEX_CREDENTIALS or GOOGLE_APPLICATION_CREDENTIALS (file path). */
|
|
1919
|
+
credentials?: string;
|
|
1920
|
+
/** GCP project id; else env GOOGLE_VERTEX_PROJECT. Required — never guessed. */
|
|
1921
|
+
project?: string;
|
|
1922
|
+
/** Vertex region; default "europe-west1" (EU by default — this adapter's reason to exist). */
|
|
1923
|
+
region?: string;
|
|
1924
|
+
fetch?: typeof fetch;
|
|
1925
|
+
pricePerSecond?: number;
|
|
1926
|
+
pollIntervalMs?: number;
|
|
1927
|
+
videoTimeoutMs?: number;
|
|
1928
|
+
}): ProviderAdapter;
|
|
1929
|
+
|
|
1930
|
+
declare function deeplAdapter(config?: {
|
|
1931
|
+
apiKey?: string;
|
|
1932
|
+
baseUrl?: string;
|
|
1933
|
+
fetch?: typeof fetch;
|
|
1934
|
+
pricePer1kChars?: number;
|
|
1935
|
+
}): ProviderAdapter;
|
|
1936
|
+
|
|
1899
1937
|
interface FalAdapterConfig {
|
|
1900
1938
|
apiKey?: string;
|
|
1901
1939
|
/** "sync" (default — fal.run, fast models) or "queue" (queue.fal.run, polled). */
|
|
@@ -1979,8 +2017,8 @@ declare const falStubAdapter: ProviderAdapter;
|
|
|
1979
2017
|
* wires the live adapters. */
|
|
1980
2018
|
declare const stubProviders: Record<string, ProviderAdapter>;
|
|
1981
2019
|
|
|
1982
|
-
declare const VERSION: "0.
|
|
1983
|
-
declare const SDK_TAG: "@broberg/ai-sdk@0.
|
|
2020
|
+
declare const VERSION: "0.22.0";
|
|
2021
|
+
declare const SDK_TAG: "@broberg/ai-sdk@0.22.0";
|
|
1984
2022
|
|
|
1985
2023
|
/** Built-in defaults. Every entry is overridable via AiConfig.defaults or a
|
|
1986
2024
|
* per-call override.
|
|
@@ -2307,4 +2345,4 @@ interface StreamTransportRequest extends TransportRequest {
|
|
|
2307
2345
|
*/
|
|
2308
2346
|
declare function streamTransport(req: StreamTransportRequest): AsyncIterable<string>;
|
|
2309
2347
|
|
|
2310
|
-
export { AZURE_DANISH_VOICES, AZURE_DANISH_VOICE_LIST, type AiClient, type AiConfig, type AzureVoiceInfo, type BatchJob, type BatchRequestItem, type BatchResultItem, type BflAdapterConfig, type BflCredits, type BudgetConfig, BudgetExceededError, BudgetGuard, type BudgetStore, type CallOptions, type Capability, type ChatInput, type ChatRequest, type ChatResult, type ChatStreamEvent, type ClassifyInput, type ClassifyResult, type ContentPart, type Contracts, type CostQuery, type CostSink, type CostSummary, type CostSummaryQuery, type CostTimeseriesQuery, DEFAULT_TIER_MAP, type DesignInput, type DesignResult, type DialogueRequest, type DialogueTurn, type DiscordSinkConfig, ELEVENLABS_DANISH_VOICES, type EmbeddingInput, type EmbeddingRequest, type EmbeddingResult, type ExtractInput, type ExtractResult, type FalAdapterConfig, type HttpResponse, type ImageInput, type ImageRequest, type ImageResult, type LoraWeight, type Message, type MockupInput, type MockupResult, type ModerationInput, type ModerationItem, type ModerationRequest, type ModerationResult, type OcrInput, type OcrPage, type OcrRequest, type OcrResult, type OpenAICompatibleConfig, type PodcastInput, type PodcastResult, type PricingEntry, type ProviderAdapter, type RefreshOptions, type RefreshResult, type RerankInput, type RerankResult, type Role, SDK_TAG, type SqliteBudgetStoreConfig, type SqliteSinkConfig, StreamHttpError, type SubprocessResponse, type Tier, type TierSpec, type Tool, type ToolCall, type TrainStyleInput, type TrainStyleRequest, type TrainStyleResult, type TranscribeInput, type TranscribeRequest, type TranscribeResult, type TranslateInput, type TranslateResult, type Transport, type TransportRequest, type TransportResponse, type TtsInput, type TtsRequest, type UpmetricsCostClientConfig, UpmetricsCostError, type UpmetricsCostRow, type UpmetricsCostSummary, type UpmetricsCostTimeseries, type UpmetricsSinkConfig, type Usage, VERSION, type VideoInput, type VisionInput, aiConfigSchema, anthropicAdapter, anthropicApiAdapter, anthropicSubprocessAdapter, azureAdapter, bflAdapter, bflCredits, chatInputSchema, computeCost, createAI, deepinfraAdapter, deepseekAdapter, defaultProviders, discordSink, elevenlabsAdapter, embeddingInputSchema, falAdapter, falStubAdapter, freshUsage, fromProviderToolCall, geminiAdapter, getCostSummary, getPrice, httpTransport, imageInputSchema, listAzureDanishVoices, makeContracts, makeOpenAICompatibleAdapter, messageSchema, mistralAdapter, mistralStubAdapter, multiSink, noopSink, openaiAdapter, openaiStubAdapter, openrouterAdapter, parseClaudeCliJson, parseJsonLoose, refreshAvailability, requestyAdapter, resetRefreshState, resetRegistry, resolveAzureVoice, resolveTier, resolveVoice, sqliteBudgetStore, sqliteSink, streamTransport, stubProviders, subprocessTransport, tierSpecSchema, toProviderTools, toolSchema, translateInputSchema, upmetricsCostClient, upmetricsSink, usdFromMicro, visionInputSchema };
|
|
2348
|
+
export { AZURE_DANISH_VOICES, AZURE_DANISH_VOICE_LIST, type AiClient, type AiConfig, type AzureVoiceInfo, type BatchJob, type BatchRequestItem, type BatchResultItem, type BflAdapterConfig, type BflCredits, type BudgetConfig, BudgetExceededError, BudgetGuard, type BudgetStore, type CallOptions, type Capability, type ChatInput, type ChatRequest, type ChatResult, type ChatStreamEvent, type ClassifyInput, type ClassifyResult, type ContentPart, type Contracts, type CostQuery, type CostSink, type CostSummary, type CostSummaryQuery, type CostTimeseriesQuery, DEFAULT_TIER_MAP, type DesignInput, type DesignResult, type DialogueRequest, type DialogueTurn, type DiscordSinkConfig, ELEVENLABS_DANISH_VOICES, type EmbeddingInput, type EmbeddingRequest, type EmbeddingResult, type ExtractInput, type ExtractResult, type FalAdapterConfig, type HttpResponse, type ImageInput, type ImageRequest, type ImageResult, type LoraWeight, type Message, type MockupInput, type MockupResult, type ModerationInput, type ModerationItem, type ModerationRequest, type ModerationResult, type OcrInput, type OcrPage, type OcrRequest, type OcrResult, type OpenAICompatibleConfig, type PodcastInput, type PodcastResult, type PricingEntry, type ProviderAdapter, type RefreshOptions, type RefreshResult, type RerankInput, type RerankResult, type Role, SDK_TAG, type SqliteBudgetStoreConfig, type SqliteSinkConfig, StreamHttpError, type SubprocessResponse, type Tier, type TierSpec, type Tool, type ToolCall, type TrainStyleInput, type TrainStyleRequest, type TrainStyleResult, type TranscribeInput, type TranscribeRequest, type TranscribeResult, type TranslateInput, type TranslateResult, type Transport, type TransportRequest, type TransportResponse, type TtsInput, type TtsRequest, type UpmetricsCostClientConfig, UpmetricsCostError, type UpmetricsCostRow, type UpmetricsCostSummary, type UpmetricsCostTimeseries, type UpmetricsSinkConfig, type Usage, VERSION, type VideoInput, type VisionInput, aiConfigSchema, anthropicAdapter, anthropicApiAdapter, anthropicSubprocessAdapter, azureAdapter, bflAdapter, bflCredits, chatInputSchema, computeCost, createAI, deepinfraAdapter, deeplAdapter, deepseekAdapter, defaultProviders, discordSink, elevenlabsAdapter, embeddingInputSchema, falAdapter, falStubAdapter, freshUsage, fromProviderToolCall, geminiAdapter, getCostSummary, getPrice, httpTransport, imageInputSchema, listAzureDanishVoices, makeContracts, makeOpenAICompatibleAdapter, messageSchema, mistralAdapter, mistralStubAdapter, multiSink, noopSink, openaiAdapter, openaiStubAdapter, openrouterAdapter, parseClaudeCliJson, parseJsonLoose, refreshAvailability, requestyAdapter, resetRefreshState, resetRegistry, resolveAzureVoice, resolveTier, resolveVoice, sqliteBudgetStore, sqliteSink, streamTransport, stubProviders, subprocessTransport, tierSpecSchema, toProviderTools, toolSchema, translateInputSchema, upmetricsCostClient, upmetricsSink, usdFromMicro, vertexAdapter, visionInputSchema };
|
package/dist/index.js
CHANGED
|
@@ -758,6 +758,30 @@ function openaiAdapter(config = {}) {
|
|
|
758
758
|
return { ...base, embedding, transcribe };
|
|
759
759
|
}
|
|
760
760
|
|
|
761
|
+
// src/providers/media.ts
|
|
762
|
+
async function toInlineImage(image, fetchImpl) {
|
|
763
|
+
if (typeof image !== "string") {
|
|
764
|
+
return { data: Buffer.from(image).toString("base64"), mimeType: sniffMime(image) };
|
|
765
|
+
}
|
|
766
|
+
if (/^https?:\/\//i.test(image)) {
|
|
767
|
+
const res = await fetchImpl(image);
|
|
768
|
+
if (!res.ok) throw new Error(`toInlineImage: failed to fetch image (${res.status})`);
|
|
769
|
+
const bytes = new Uint8Array(await res.arrayBuffer());
|
|
770
|
+
const mimeType2 = res.headers.get("content-type") ?? sniffMime(bytes);
|
|
771
|
+
return { data: Buffer.from(bytes).toString("base64"), mimeType: mimeType2 };
|
|
772
|
+
}
|
|
773
|
+
const comma = image.startsWith("data:") ? image.indexOf(",") : -1;
|
|
774
|
+
const b64 = comma >= 0 ? image.slice(comma + 1) : image;
|
|
775
|
+
const mimeType = image.startsWith("data:") ? image.slice(5, image.indexOf(";")) : "image/png";
|
|
776
|
+
return { data: b64, mimeType };
|
|
777
|
+
}
|
|
778
|
+
function sniffMime(b) {
|
|
779
|
+
if (b[0] === 137 && b[1] === 80) return "image/png";
|
|
780
|
+
if (b[0] === 71 && b[1] === 73) return "image/gif";
|
|
781
|
+
if (b[0] === 82 && b[1] === 73 && b[8] === 87) return "image/webp";
|
|
782
|
+
return "image/jpeg";
|
|
783
|
+
}
|
|
784
|
+
|
|
761
785
|
// src/providers/gemini.ts
|
|
762
786
|
var GEMINI_IMAGE_PRICE_PER_IMAGE = {
|
|
763
787
|
"gemini-2.5-flash-image": 0.039,
|
|
@@ -1002,28 +1026,6 @@ function geminiAdapter(config = {}) {
|
|
|
1002
1026
|
}
|
|
1003
1027
|
return { name: "gemini", chat, chatStream, image, animate, vision: chat };
|
|
1004
1028
|
}
|
|
1005
|
-
async function toInlineImage(image, fetchImpl) {
|
|
1006
|
-
if (typeof image !== "string") {
|
|
1007
|
-
return { data: Buffer.from(image).toString("base64"), mimeType: sniffMime(image) };
|
|
1008
|
-
}
|
|
1009
|
-
if (/^https?:\/\//i.test(image)) {
|
|
1010
|
-
const res = await fetchImpl(image);
|
|
1011
|
-
if (!res.ok) throw new Error(`gemini animate: failed to fetch image (${res.status})`);
|
|
1012
|
-
const bytes = new Uint8Array(await res.arrayBuffer());
|
|
1013
|
-
const mimeType2 = res.headers.get("content-type") ?? sniffMime(bytes);
|
|
1014
|
-
return { data: Buffer.from(bytes).toString("base64"), mimeType: mimeType2 };
|
|
1015
|
-
}
|
|
1016
|
-
const comma = image.startsWith("data:") ? image.indexOf(",") : -1;
|
|
1017
|
-
const b64 = comma >= 0 ? image.slice(comma + 1) : image;
|
|
1018
|
-
const mimeType = image.startsWith("data:") ? image.slice(5, image.indexOf(";")) : "image/png";
|
|
1019
|
-
return { data: b64, mimeType };
|
|
1020
|
-
}
|
|
1021
|
-
function sniffMime(b) {
|
|
1022
|
-
if (b[0] === 137 && b[1] === 80) return "image/png";
|
|
1023
|
-
if (b[0] === 71 && b[1] === 73) return "image/gif";
|
|
1024
|
-
if (b[0] === 82 && b[1] === 73 && b[8] === 87) return "image/webp";
|
|
1025
|
-
return "image/jpeg";
|
|
1026
|
-
}
|
|
1027
1029
|
function mapGeminiFinish(reason) {
|
|
1028
1030
|
switch (reason) {
|
|
1029
1031
|
case "MAX_TOKENS":
|
|
@@ -1045,19 +1047,68 @@ function deepinfraAdapter(config = {}) {
|
|
|
1045
1047
|
}
|
|
1046
1048
|
|
|
1047
1049
|
// src/providers/openrouter.ts
|
|
1050
|
+
var OPENROUTER_IMAGE_PRICE_ESTIMATE = {
|
|
1051
|
+
"recraft/recraft-v4.1": 0.035,
|
|
1052
|
+
"recraft/recraft-v4.1-vector": 0.08
|
|
1053
|
+
};
|
|
1048
1054
|
function openrouterAdapter(config = {}) {
|
|
1049
|
-
|
|
1055
|
+
const baseUrl = config.baseUrl ?? "https://openrouter.ai/api/v1";
|
|
1056
|
+
const headers = {
|
|
1057
|
+
"HTTP-Referer": config.referer ?? "https://broberg.ai",
|
|
1058
|
+
"X-Title": config.title ?? "@broberg/ai-sdk"
|
|
1059
|
+
};
|
|
1060
|
+
const base = makeOpenAICompatibleAdapter({
|
|
1050
1061
|
name: "openrouter",
|
|
1051
|
-
baseUrl
|
|
1062
|
+
baseUrl,
|
|
1052
1063
|
apiKey: config.apiKey,
|
|
1053
|
-
extraHeaders:
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1064
|
+
extraHeaders: headers,
|
|
1065
|
+
// Forward the injectable fetch so an override applies uniformly to
|
|
1066
|
+
// chat/chatStream/vision as well as the image() path below.
|
|
1067
|
+
fetch: config.fetch,
|
|
1057
1068
|
// OpenRouter returns ground-truth usage.cost (USD) when usage:{include:true}
|
|
1058
1069
|
// is set — use it over the local pricing-table estimate (F010).
|
|
1059
1070
|
costFromResponseField: true
|
|
1060
1071
|
});
|
|
1072
|
+
async function image(req) {
|
|
1073
|
+
const apiKey = config.apiKey ?? process.env.OPENROUTER_API_KEY;
|
|
1074
|
+
if (!apiKey) throw new Error("openrouter adapter: OPENROUTER_API_KEY not set");
|
|
1075
|
+
const doFetch = config.fetch ?? fetch;
|
|
1076
|
+
const body = { model: req.spec.model, prompt: req.prompt };
|
|
1077
|
+
if (req.width !== void 0 && req.height !== void 0) {
|
|
1078
|
+
body.size = `${req.width}x${req.height}`;
|
|
1079
|
+
}
|
|
1080
|
+
if (req.seed !== void 0) body.seed = req.seed;
|
|
1081
|
+
if (req.outputFormat !== void 0) body.output_format = req.outputFormat;
|
|
1082
|
+
const res = await doFetch(`${baseUrl}/images`, {
|
|
1083
|
+
method: "POST",
|
|
1084
|
+
headers: {
|
|
1085
|
+
"content-type": "application/json",
|
|
1086
|
+
Authorization: `Bearer ${apiKey}`,
|
|
1087
|
+
...headers
|
|
1088
|
+
},
|
|
1089
|
+
body: JSON.stringify(body)
|
|
1090
|
+
});
|
|
1091
|
+
if (!res.ok) {
|
|
1092
|
+
throw new Error(`openrouter images ${res.status}: ${(await res.text().catch(() => "")).slice(0, 300)}`);
|
|
1093
|
+
}
|
|
1094
|
+
const data = await res.json();
|
|
1095
|
+
const first = data.data?.[0];
|
|
1096
|
+
if (!first?.b64_json) {
|
|
1097
|
+
const errMsg = typeof data.error === "string" ? data.error : data.error?.message;
|
|
1098
|
+
throw new Error(`openrouter images: ${errMsg ?? "no image data in response"}`);
|
|
1099
|
+
}
|
|
1100
|
+
const usage = freshUsage({
|
|
1101
|
+
provider: "openrouter",
|
|
1102
|
+
model: req.spec.model,
|
|
1103
|
+
transport: "http",
|
|
1104
|
+
capability: "image",
|
|
1105
|
+
inputTokens: 0,
|
|
1106
|
+
outputTokens: 0
|
|
1107
|
+
});
|
|
1108
|
+
usage.costUsd = data.usage?.cost ?? config.pricePerImage ?? OPENROUTER_IMAGE_PRICE_ESTIMATE[req.spec.model] ?? 0;
|
|
1109
|
+
return { url: `data:${first.media_type ?? "image/png"};base64,${first.b64_json}`, usage };
|
|
1110
|
+
}
|
|
1111
|
+
return { ...base, image };
|
|
1061
1112
|
}
|
|
1062
1113
|
|
|
1063
1114
|
// src/providers/requesty.ts
|
|
@@ -1477,6 +1528,203 @@ function azureAdapter(config = {}) {
|
|
|
1477
1528
|
return { name: "azure", tts, transcribe };
|
|
1478
1529
|
}
|
|
1479
1530
|
|
|
1531
|
+
// src/providers/vertex.ts
|
|
1532
|
+
import { createSign } from "crypto";
|
|
1533
|
+
import { readFileSync } from "fs";
|
|
1534
|
+
var TOKEN_ENDPOINT = "https://oauth2.googleapis.com/token";
|
|
1535
|
+
var DEFAULT_REGION2 = "europe-west1";
|
|
1536
|
+
var CLOUD_PLATFORM_SCOPE = "https://www.googleapis.com/auth/cloud-platform";
|
|
1537
|
+
var VERTEX_VEO_PRICE_PER_SEC = {
|
|
1538
|
+
"veo-3.1-generate-preview": 0.4,
|
|
1539
|
+
"veo-3.1-fast-generate-preview": 0.1,
|
|
1540
|
+
"veo-3.1-lite-generate-preview": 0.05,
|
|
1541
|
+
"veo-3.0-generate-001": 0.4,
|
|
1542
|
+
"veo-3.0-fast-generate-001": 0.1
|
|
1543
|
+
};
|
|
1544
|
+
function base64url(input) {
|
|
1545
|
+
const buf = typeof input === "string" ? Buffer.from(input) : input;
|
|
1546
|
+
return buf.toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
1547
|
+
}
|
|
1548
|
+
function resolveCredentials(config) {
|
|
1549
|
+
const inline = config.credentials ?? process.env.GOOGLE_VERTEX_CREDENTIALS;
|
|
1550
|
+
if (inline) {
|
|
1551
|
+
try {
|
|
1552
|
+
return JSON.parse(inline);
|
|
1553
|
+
} catch {
|
|
1554
|
+
throw new Error("vertex adapter: GOOGLE_VERTEX_CREDENTIALS is not valid JSON");
|
|
1555
|
+
}
|
|
1556
|
+
}
|
|
1557
|
+
const path = process.env.GOOGLE_APPLICATION_CREDENTIALS;
|
|
1558
|
+
if (path) {
|
|
1559
|
+
let raw;
|
|
1560
|
+
try {
|
|
1561
|
+
raw = readFileSync(path, "utf8");
|
|
1562
|
+
} catch (err) {
|
|
1563
|
+
throw new Error(`vertex adapter: failed to read GOOGLE_APPLICATION_CREDENTIALS file: ${err.message}`);
|
|
1564
|
+
}
|
|
1565
|
+
return JSON.parse(raw);
|
|
1566
|
+
}
|
|
1567
|
+
throw new Error(
|
|
1568
|
+
"vertex adapter: service-account credentials not set (env GOOGLE_VERTEX_CREDENTIALS inline JSON, or GOOGLE_APPLICATION_CREDENTIALS file path)"
|
|
1569
|
+
);
|
|
1570
|
+
}
|
|
1571
|
+
async function mintAccessToken(creds, fetchImpl) {
|
|
1572
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
1573
|
+
const header = { alg: "RS256", typ: "JWT" };
|
|
1574
|
+
const claims = {
|
|
1575
|
+
iss: creds.client_email,
|
|
1576
|
+
scope: CLOUD_PLATFORM_SCOPE,
|
|
1577
|
+
aud: TOKEN_ENDPOINT,
|
|
1578
|
+
iat: now,
|
|
1579
|
+
exp: now + 3600
|
|
1580
|
+
};
|
|
1581
|
+
const unsigned = `${base64url(JSON.stringify(header))}.${base64url(JSON.stringify(claims))}`;
|
|
1582
|
+
const signature = createSign("RSA-SHA256").update(unsigned).sign(creds.private_key);
|
|
1583
|
+
const jwt = `${unsigned}.${base64url(signature)}`;
|
|
1584
|
+
const res = await fetchImpl(TOKEN_ENDPOINT, {
|
|
1585
|
+
method: "POST",
|
|
1586
|
+
headers: { "content-type": "application/x-www-form-urlencoded" },
|
|
1587
|
+
body: new URLSearchParams({
|
|
1588
|
+
grant_type: "urn:ietf:params:oauth:grant-type:jwt-bearer",
|
|
1589
|
+
assertion: jwt
|
|
1590
|
+
}).toString()
|
|
1591
|
+
});
|
|
1592
|
+
if (!res.ok) {
|
|
1593
|
+
throw new Error(`vertex adapter: token exchange failed ${res.status}: ${(await res.text().catch(() => "")).slice(0, 300)}`);
|
|
1594
|
+
}
|
|
1595
|
+
const data = await res.json();
|
|
1596
|
+
if (!data.access_token) throw new Error("vertex adapter: token exchange returned no access_token");
|
|
1597
|
+
return { token: data.access_token, expiresAt: Date.now() + (data.expires_in ?? 3600) * 1e3 };
|
|
1598
|
+
}
|
|
1599
|
+
function vertexAdapter(config = {}) {
|
|
1600
|
+
const fetchImpl = config.fetch ?? fetch;
|
|
1601
|
+
let cached = null;
|
|
1602
|
+
function region() {
|
|
1603
|
+
return config.region ?? process.env.GOOGLE_VERTEX_REGION ?? DEFAULT_REGION2;
|
|
1604
|
+
}
|
|
1605
|
+
function project() {
|
|
1606
|
+
const p = config.project ?? process.env.GOOGLE_VERTEX_PROJECT;
|
|
1607
|
+
if (!p) throw new Error("vertex adapter: project not set (config.project or env GOOGLE_VERTEX_PROJECT)");
|
|
1608
|
+
return p;
|
|
1609
|
+
}
|
|
1610
|
+
async function accessToken() {
|
|
1611
|
+
if (cached && cached.expiresAt - 6e4 > Date.now()) return cached.token;
|
|
1612
|
+
const creds = resolveCredentials(config);
|
|
1613
|
+
cached = await mintAccessToken(creds, fetchImpl);
|
|
1614
|
+
return cached.token;
|
|
1615
|
+
}
|
|
1616
|
+
async function animate(req) {
|
|
1617
|
+
const token = await accessToken();
|
|
1618
|
+
const proj = project();
|
|
1619
|
+
const reg = region();
|
|
1620
|
+
const pollIntervalMs = config.pollIntervalMs ?? 5e3;
|
|
1621
|
+
const deadline = Date.now() + (config.videoTimeoutMs ?? 3e5);
|
|
1622
|
+
const baseUrl = `https://${reg}-aiplatform.googleapis.com/v1`;
|
|
1623
|
+
const { data, mimeType } = await toInlineImage(req.image, fetchImpl);
|
|
1624
|
+
const parameters = {};
|
|
1625
|
+
if (req.durationSec !== void 0) parameters.durationSeconds = req.durationSec;
|
|
1626
|
+
if (req.resolution !== void 0) parameters.resolution = req.resolution;
|
|
1627
|
+
const body = {
|
|
1628
|
+
instances: [{ prompt: req.prompt ?? "", image: { bytesBase64Encoded: data, mimeType } }],
|
|
1629
|
+
...Object.keys(parameters).length ? { parameters } : {}
|
|
1630
|
+
};
|
|
1631
|
+
const submit = await fetchImpl(
|
|
1632
|
+
`${baseUrl}/projects/${proj}/locations/${reg}/publishers/google/models/${req.spec.model}:predictLongRunning`,
|
|
1633
|
+
{
|
|
1634
|
+
method: "POST",
|
|
1635
|
+
headers: { "content-type": "application/json", authorization: `Bearer ${token}` },
|
|
1636
|
+
body: JSON.stringify(body)
|
|
1637
|
+
}
|
|
1638
|
+
);
|
|
1639
|
+
if (!submit.ok) {
|
|
1640
|
+
throw new Error(`vertex animate ${submit.status}: ${(await submit.text().catch(() => "")).slice(0, 300)}`);
|
|
1641
|
+
}
|
|
1642
|
+
const op = await submit.json();
|
|
1643
|
+
if (!op.name) throw new Error("vertex animate: no operation name in submit response");
|
|
1644
|
+
let videoB64;
|
|
1645
|
+
let videoMime = "video/mp4";
|
|
1646
|
+
for (; ; ) {
|
|
1647
|
+
const poll = await fetchImpl(`${baseUrl}/${op.name}`, { headers: { authorization: `Bearer ${token}` } });
|
|
1648
|
+
if (!poll.ok) throw new Error(`vertex animate poll ${poll.status}`);
|
|
1649
|
+
const opData = await poll.json();
|
|
1650
|
+
if (opData.error) throw new Error(`vertex animate: ${opData.error.message ?? "operation error"}`);
|
|
1651
|
+
if (opData.done) {
|
|
1652
|
+
const video = opData.response?.videos?.[0];
|
|
1653
|
+
if (!video) {
|
|
1654
|
+
throw new Error(`vertex animate: done but no video in response: ${JSON.stringify(opData.response).slice(0, 300)}`);
|
|
1655
|
+
}
|
|
1656
|
+
if (!video.bytesBase64Encoded) {
|
|
1657
|
+
if (video.gcsUri) {
|
|
1658
|
+
throw new Error(
|
|
1659
|
+
`vertex animate: response returned a gcsUri ("${video.gcsUri}") \u2014 GCS download not yet supported (F031.x); this build only handles inline bytes`
|
|
1660
|
+
);
|
|
1661
|
+
}
|
|
1662
|
+
throw new Error(`vertex animate: done but no bytesBase64Encoded in response: ${JSON.stringify(opData.response).slice(0, 300)}`);
|
|
1663
|
+
}
|
|
1664
|
+
videoB64 = video.bytesBase64Encoded;
|
|
1665
|
+
videoMime = video.mimeType ?? "video/mp4";
|
|
1666
|
+
break;
|
|
1667
|
+
}
|
|
1668
|
+
if (Date.now() >= deadline) throw new Error("vertex animate: timed out");
|
|
1669
|
+
await new Promise((r) => setTimeout(r, pollIntervalMs));
|
|
1670
|
+
}
|
|
1671
|
+
const usage = freshUsage({
|
|
1672
|
+
provider: "vertex",
|
|
1673
|
+
model: req.spec.model,
|
|
1674
|
+
transport: "http",
|
|
1675
|
+
capability: "animate",
|
|
1676
|
+
inputTokens: 0,
|
|
1677
|
+
outputTokens: 0
|
|
1678
|
+
});
|
|
1679
|
+
const perSec = config.pricePerSecond ?? VERTEX_VEO_PRICE_PER_SEC[req.spec.model] ?? 0;
|
|
1680
|
+
usage.costUsd = perSec * (req.durationSec ?? 8);
|
|
1681
|
+
return { url: `vertex://${op.name}`, bytes: Buffer.from(videoB64, "base64"), mimeType: videoMime, usage };
|
|
1682
|
+
}
|
|
1683
|
+
return { name: "vertex", animate };
|
|
1684
|
+
}
|
|
1685
|
+
|
|
1686
|
+
// src/providers/deepl.ts
|
|
1687
|
+
var DEEPL_PRICE_PER_1K_CHARS_ESTIMATE = 0.0217;
|
|
1688
|
+
function deeplAdapter(config = {}) {
|
|
1689
|
+
const fetchImpl = config.fetch ?? fetch;
|
|
1690
|
+
function key() {
|
|
1691
|
+
const k = config.apiKey ?? process.env.DEEPL_API_KEY;
|
|
1692
|
+
if (!k) throw new Error("deepl adapter: API key not set (env DEEPL_API_KEY)");
|
|
1693
|
+
return k;
|
|
1694
|
+
}
|
|
1695
|
+
function baseUrl(apiKey) {
|
|
1696
|
+
return config.baseUrl ?? (apiKey.endsWith(":fx") ? "https://api-free.deepl.com" : "https://api.deepl.com");
|
|
1697
|
+
}
|
|
1698
|
+
async function translate(req) {
|
|
1699
|
+
const apiKey = key();
|
|
1700
|
+
const body = { text: [req.text], target_lang: req.to.toUpperCase() };
|
|
1701
|
+
if (req.from) body.source_lang = req.from.toUpperCase();
|
|
1702
|
+
const res = await fetchImpl(`${baseUrl(apiKey)}/v2/translate`, {
|
|
1703
|
+
method: "POST",
|
|
1704
|
+
headers: { "content-type": "application/json", authorization: `DeepL-Auth-Key ${apiKey}` },
|
|
1705
|
+
body: JSON.stringify(body)
|
|
1706
|
+
});
|
|
1707
|
+
if (!res.ok) {
|
|
1708
|
+
const errBody = await res.text().catch(() => "");
|
|
1709
|
+
throw new Error(`deepl translate ${res.status}: ${errBody.slice(0, 300)}`);
|
|
1710
|
+
}
|
|
1711
|
+
const data = await res.json();
|
|
1712
|
+
const text = data.translations?.[0]?.text;
|
|
1713
|
+
if (text === void 0) throw new Error("deepl translate: response contained no translation");
|
|
1714
|
+
const usage = freshUsage({
|
|
1715
|
+
provider: "deepl",
|
|
1716
|
+
model: req.spec.model,
|
|
1717
|
+
transport: "http",
|
|
1718
|
+
capability: "translate",
|
|
1719
|
+
inputTokens: 0,
|
|
1720
|
+
outputTokens: 0
|
|
1721
|
+
});
|
|
1722
|
+
usage.costUsd = req.text.length / 1e3 * (config.pricePer1kChars ?? DEEPL_PRICE_PER_1K_CHARS_ESTIMATE);
|
|
1723
|
+
return { text, usage };
|
|
1724
|
+
}
|
|
1725
|
+
return { name: "deepl", translate };
|
|
1726
|
+
}
|
|
1727
|
+
|
|
1480
1728
|
// src/providers/fal.ts
|
|
1481
1729
|
import { deflateRawSync, crc32 } from "zlib";
|
|
1482
1730
|
var FAL_IMAGE_PRICE_ESTIMATE = {
|
|
@@ -1907,6 +2155,8 @@ var defaultProviders = {
|
|
|
1907
2155
|
mistral: mistralAdapter(),
|
|
1908
2156
|
elevenlabs: elevenlabsAdapter(),
|
|
1909
2157
|
azure: azureAdapter(),
|
|
2158
|
+
vertex: vertexAdapter(),
|
|
2159
|
+
deepl: deeplAdapter(),
|
|
1910
2160
|
fal: falAdapter(),
|
|
1911
2161
|
bfl: bflAdapter()
|
|
1912
2162
|
};
|
|
@@ -2582,6 +2832,7 @@ function createAI(config = {}) {
|
|
|
2582
2832
|
estOut: estIn,
|
|
2583
2833
|
invoke: async (spec) => {
|
|
2584
2834
|
const adapter = pickProvider(spec.provider);
|
|
2835
|
+
if (adapter.translate) return adapter.translate({ text: input.text, to: input.to, from: input.from, spec });
|
|
2585
2836
|
if (!adapter.chat) throw new Error(`createAI: provider "${spec.provider}" does not support chat (translate routes through chat)`);
|
|
2586
2837
|
return adapter.chat({ messages, spec });
|
|
2587
2838
|
}
|
|
@@ -2915,8 +3166,8 @@ var stubProviders = {
|
|
|
2915
3166
|
};
|
|
2916
3167
|
|
|
2917
3168
|
// src/version.ts
|
|
2918
|
-
var VERSION = "0.
|
|
2919
|
-
var SDK_TAG = "@broberg/ai-sdk@0.
|
|
3169
|
+
var VERSION = "0.22.0";
|
|
3170
|
+
var SDK_TAG = "@broberg/ai-sdk@0.22.0";
|
|
2920
3171
|
|
|
2921
3172
|
// src/availability/refresh.ts
|
|
2922
3173
|
var NOT_REFRESHED = { refreshed: false, checked: 0, markedUnavailable: [] };
|
|
@@ -3279,6 +3530,7 @@ export {
|
|
|
3279
3530
|
computeCost,
|
|
3280
3531
|
createAI,
|
|
3281
3532
|
deepinfraAdapter,
|
|
3533
|
+
deeplAdapter,
|
|
3282
3534
|
deepseekAdapter,
|
|
3283
3535
|
defaultProviders,
|
|
3284
3536
|
discordSink,
|
|
@@ -3327,6 +3579,7 @@ export {
|
|
|
3327
3579
|
upmetricsCostClient,
|
|
3328
3580
|
upmetricsSink,
|
|
3329
3581
|
usdFromMicro,
|
|
3582
|
+
vertexAdapter,
|
|
3330
3583
|
visionInputSchema
|
|
3331
3584
|
};
|
|
3332
3585
|
//# sourceMappingURL=index.js.map
|