@broberg/ai-sdk 0.45.1 → 0.47.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/README.md +71 -0
- package/dist/index.d.ts +77 -3
- package/dist/index.js +70 -14
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -81,6 +81,43 @@ const { data } = await ai.contracts.extract({
|
|
|
81
81
|
// also: ai.contracts.{ mockup, design, classify, rerank }
|
|
82
82
|
```
|
|
83
83
|
|
|
84
|
+
## Read-aloud: what was actually SPOKEN is not your `text`
|
|
85
|
+
|
|
86
|
+
`ai.tts({ pronunciations })` rewrites your text before the provider ever sees it —
|
|
87
|
+
`broberg.ai` becomes four spoken words. So **`text` is a retelling of the audio**, and a
|
|
88
|
+
word-highlighter built on `text` alone will drift at every rewritten word.
|
|
89
|
+
|
|
90
|
+
Two fields close that gap:
|
|
91
|
+
|
|
92
|
+
```ts
|
|
93
|
+
const { audio, wordTimings, ssml } = await ai.tts({
|
|
94
|
+
text, voice, wordTimings: true, pronunciations,
|
|
95
|
+
override: { provider: "azure" },
|
|
96
|
+
});
|
|
97
|
+
wordTimings.words // [{ text, startMs, endMs, sourceStart, sourceEnd }]
|
|
98
|
+
wordTimings.unaligned // spoken words we could NOT place — never a guessed offset
|
|
99
|
+
ssml // the EXACT markup we sent, verbatim — your ground truth
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
`sourceStart`/`sourceEnd` index **your original text**, because Azure reports audio time
|
|
103
|
+
only and carries no text offset at all; the link back to the manuscript is derived here.
|
|
104
|
+
`ssml` exists so you can check that derivation instead of trusting it — it is the string
|
|
105
|
+
we sent, not a rebuild (filed by a consumer who could not verify what was spoken, F055.4).
|
|
106
|
+
It is `undefined` on routes that build no markup; an empty string would be a different claim.
|
|
107
|
+
|
|
108
|
+
**Two matcher rules that surprise people**, both measured in `pronunciations`:
|
|
109
|
+
|
|
110
|
+
- **Case-insensitive.** `ai`, `Ai` and `AI` all match a rule for `AI`.
|
|
111
|
+
- **A word touching a hyphen keeps its spelling.** `broberg.ai-drevet` is NOT rewritten
|
|
112
|
+
unless that entry sets `matchInCompounds: true` — the rule that keeps `mail` out of
|
|
113
|
+
`e-mail`. A consumer deriving their own expected-word list got exactly these two
|
|
114
|
+
occurrences wrong before the field existed.
|
|
115
|
+
|
|
116
|
+
**Word timings need Azure's BATCH route**, a different call shape (submit → poll → ZIP),
|
|
117
|
+
and that route requires the resource's custom subdomain — the regional host answers 401
|
|
118
|
+
with a valid key, blaming the key. Set `AZURE_SPEECH_RESOURCE`; without it `wordTimings`
|
|
119
|
+
fails before the call, naming the fix.
|
|
120
|
+
|
|
84
121
|
## Providers & tiers
|
|
85
122
|
|
|
86
123
|
Adapters: **Anthropic** (HTTP + `claude -p` subprocess), **OpenAI**, **Google
|
|
@@ -109,6 +146,40 @@ personal data by default; override per call for an even cheaper non-personal rou
|
|
|
109
146
|
(The `claude -p` subprocess transport is still available via explicit
|
|
110
147
|
`override: { transport: "subprocess" }`, but is no longer a default route.)
|
|
111
148
|
|
|
149
|
+
## Where WOULD this go? — `wouldRouteTo` (a forecast, not a fact)
|
|
150
|
+
|
|
151
|
+
`usage.region` answers **where a call went**. It cannot answer *may I send this?*, because
|
|
152
|
+
it only exists once the bytes have left. `wouldRouteTo` answers that half:
|
|
153
|
+
|
|
154
|
+
```ts
|
|
155
|
+
import { wouldRouteTo } from "@broberg/ai-sdk";
|
|
156
|
+
|
|
157
|
+
const f = wouldRouteTo("smart");
|
|
158
|
+
// { kind: "forecast", provider: "mistral", host: "https://api.mistral.ai/v1",
|
|
159
|
+
// wouldRouteTo: "eu", onlyIf: [ …the assumptions… ] }
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
**It is deliberately not shaped like `usage.region`.** There is no field called `region` on
|
|
163
|
+
it, and that is enforced by a test: a forecast that can be wrong must not wear the clothes
|
|
164
|
+
of a fact that cannot. `onlyIf` comes back **with** the answer rather than living in these
|
|
165
|
+
docs — the answer holds only while you override no `baseUrl` and pass no fallback, because
|
|
166
|
+
**a fallback IS a route and the route decides residency**.
|
|
167
|
+
|
|
168
|
+
**`"depends-on-config"` is an answer, not a failure.** `azure`, `vertex`, `deepl`, `requesty`
|
|
169
|
+
and `fal` build their host from values you supply. `requesty` is why this state exists: it
|
|
170
|
+
has *both* an EU and a non-EU host, so any region we named there would be a residency claim
|
|
171
|
+
decided by a table instead of by a route.
|
|
172
|
+
|
|
173
|
+
**It refuses nothing.** It never throws and never blocks a call — not even for a nonsense
|
|
174
|
+
provider. Residency policy belongs to each consumer, not to this package; this is the
|
|
175
|
+
information that makes that decision possible, which is the opposite of a gate.
|
|
176
|
+
|
|
177
|
+
**Why it can say `"eu"` where `regionOfProvider` says `"unknown"`:** it asks the *host* the
|
|
178
|
+
SDK would actually use, not the provider's *name*. `regionOfProvider("mistral")` is
|
|
179
|
+
`"unknown"` by design — that adapter takes a `baseUrl`. Filed by a consumer who had
|
|
180
|
+
hand-copied our tier table into their own repo to answer this; that copy can now be deleted
|
|
181
|
+
instead of maintained.
|
|
182
|
+
|
|
112
183
|
## Cost, budget & sinks
|
|
113
184
|
|
|
114
185
|
```ts
|
package/dist/index.d.ts
CHANGED
|
@@ -555,6 +555,17 @@ interface PodcastResult {
|
|
|
555
555
|
* — Azure reports only audio time, so the link back to the manuscript is derived
|
|
556
556
|
* here. `unaligned` names any spoken word that could not be placed. */
|
|
557
557
|
wordTimings?: AlignedWordTimings;
|
|
558
|
+
/** F055.4 — the EXACT markup we sent the provider, verbatim, not rebuilt.
|
|
559
|
+
*
|
|
560
|
+
* Why it exists: the text you passed is not what was spoken. A pronunciation
|
|
561
|
+
* dictionary rewrites it, so `text` is a RETELLING of the audio — and without this
|
|
562
|
+
* field a consumer cannot check our retelling any more than we could check theirs.
|
|
563
|
+
* cms filed exactly that (11 September 2026) while we were asking them for markup
|
|
564
|
+
* only we could produce.
|
|
565
|
+
*
|
|
566
|
+
* UNDEFINED when the route builds no markup (ElevenLabs routes by voice, not SSML).
|
|
567
|
+
* An empty string would say "we sent empty markup", which is a different claim. */
|
|
568
|
+
ssml?: string;
|
|
558
569
|
usage: Usage;
|
|
559
570
|
}
|
|
560
571
|
|
|
@@ -2523,8 +2534,8 @@ declare const falStubAdapter: ProviderAdapter;
|
|
|
2523
2534
|
* wires the live adapters. */
|
|
2524
2535
|
declare const stubProviders: Record<string, ProviderAdapter>;
|
|
2525
2536
|
|
|
2526
|
-
declare const VERSION: "0.
|
|
2527
|
-
declare const SDK_TAG: "@broberg/ai-sdk@0.
|
|
2537
|
+
declare const VERSION: "0.47.0";
|
|
2538
|
+
declare const SDK_TAG: "@broberg/ai-sdk@0.47.0";
|
|
2528
2539
|
|
|
2529
2540
|
/** Built-in defaults. Every entry is overridable via AiConfig.defaults or a
|
|
2530
2541
|
* per-call override.
|
|
@@ -2870,6 +2881,69 @@ interface PricingEntry {
|
|
|
2870
2881
|
}
|
|
2871
2882
|
declare function getPrice(provider: string, model: string): PricingEntry | undefined;
|
|
2872
2883
|
|
|
2884
|
+
/** What a forecast can say. `"depends-on-config"` is NOT a region — it is the honest
|
|
2885
|
+
* answer for a provider whose host you supply part of (azure, vertex, deepl, requesty,
|
|
2886
|
+
* fal). `requesty` is why it exists: it has both an EU and a non-EU host, so a table
|
|
2887
|
+
* answering there would be a residency claim decided by a table rather than by a route. */
|
|
2888
|
+
type RouteForecast = {
|
|
2889
|
+
kind: "forecast";
|
|
2890
|
+
/** The provider the tier resolves to today. */
|
|
2891
|
+
provider: string;
|
|
2892
|
+
/** The host the SDK would use if you override nothing. */
|
|
2893
|
+
host: string;
|
|
2894
|
+
/** Region of THAT host. Only `"eu"` is a positive claim — `"unknown"` means we
|
|
2895
|
+
* cannot say, never "probably fine". */
|
|
2896
|
+
wouldRouteTo: Region;
|
|
2897
|
+
/** The assumptions, returned WITH the answer rather than left in the docs. Never
|
|
2898
|
+
* empty: a forecast whose conditions are invisible reads as a fact. */
|
|
2899
|
+
onlyIf: string[];
|
|
2900
|
+
} | {
|
|
2901
|
+
kind: "depends-on-config";
|
|
2902
|
+
provider: string;
|
|
2903
|
+
/** Why no table can answer for this provider. */
|
|
2904
|
+
because: string;
|
|
2905
|
+
onlyIf: string[];
|
|
2906
|
+
} | {
|
|
2907
|
+
kind: "unknown-provider";
|
|
2908
|
+
provider: string;
|
|
2909
|
+
onlyIf: string[];
|
|
2910
|
+
};
|
|
2911
|
+
/** Forecast for a PROVIDER, using the host the SDK would default to. */
|
|
2912
|
+
declare function wouldProviderRouteTo(provider: string): RouteForecast;
|
|
2913
|
+
/** Forecast for a TIER — the question helpdesk actually asked. */
|
|
2914
|
+
declare function wouldRouteTo(tier: Tier): RouteForecast;
|
|
2915
|
+
|
|
2916
|
+
/** Providers whose default endpoint is a FIXED host. */
|
|
2917
|
+
declare const DEFAULT_BASE_URLS: {
|
|
2918
|
+
readonly anthropic: "https://api.anthropic.com";
|
|
2919
|
+
readonly deepinfra: "https://api.deepinfra.com/v1/openai";
|
|
2920
|
+
readonly deepseek: "https://api.deepseek.com/v1";
|
|
2921
|
+
readonly elevenlabs: "https://api.elevenlabs.io/v1";
|
|
2922
|
+
readonly gemini: "https://generativelanguage.googleapis.com/v1beta";
|
|
2923
|
+
readonly mistral: "https://api.mistral.ai/v1";
|
|
2924
|
+
readonly openai: "https://api.openai.com/v1";
|
|
2925
|
+
readonly openrouter: "https://openrouter.ai/api/v1";
|
|
2926
|
+
};
|
|
2927
|
+
type FixedHostProvider = keyof typeof DEFAULT_BASE_URLS;
|
|
2928
|
+
/** `bfl` is DELIBERATELY ABSENT. Its adapter hard-pins `https://api.eu.bfl.ai` in a local
|
|
2929
|
+
* constant and calls that pin the GDPR crux, non-negotiable — faces and portraits go
|
|
2930
|
+
* through it. Hoisting the value here would make a deliberate pin editable from a shared
|
|
2931
|
+
* file that nine other providers also read, which is a worse trade than one more name for
|
|
2932
|
+
* one URL. It is not a drifting copy either: there is exactly one place, it is just not
|
|
2933
|
+
* this one. The scanner below does not flag it because bfl never used the
|
|
2934
|
+
* `baseUrl ?? "https://…"` form it forbids. */
|
|
2935
|
+
declare const LOCALLY_PINNED_HOSTS: Record<string, string>;
|
|
2936
|
+
/** Providers whose host is DERIVED FROM CONFIG, so no table can answer for them.
|
|
2937
|
+
*
|
|
2938
|
+
* Measured 12 September 2026, and this list is the honest half of F056: a flat
|
|
2939
|
+
* "default host per provider" table would answer for these too, and be wrong in the
|
|
2940
|
+
* GREEN direction — it would say something instead of saying it cannot.
|
|
2941
|
+
*
|
|
2942
|
+
* `requesty` is the sharp one: it has BOTH an EU and a non-EU host, so a guess there
|
|
2943
|
+
* would be a residency claim decided by a table rather than by the route. */
|
|
2944
|
+
declare const CONFIG_DERIVED_HOSTS: Record<string, string>;
|
|
2945
|
+
declare function defaultBaseUrl(provider: string): string | undefined;
|
|
2946
|
+
|
|
2873
2947
|
interface TransportRequest {
|
|
2874
2948
|
/** Resolved routing for this call (transport field selects the path). */
|
|
2875
2949
|
spec: TierSpec;
|
|
@@ -2935,4 +3009,4 @@ interface StreamTransportRequest extends TransportRequest {
|
|
|
2935
3009
|
*/
|
|
2936
3010
|
declare function streamTransport(req: StreamTransportRequest): AsyncIterable<string>;
|
|
2937
3011
|
|
|
2938
|
-
export { AZURE_DANISH_VOICES, AZURE_DANISH_VOICE_LIST, type AiClient, type AiConfig, type AlignedWordTimings, type AzureVoiceInfo, type AzureWordBoundary, 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 CheckVoiceOptions, 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 Region, 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, type VoiceInfo, type VoiceProvider, type VoiceResolveResult, type VoiceStatus, VoiceUnavailableError, type WordTiming, aiConfigSchema, alignWordTimings, anthropicAdapter, anthropicApiAdapter, anthropicSubprocessAdapter, azureAdapter, bflAdapter, bflCredits, chatInputSchema, checkVoice, classifyRegionName, computeCost, createAI, deepinfraAdapter, deeplAdapter, deepseekAdapter, defaultProviders, discordSink, elevenlabsAdapter, embeddingInputSchema, falAdapter, falStubAdapter, freshUsage, fromProviderToolCall, geminiAdapter, getCostSummary, getPrice, httpTransport, imageInputSchema, listAzureDanishVoices, listVoices, makeContracts, makeOpenAICompatibleAdapter, messageSchema, mistralAdapter, mistralStubAdapter, multiSink, noopSink, openaiAdapter, openaiStubAdapter, openrouterAdapter, parseClaudeCliJson, parseJsonLoose, refreshAvailability, regionOfHost, regionOfProvider, requestyAdapter, resetRefreshState, resetRegistry, resolveAzureVoice, resolveTier, resolveVoice, sqliteBudgetStore, sqliteSink, streamTransport, stubProviders, subprocessTransport, tierSpecSchema, toProviderTools, toolSchema, translateInputSchema, upmetricsCostClient, upmetricsSink, usdFromMicro, vertexAdapter, visionInputSchema };
|
|
3012
|
+
export { AZURE_DANISH_VOICES, AZURE_DANISH_VOICE_LIST, type AiClient, type AiConfig, type AlignedWordTimings, type AzureVoiceInfo, type AzureWordBoundary, type BatchJob, type BatchRequestItem, type BatchResultItem, type BflAdapterConfig, type BflCredits, type BudgetConfig, BudgetExceededError, BudgetGuard, type BudgetStore, CONFIG_DERIVED_HOSTS, type CallOptions, type Capability, type ChatInput, type ChatRequest, type ChatResult, type ChatStreamEvent, type CheckVoiceOptions, type ClassifyInput, type ClassifyResult, type ContentPart, type Contracts, type CostQuery, type CostSink, type CostSummary, type CostSummaryQuery, type CostTimeseriesQuery, DEFAULT_BASE_URLS, 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 FixedHostProvider, type HttpResponse, type ImageInput, type ImageRequest, type ImageResult, LOCALLY_PINNED_HOSTS, 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 Region, type RerankInput, type RerankResult, type Role, type RouteForecast, 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, type VoiceInfo, type VoiceProvider, type VoiceResolveResult, type VoiceStatus, VoiceUnavailableError, type WordTiming, aiConfigSchema, alignWordTimings, anthropicAdapter, anthropicApiAdapter, anthropicSubprocessAdapter, azureAdapter, bflAdapter, bflCredits, chatInputSchema, checkVoice, classifyRegionName, computeCost, createAI, deepinfraAdapter, deeplAdapter, deepseekAdapter, defaultBaseUrl, defaultProviders, discordSink, elevenlabsAdapter, embeddingInputSchema, falAdapter, falStubAdapter, freshUsage, fromProviderToolCall, geminiAdapter, getCostSummary, getPrice, httpTransport, imageInputSchema, listAzureDanishVoices, listVoices, makeContracts, makeOpenAICompatibleAdapter, messageSchema, mistralAdapter, mistralStubAdapter, multiSink, noopSink, openaiAdapter, openaiStubAdapter, openrouterAdapter, parseClaudeCliJson, parseJsonLoose, refreshAvailability, regionOfHost, regionOfProvider, requestyAdapter, resetRefreshState, resetRegistry, resolveAzureVoice, resolveTier, resolveVoice, sqliteBudgetStore, sqliteSink, streamTransport, stubProviders, subprocessTransport, tierSpecSchema, toProviderTools, toolSchema, translateInputSchema, upmetricsCostClient, upmetricsSink, usdFromMicro, vertexAdapter, visionInputSchema, wouldProviderRouteTo, wouldRouteTo };
|
package/dist/index.js
CHANGED
|
@@ -20,6 +20,31 @@ import {
|
|
|
20
20
|
pricingGeneratedAt
|
|
21
21
|
} from "./chunk-OWXXPBKN.js";
|
|
22
22
|
|
|
23
|
+
// src/cost/default-hosts.ts
|
|
24
|
+
var DEFAULT_BASE_URLS = {
|
|
25
|
+
anthropic: "https://api.anthropic.com",
|
|
26
|
+
deepinfra: "https://api.deepinfra.com/v1/openai",
|
|
27
|
+
deepseek: "https://api.deepseek.com/v1",
|
|
28
|
+
elevenlabs: "https://api.elevenlabs.io/v1",
|
|
29
|
+
gemini: "https://generativelanguage.googleapis.com/v1beta",
|
|
30
|
+
mistral: "https://api.mistral.ai/v1",
|
|
31
|
+
openai: "https://api.openai.com/v1",
|
|
32
|
+
openrouter: "https://openrouter.ai/api/v1"
|
|
33
|
+
};
|
|
34
|
+
var LOCALLY_PINNED_HOSTS = {
|
|
35
|
+
bfl: "https://api.eu.bfl.ai"
|
|
36
|
+
};
|
|
37
|
+
var CONFIG_DERIVED_HOSTS = {
|
|
38
|
+
azure: "host comes from your region + resource name",
|
|
39
|
+
vertex: "host comes from your region (europe-west1 by default)",
|
|
40
|
+
deepl: "host differs between the free and paid plans",
|
|
41
|
+
requesty: "has BOTH an EU and a non-EU host \u2014 your config decides",
|
|
42
|
+
fal: "uses several hosts depending on the call"
|
|
43
|
+
};
|
|
44
|
+
function defaultBaseUrl(provider) {
|
|
45
|
+
return DEFAULT_BASE_URLS[provider];
|
|
46
|
+
}
|
|
47
|
+
|
|
23
48
|
// src/transport/http.ts
|
|
24
49
|
async function httpTransport(req) {
|
|
25
50
|
if (!req.http) {
|
|
@@ -401,7 +426,7 @@ function flattenForSubprocess(messages) {
|
|
|
401
426
|
return { prompt: turns.join("\n\n"), system: sys.length ? sys.join("\n") : void 0 };
|
|
402
427
|
}
|
|
403
428
|
function anthropicAdapter(config = {}) {
|
|
404
|
-
const baseUrl = config.baseUrl ??
|
|
429
|
+
const baseUrl = config.baseUrl ?? DEFAULT_BASE_URLS.anthropic;
|
|
405
430
|
const version = config.anthropicVersion ?? "2023-06-01";
|
|
406
431
|
function buildBody(req) {
|
|
407
432
|
const system = [];
|
|
@@ -892,7 +917,7 @@ function mapFinishReason(reason) {
|
|
|
892
917
|
|
|
893
918
|
// src/providers/openai.ts
|
|
894
919
|
function openaiAdapter(config = {}) {
|
|
895
|
-
const baseUrl = config.baseUrl ??
|
|
920
|
+
const baseUrl = config.baseUrl ?? DEFAULT_BASE_URLS.openai;
|
|
896
921
|
const base = makeOpenAICompatibleAdapter({ name: "openai", baseUrl, apiKey: config.apiKey });
|
|
897
922
|
async function embedding(req) {
|
|
898
923
|
const apiKey = config.apiKey ?? process.env.OPENAI_API_KEY;
|
|
@@ -1010,7 +1035,7 @@ function partsFrom(content) {
|
|
|
1010
1035
|
});
|
|
1011
1036
|
}
|
|
1012
1037
|
function geminiAdapter(config = {}) {
|
|
1013
|
-
const baseUrl = config.baseUrl ??
|
|
1038
|
+
const baseUrl = config.baseUrl ?? DEFAULT_BASE_URLS.gemini;
|
|
1014
1039
|
function resolveKey() {
|
|
1015
1040
|
const apiKey = config.apiKey ?? process.env.GOOGLE_API_KEY ?? process.env.GEMINI_API_KEY;
|
|
1016
1041
|
if (!apiKey) throw new Error("gemini adapter: API key not set (env GOOGLE_API_KEY or GEMINI_API_KEY)");
|
|
@@ -1239,14 +1264,14 @@ function mapGeminiFinish(reason) {
|
|
|
1239
1264
|
function deepinfraAdapter(config = {}) {
|
|
1240
1265
|
return makeOpenAICompatibleAdapter({
|
|
1241
1266
|
name: "deepinfra",
|
|
1242
|
-
baseUrl: config.baseUrl ??
|
|
1267
|
+
baseUrl: config.baseUrl ?? DEFAULT_BASE_URLS.deepinfra,
|
|
1243
1268
|
apiKey: config.apiKey
|
|
1244
1269
|
});
|
|
1245
1270
|
}
|
|
1246
1271
|
|
|
1247
1272
|
// src/providers/openrouter.ts
|
|
1248
1273
|
function openrouterAdapter(config = {}) {
|
|
1249
|
-
const baseUrl = config.baseUrl ??
|
|
1274
|
+
const baseUrl = config.baseUrl ?? DEFAULT_BASE_URLS.openrouter;
|
|
1250
1275
|
const headers = {
|
|
1251
1276
|
"HTTP-Referer": config.referer ?? "https://broberg.ai",
|
|
1252
1277
|
"X-Title": config.title ?? "@broberg/ai-sdk"
|
|
@@ -1328,7 +1353,7 @@ function deepseekAdapter(config = {}) {
|
|
|
1328
1353
|
return makeOpenAICompatibleAdapter({
|
|
1329
1354
|
name: "deepseek",
|
|
1330
1355
|
// → key DEEPSEEK_API_KEY
|
|
1331
|
-
baseUrl: config.baseUrl ??
|
|
1356
|
+
baseUrl: config.baseUrl ?? DEFAULT_BASE_URLS.deepseek,
|
|
1332
1357
|
apiKey: config.apiKey,
|
|
1333
1358
|
// Direct API returns no usage.cost → price from the table (not response).
|
|
1334
1359
|
costFromResponseField: false
|
|
@@ -1337,7 +1362,7 @@ function deepseekAdapter(config = {}) {
|
|
|
1337
1362
|
|
|
1338
1363
|
// src/providers/mistral.ts
|
|
1339
1364
|
function mistralAdapter(config = {}) {
|
|
1340
|
-
const baseUrl = config.baseUrl ??
|
|
1365
|
+
const baseUrl = config.baseUrl ?? DEFAULT_BASE_URLS.mistral;
|
|
1341
1366
|
const base = makeOpenAICompatibleAdapter({ name: "mistral", baseUrl, apiKey: config.apiKey, supportsPromptCacheKey: true, supportsPrefix: true });
|
|
1342
1367
|
function key() {
|
|
1343
1368
|
const k = config.apiKey ?? process.env.MISTRAL_API_KEY;
|
|
@@ -1591,7 +1616,7 @@ function ttsText(req) {
|
|
|
1591
1616
|
return applyPronunciations(req.text, req.pronunciations, (p) => p.alias);
|
|
1592
1617
|
}
|
|
1593
1618
|
function elevenlabsAdapter(config = {}) {
|
|
1594
|
-
const baseUrl = config.baseUrl ??
|
|
1619
|
+
const baseUrl = config.baseUrl ?? DEFAULT_BASE_URLS.elevenlabs;
|
|
1595
1620
|
const fetchImpl = config.fetch ?? fetch;
|
|
1596
1621
|
function key() {
|
|
1597
1622
|
const k = config.apiKey ?? process.env.ELEVENLABS_API_KEY;
|
|
@@ -1877,6 +1902,7 @@ function azureAdapter(config = {}) {
|
|
|
1877
1902
|
async function tts(req) {
|
|
1878
1903
|
if (req.wordTimings) return ttsBatch(req);
|
|
1879
1904
|
const format = req.format ?? DEFAULT_FORMAT;
|
|
1905
|
+
const ssml = buildSsml(req);
|
|
1880
1906
|
const res = await fetchImpl(
|
|
1881
1907
|
`https://${region()}.tts.speech.microsoft.com/cognitiveservices/v1`,
|
|
1882
1908
|
{
|
|
@@ -1886,7 +1912,7 @@ function azureAdapter(config = {}) {
|
|
|
1886
1912
|
"Content-Type": "application/ssml+xml",
|
|
1887
1913
|
"X-Microsoft-OutputFormat": format
|
|
1888
1914
|
},
|
|
1889
|
-
body:
|
|
1915
|
+
body: ssml
|
|
1890
1916
|
}
|
|
1891
1917
|
);
|
|
1892
1918
|
if (!res.ok) {
|
|
@@ -1894,9 +1920,10 @@ function azureAdapter(config = {}) {
|
|
|
1894
1920
|
throw new Error(`azure tts ${res.status}: ${body.slice(0, 300)}`);
|
|
1895
1921
|
}
|
|
1896
1922
|
const audio = new Uint8Array(await res.arrayBuffer());
|
|
1897
|
-
return { audio, mimeType: "audio/mpeg", usage: priceFor(req.text.length, req.spec.model) };
|
|
1923
|
+
return { audio, mimeType: "audio/mpeg", ssml, usage: priceFor(req.text.length, req.spec.model) };
|
|
1898
1924
|
}
|
|
1899
1925
|
async function ttsBatch(req) {
|
|
1926
|
+
const ssml = buildSsml(req);
|
|
1900
1927
|
const picked = sttHost();
|
|
1901
1928
|
if (picked.source === "regional-fallback") {
|
|
1902
1929
|
throw new Error(
|
|
@@ -1912,7 +1939,7 @@ function azureAdapter(config = {}) {
|
|
|
1912
1939
|
headers,
|
|
1913
1940
|
body: JSON.stringify({
|
|
1914
1941
|
inputKind: "SSML",
|
|
1915
|
-
inputs: [{ content:
|
|
1942
|
+
inputs: [{ content: ssml }],
|
|
1916
1943
|
properties: {
|
|
1917
1944
|
wordBoundaryEnabled: true,
|
|
1918
1945
|
// ONE audio file and ONE word list for the whole text. Without it a chunked
|
|
@@ -1972,6 +1999,7 @@ function azureAdapter(config = {}) {
|
|
|
1972
1999
|
// Batch defaults to riff PCM, not mp3 — saying audio/mpeg here would be a lie the
|
|
1973
2000
|
// browser would act on.
|
|
1974
2001
|
mimeType: req.format?.includes("mp3") ? "audio/mpeg" : "audio/wav",
|
|
2002
|
+
ssml,
|
|
1975
2003
|
// Aligned against the ORIGINAL text, with the dictionary, so the offsets index the
|
|
1976
2004
|
// manuscript rather than the SSML we sent.
|
|
1977
2005
|
wordTimings: alignWordTimings(req.text, boundaries, { pronunciations: req.pronunciations }),
|
|
@@ -3334,8 +3362,8 @@ var aiConfigSchema = z.object({
|
|
|
3334
3362
|
});
|
|
3335
3363
|
|
|
3336
3364
|
// src/version.ts
|
|
3337
|
-
var VERSION = "0.
|
|
3338
|
-
var SDK_TAG = "@broberg/ai-sdk@0.
|
|
3365
|
+
var VERSION = "0.47.0";
|
|
3366
|
+
var SDK_TAG = "@broberg/ai-sdk@0.47.0";
|
|
3339
3367
|
|
|
3340
3368
|
// src/cost/sinks/upmetrics.ts
|
|
3341
3369
|
function upmetricsSink(config) {
|
|
@@ -4342,14 +4370,39 @@ function upmetricsCostClient(config) {
|
|
|
4342
4370
|
}
|
|
4343
4371
|
};
|
|
4344
4372
|
}
|
|
4373
|
+
|
|
4374
|
+
// src/cost/would-route.ts
|
|
4375
|
+
var ASSUMPTIONS = [
|
|
4376
|
+
"you do not set your own baseUrl \u2014 we cannot know where your gateway forwards to",
|
|
4377
|
+
"you pass no fallback: a fallback IS a route, and the route decides residency",
|
|
4378
|
+
"you pass no override that changes the provider"
|
|
4379
|
+
];
|
|
4380
|
+
function wouldProviderRouteTo(provider) {
|
|
4381
|
+
const onlyIf = [...ASSUMPTIONS];
|
|
4382
|
+
const host = DEFAULT_BASE_URLS[provider] ?? LOCALLY_PINNED_HOSTS[provider];
|
|
4383
|
+
if (host) {
|
|
4384
|
+
return { kind: "forecast", provider, host, wouldRouteTo: regionOfHost(host), onlyIf };
|
|
4385
|
+
}
|
|
4386
|
+
const because = CONFIG_DERIVED_HOSTS[provider];
|
|
4387
|
+
if (because) return { kind: "depends-on-config", provider, because, onlyIf };
|
|
4388
|
+
return { kind: "unknown-provider", provider, onlyIf };
|
|
4389
|
+
}
|
|
4390
|
+
function wouldRouteTo(tier) {
|
|
4391
|
+
const spec = DEFAULT_TIER_MAP[tier];
|
|
4392
|
+
if (!spec) return { kind: "unknown-provider", provider: String(tier), onlyIf: [...ASSUMPTIONS] };
|
|
4393
|
+
return wouldProviderRouteTo(spec.provider);
|
|
4394
|
+
}
|
|
4345
4395
|
export {
|
|
4346
4396
|
AZURE_DANISH_VOICES,
|
|
4347
4397
|
AZURE_DANISH_VOICE_LIST,
|
|
4348
4398
|
BudgetExceededError,
|
|
4349
4399
|
BudgetGuard,
|
|
4400
|
+
CONFIG_DERIVED_HOSTS,
|
|
4401
|
+
DEFAULT_BASE_URLS,
|
|
4350
4402
|
DEFAULT_CLIP_SEC,
|
|
4351
4403
|
DEFAULT_TIER_MAP,
|
|
4352
4404
|
ELEVENLABS_DANISH_VOICES,
|
|
4405
|
+
LOCALLY_PINNED_HOSTS,
|
|
4353
4406
|
MEDIA_PRICING_CHECKED_AT,
|
|
4354
4407
|
ModelUnavailableError,
|
|
4355
4408
|
PRICING_STALE_AFTER_DAYS,
|
|
@@ -4374,6 +4427,7 @@ export {
|
|
|
4374
4427
|
deepinfraAdapter,
|
|
4375
4428
|
deeplAdapter,
|
|
4376
4429
|
deepseekAdapter,
|
|
4430
|
+
defaultBaseUrl,
|
|
4377
4431
|
defaultProviders,
|
|
4378
4432
|
discordSink,
|
|
4379
4433
|
elevenlabsAdapter,
|
|
@@ -4429,6 +4483,8 @@ export {
|
|
|
4429
4483
|
upmetricsSink,
|
|
4430
4484
|
usdFromMicro,
|
|
4431
4485
|
vertexAdapter,
|
|
4432
|
-
visionInputSchema
|
|
4486
|
+
visionInputSchema,
|
|
4487
|
+
wouldProviderRouteTo,
|
|
4488
|
+
wouldRouteTo
|
|
4433
4489
|
};
|
|
4434
4490
|
//# sourceMappingURL=index.js.map
|