@broberg/ai-sdk 0.21.0 → 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/{chunk-HVZSYNZ5.js → chunk-IT7HNKLY.js} +2 -2
- package/dist/chunk-IT7HNKLY.js.map +1 -0
- package/dist/index.d.ts +45 -7
- package/dist/index.js +284 -31
- package/dist/index.js.map +1 -1
- package/dist/registry.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-HVZSYNZ5.js.map +0 -1
|
@@ -5,7 +5,7 @@ var DEFAULTS = [
|
|
|
5
5
|
{ id: "claude-haiku-4-5", aliases: ["haiku", "fast"], provider: "anthropic", available: true, status: "available", source: "default" },
|
|
6
6
|
{ id: "claude-sonnet-4-6", aliases: ["sonnet", "smart"], provider: "anthropic", available: true, status: "available", source: "default" },
|
|
7
7
|
{ id: "claude-opus-4-8", aliases: ["opus", "powerful"], provider: "anthropic", available: true, status: "available", source: "default" },
|
|
8
|
-
{ id: "claude-fable-5", aliases: ["fable"], provider: "anthropic", available:
|
|
8
|
+
{ id: "claude-fable-5", aliases: ["fable"], provider: "anthropic", available: true, status: "available", source: "default" },
|
|
9
9
|
{ id: "claude-mythos-5", aliases: ["mythos"], provider: "anthropic", available: false, status: "suspended", note: SUSPENDED_FABLE_MYTHOS, source: "default" },
|
|
10
10
|
// ── Gemini ───────────────────────────────────────────────────────────────
|
|
11
11
|
{ id: "gemini-2.5-flash", aliases: ["gemini-flash"], provider: "gemini", available: true, status: "available", source: "default" },
|
|
@@ -138,4 +138,4 @@ export {
|
|
|
138
138
|
listModels,
|
|
139
139
|
resolveModel
|
|
140
140
|
};
|
|
141
|
-
//# sourceMappingURL=chunk-
|
|
141
|
+
//# sourceMappingURL=chunk-IT7HNKLY.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/availability/registry.ts","../src/availability/types.ts","../src/availability/resolve.ts"],"sourcesContent":["// F022 — the model-availability registry: the ONE source both resolveModel()\n// (spawn / call path) and listModels() (UI picker) read. A curated default seed\n// (works offline — the durable floor) plus a mutable overlay that\n// refreshAvailability() updates from the live provider list.\n//\n// Scope note: this is a LIVENESS view (is this id alive right now?), not the\n// rich capability/price inventory (that is F017 src/catalogue). We only track\n// ids we want to assert status on; anything not here is fail-open (treated\n// available) so we never block a model we simply do not track.\nimport type { AvailabilityStatus, AvailabilitySource, ModelStatus } from \"./types.js\";\n\n/** Internal registry row. `aliases[0]` surfaces as ModelStatus.alias. */\nexport interface RegistryEntry {\n id: string;\n aliases: string[];\n provider: string;\n available: boolean;\n status: AvailabilityStatus;\n note?: string;\n source: AvailabilitySource;\n}\n\nconst SUSPENDED_FABLE_MYTHOS = \"suspended — US export-control directive (2026-06-12)\";\n\n/** Curated defaults. Mirrors DEFAULT_TIER_MAP model ids (src/routing/tier-map.ts)\n * + the models documented in CLAUDE.md, plus the two ids Anthropic suspended\n * globally on 2026-06-12. Aliases are the tier / short names a caller or picker\n * may pass instead of the canonical id. */\nconst DEFAULTS: RegistryEntry[] = [\n // ── Anthropic ────────────────────────────────────────────────────────────\n { id: \"claude-haiku-4-5\", aliases: [\"haiku\", \"fast\"], provider: \"anthropic\", available: true, status: \"available\", source: \"default\" },\n { id: \"claude-sonnet-4-6\", aliases: [\"sonnet\", \"smart\"], provider: \"anthropic\", available: true, status: \"available\", source: \"default\" },\n { id: \"claude-opus-4-8\", aliases: [\"opus\", \"powerful\"], provider: \"anthropic\", available: true, status: \"available\", source: \"default\" },\n { id: \"claude-fable-5\", aliases: [\"fable\"], provider: \"anthropic\", available: true, status: \"available\", source: \"default\" },\n { id: \"claude-mythos-5\", aliases: [\"mythos\"], provider: \"anthropic\", available: false, status: \"suspended\", note: SUSPENDED_FABLE_MYTHOS, source: \"default\" },\n // ── Gemini ───────────────────────────────────────────────────────────────\n { id: \"gemini-2.5-flash\", aliases: [\"gemini-flash\"], provider: \"gemini\", available: true, status: \"available\", source: \"default\" },\n { id: \"gemini-2.5-flash-lite\", aliases: [\"gemini-flash-lite\", \"video\"], provider: \"gemini\", available: true, status: \"available\", source: \"default\" },\n // ── OpenAI ───────────────────────────────────────────────────────────────\n { id: \"text-embedding-3-small\", aliases: [\"embedding\"], provider: \"openai\", available: true, status: \"available\", source: \"default\" },\n // ── Mistral (EU / GDPR) ──────────────────────────────────────────────────\n { id: \"mistral-large-latest\", aliases: [\"mistral-large\"], provider: \"mistral\", available: true, status: \"available\", source: \"default\" },\n { id: \"mistral-small-latest\", aliases: [\"mistral-small\"], provider: \"mistral\", available: true, status: \"available\", source: \"default\" },\n];\n\n/** The live overlay, keyed by canonical id. Seeded from DEFAULTS (deep-copied so\n * resetting is clean). refreshAvailability() mutates this; resolve/listModels\n * read it synchronously. */\nlet OVERLAY = new Map<string, RegistryEntry>();\n/** alias → canonical id, rebuilt whenever the overlay is seeded. */\nlet ALIAS_INDEX = new Map<string, string>();\n\nfunction seed(): void {\n OVERLAY = new Map(DEFAULTS.map((e) => [e.id, { ...e, aliases: [...e.aliases] }]));\n ALIAS_INDEX = new Map();\n for (const e of DEFAULTS) for (const a of e.aliases) ALIAS_INDEX.set(a, e.id);\n}\nseed();\n\n/** Reset the overlay back to the curated defaults. For tests. */\nexport function resetRegistry(): void {\n seed();\n}\n\n/** Canonical id for a model id OR alias; null when we track neither. */\nexport function canonicalId(requested: string): string | null {\n if (OVERLAY.has(requested)) return requested;\n return ALIAS_INDEX.get(requested) ?? null;\n}\n\n/** The current entry for an id/alias, or undefined when untracked (fail-open). */\nexport function getEntry(requested: string): RegistryEntry | undefined {\n const id = canonicalId(requested);\n return id ? OVERLAY.get(id) : undefined;\n}\n\n/** All tracked entries (optionally provider-scoped), as a public ModelStatus[]. */\nexport function allEntries(provider?: string): ModelStatus[] {\n const rows: ModelStatus[] = [];\n for (const e of OVERLAY.values()) {\n if (provider && e.provider !== provider) continue;\n rows.push({\n id: e.id,\n alias: e.aliases[0],\n provider: e.provider,\n available: e.available,\n status: e.status,\n note: e.note,\n source: e.source,\n });\n }\n return rows;\n}\n\n/** Provider-scoped canonical ids (for refresh reconciliation). */\nexport function providerIds(provider: string): string[] {\n return [...OVERLAY.values()].filter((e) => e.provider === provider).map((e) => e.id);\n}\n\n/** Mark a tracked id available/suspended from a live refresh. No-op if untracked. */\nexport function setAvailability(id: string, available: boolean, note?: string): void {\n const e = OVERLAY.get(id);\n if (!e) return;\n e.available = available;\n e.status = available ? \"available\" : \"suspended\";\n e.source = \"refresh\";\n if (note !== undefined) e.note = note;\n else if (available) e.note = undefined;\n}\n","// F022 — Model Availability Harness. Public types for the availability layer:\n// the shared status read (ModelStatus), the resolve result, and the structured\n// error a caller can flag on. The registry is the one source both the spawn /\n// call path (resolveModel) and UI pickers (listModels) read.\n\nexport type AvailabilityStatus = \"available\" | \"suspended\" | \"unknown\";\n\n/** Where a model's current availability came from: the curated default seed,\n * or a live provider refresh (Anthropic GET /v1/models). */\nexport type AvailabilitySource = \"default\" | \"refresh\";\n\n/** One row of the shared status read — what a UI model-picker renders. */\nexport interface ModelStatus {\n /** Canonical provider model id, e.g. \"claude-fable-5\". */\n id: string;\n /** Short/tier alias, e.g. \"fable\" (the first registered alias). */\n alias?: string;\n /** \"anthropic\" | \"openai\" | \"gemini\" | \"mistral\" | … */\n provider: string;\n available: boolean;\n status: AvailabilityStatus;\n /** Friendly reason, e.g. \"suspended — US export-control directive (2026-06-12)\". */\n note?: string;\n source: AvailabilitySource;\n}\n\n/** Result of resolveModel — the spawn / call path consumes this synchronously. */\nexport interface ResolveResult {\n /** True when the requested model itself is available. */\n ok: boolean;\n /** The id to actually use: `requested` when ok, else the first available fallback. */\n model: string;\n /** What the caller asked for (id or alias, normalized to the canonical id). */\n requested: string;\n provider?: string;\n /** True when `model` differs from `requested` because we fell back. */\n fellBack: boolean;\n status: AvailabilityStatus;\n /** Why it degraded / why it is unavailable. */\n reason?: string;\n}\n\n/** Thrown by resolveModel when the requested model is unavailable, no usable\n * fallback exists, and the caller passed `throwIfUnavailable`. Callers flag on\n * `.code === \"model_unavailable\"`. */\nexport class ModelUnavailableError extends Error {\n readonly code = \"model_unavailable\";\n readonly requested: string;\n readonly provider?: string;\n readonly note?: string;\n constructor(requested: string, note?: string, provider?: string) {\n super(`model \"${requested}\" is unavailable${note ? ` (${note})` : \"\"}`);\n this.name = \"ModelUnavailableError\";\n this.requested = requested;\n this.note = note;\n this.provider = provider;\n }\n}\n","// F022 — the synchronous, zero-I/O resolve + status read. This is the spawn /\n// call hot path (buddy's launcher calls resolveModel per spawn, cardmem #4842):\n// it MUST never await and never touch the network. Freshness comes only from a\n// prior async refreshAvailability(); resolve just reads the in-memory registry.\nimport { allEntries, canonicalId, getEntry } from \"./registry.js\";\nimport { ModelUnavailableError } from \"./types.js\";\nimport type { ModelStatus, ResolveResult } from \"./types.js\";\n\nexport interface ResolveOptions {\n /** One id/alias or an ordered chain to try when `requested` is unavailable. */\n fallback?: string | string[];\n /** Scope hint (passed through to the result); does not gate lookup. */\n provider?: string;\n /** Throw ModelUnavailableError instead of returning ok:false when there is no\n * usable fallback. For callers that want to flag rather than degrade. */\n throwIfUnavailable?: boolean;\n}\n\n/** The shared status read — UI pickers grey out `available:false` rows. */\nexport function listModels(opts: { provider?: string } = {}): ModelStatus[] {\n return allEntries(opts.provider);\n}\n\n/** Is this id/alias currently usable? Untracked ids are fail-open (true). */\nfunction isAvailable(requested: string): boolean {\n const e = getEntry(requested);\n return e ? e.available : true; // fail-open on unknown\n}\n\n/**\n * Resolve a requested model (id or alias) to one that is actually usable.\n * Synchronous + offline by contract (cardmem #4842) — reads the registry only.\n *\n * - Available → pass through ({ ok:true, fellBack:false }).\n * - Unavailable + a fallback that IS available → swap ({ ok:false, fellBack:true }).\n * - Unavailable + no usable fallback → throw (throwIfUnavailable) or return ok:false.\n * - Unknown id → treated available (never block a model we do not track).\n */\nexport function resolveModel(requested: string, opts: ResolveOptions = {}): ResolveResult {\n const id = canonicalId(requested) ?? requested;\n const entry = getEntry(requested);\n const provider = opts.provider ?? entry?.provider;\n\n if (isAvailable(requested)) {\n return {\n ok: true,\n model: id,\n requested: id,\n provider,\n fellBack: false,\n status: entry?.status ?? \"unknown\",\n };\n }\n\n // Requested is suspended — walk the fallback chain for the first available one.\n const chain = opts.fallback === undefined ? [] : Array.isArray(opts.fallback) ? opts.fallback : [opts.fallback];\n for (const fb of chain) {\n if (isAvailable(fb)) {\n const fbId = canonicalId(fb) ?? fb;\n return {\n ok: false,\n model: fbId,\n requested: id,\n provider,\n fellBack: true,\n status: entry?.status ?? \"suspended\",\n reason: entry?.note ?? `${id} is unavailable`,\n };\n }\n }\n\n // No usable fallback.\n if (opts.throwIfUnavailable) {\n throw new ModelUnavailableError(id, entry?.note, provider);\n }\n return {\n ok: false,\n model: id,\n requested: id,\n provider,\n fellBack: false,\n status: entry?.status ?? \"suspended\",\n reason: entry?.note ?? `${id} is unavailable`,\n };\n}\n"],"mappings":"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","names":[]}
|
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
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
resetRegistry,
|
|
6
6
|
resolveModel,
|
|
7
7
|
setAvailability
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-IT7HNKLY.js";
|
|
9
9
|
import {
|
|
10
10
|
getPrice
|
|
11
11
|
} from "./chunk-IZG5UZH5.js";
|
|
@@ -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
|