@broberg/ai-sdk 0.44.0 → 0.45.1
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 +126 -35
- package/dist/index.js +220 -10
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -87,6 +87,61 @@ interface Pronunciation {
|
|
|
87
87
|
lang?: string;
|
|
88
88
|
}
|
|
89
89
|
|
|
90
|
+
/** One entry of Azure's `[nnnn].word.json`, verbatim field names. */
|
|
91
|
+
interface AzureWordBoundary {
|
|
92
|
+
Text: string;
|
|
93
|
+
/** Milliseconds into the audio. */
|
|
94
|
+
AudioOffset: number;
|
|
95
|
+
/** Milliseconds. */
|
|
96
|
+
Duration: number;
|
|
97
|
+
}
|
|
98
|
+
interface WordTiming {
|
|
99
|
+
/** The spoken word, as Azure reported it. */
|
|
100
|
+
text: string;
|
|
101
|
+
startMs: number;
|
|
102
|
+
endMs: number;
|
|
103
|
+
/** Character span in the SOURCE text. Several spoken words share one span when they
|
|
104
|
+
* came from one substituted manuscript word. */
|
|
105
|
+
sourceStart: number;
|
|
106
|
+
sourceEnd: number;
|
|
107
|
+
}
|
|
108
|
+
interface AlignedWordTimings {
|
|
109
|
+
/** Every word we could place in the source, in audio order. */
|
|
110
|
+
words: WordTiming[];
|
|
111
|
+
/** Spoken words we could NOT place, in order, verbatim.
|
|
112
|
+
*
|
|
113
|
+
* Named rather than dropped, and never given a guessed offset. "I could not place
|
|
114
|
+
* this" and "it was here" must not be the same answer — the same discipline as
|
|
115
|
+
* `rerank.unscored` (F052). A highlighter that treats `words` as complete must look
|
|
116
|
+
* here. */
|
|
117
|
+
unaligned: string[];
|
|
118
|
+
}
|
|
119
|
+
/** A pronunciation entry, narrowed to what alignment needs. */
|
|
120
|
+
interface AliasEntry {
|
|
121
|
+
word: string;
|
|
122
|
+
alias?: string;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Map Azure's spoken word list onto positions in the SOURCE text.
|
|
126
|
+
*
|
|
127
|
+
* Two rules carry the whole function:
|
|
128
|
+
*
|
|
129
|
+
* **The walk only moves FORWARD.** That is what puts a repeated word on its own
|
|
130
|
+
* occurrence — "AI er AI" must not place the second on the first. Searching from 0 each
|
|
131
|
+
* time passes every test with distinct words and fails silently on real prose.
|
|
132
|
+
*
|
|
133
|
+
* **A substitution is matched as a RUN, not word by word.** Azure speaks SSML, so
|
|
134
|
+
* `<sub alias='broberg punktum a i'>broberg.ai</sub>` puts four words in the list where
|
|
135
|
+
* the manuscript has one. Matching them individually is not merely imprecise, it is
|
|
136
|
+
* wrong in a way that bites every Danish text: MEASURED on cms's own dictionary entry,
|
|
137
|
+
* a per-word map made the alias's "i" swallow the next real Danish word "i" in
|
|
138
|
+
* "broberg.ai i dag". An alias's parts are often ordinary words ("a", "i", "punktum"),
|
|
139
|
+
* so a lookup table cannot be the mechanism — the run has to be consumed positionally.
|
|
140
|
+
*/
|
|
141
|
+
declare function alignWordTimings(text: string, words: AzureWordBoundary[], opts?: {
|
|
142
|
+
pronunciations?: AliasEntry[];
|
|
143
|
+
}): AlignedWordTimings;
|
|
144
|
+
|
|
90
145
|
/** How a call reaches the model. `http` = provider REST API; `subprocess` = local
|
|
91
146
|
* `claude -p` CLI (Max plan, costUsd 0). */
|
|
92
147
|
type Transport = "http" | "subprocess";
|
|
@@ -492,6 +547,14 @@ interface PodcastResult {
|
|
|
492
547
|
/** Episode audio bytes. */
|
|
493
548
|
audio: Uint8Array;
|
|
494
549
|
mimeType: string;
|
|
550
|
+
/** F055 — per-word timings, present only when `wordTimings` was asked for AND the
|
|
551
|
+
* route produces them. Its ABSENCE says "not available here"; an empty `words` array
|
|
552
|
+
* would say "the text had no words", which is a different claim.
|
|
553
|
+
*
|
|
554
|
+
* `sourceStart`/`sourceEnd` index the ORIGINAL text you passed, not the SSML we sent
|
|
555
|
+
* — Azure reports only audio time, so the link back to the manuscript is derived
|
|
556
|
+
* here. `unaligned` names any spoken word that could not be placed. */
|
|
557
|
+
wordTimings?: AlignedWordTimings;
|
|
495
558
|
usage: Usage;
|
|
496
559
|
}
|
|
497
560
|
|
|
@@ -512,6 +575,16 @@ interface TtsRequest {
|
|
|
512
575
|
* into SSML** — the substitution happens adapter-side AFTER the text is escaped, so
|
|
513
576
|
* `text` can never inject markup, and `alias`/`ipa` are escaped too. */
|
|
514
577
|
pronunciations?: Pronunciation[];
|
|
578
|
+
/** F055 — ask for per-word timings so a reader can highlight each word as it is
|
|
579
|
+
* spoken. Azure only. Absent from the result means "this route does not produce
|
|
580
|
+
* them", which is a different answer from an empty list.
|
|
581
|
+
*
|
|
582
|
+
* **This changes the ROUTE, not just the payload.** The real-time endpoint returns
|
|
583
|
+
* audio only; word boundaries exist solely on Azure's asynchronous batch synthesis
|
|
584
|
+
* API, so the call becomes submit → poll → fetch. Microsoft quotes 10–20s for half
|
|
585
|
+
* of all jobs and up to 120s for 95%. Worth it for a pre-generated, cached reading;
|
|
586
|
+
* wrong for anything a user is waiting on. */
|
|
587
|
+
wordTimings?: boolean;
|
|
515
588
|
spec: TierSpec;
|
|
516
589
|
}
|
|
517
590
|
interface BatchRequestItem {
|
|
@@ -1322,6 +1395,7 @@ declare const imageInputSchema: z.ZodObject<{
|
|
|
1322
1395
|
tier?: "fast" | "smart" | "powerful" | "cheap" | "vision" | "video" | "embedding" | undefined;
|
|
1323
1396
|
seed?: number | undefined;
|
|
1324
1397
|
purpose?: string | undefined;
|
|
1398
|
+
outputFormat?: "jpeg" | "png" | "webp" | undefined;
|
|
1325
1399
|
loras?: {
|
|
1326
1400
|
path: string;
|
|
1327
1401
|
scale?: number | undefined;
|
|
@@ -1343,7 +1417,6 @@ declare const imageInputSchema: z.ZodObject<{
|
|
|
1343
1417
|
finetune?: string | undefined;
|
|
1344
1418
|
finetuneStrength?: number | undefined;
|
|
1345
1419
|
referenceImages?: (string | Uint8Array<ArrayBuffer>)[] | undefined;
|
|
1346
|
-
outputFormat?: "jpeg" | "png" | "webp" | undefined;
|
|
1347
1420
|
safetyTolerance?: number | undefined;
|
|
1348
1421
|
retryOnBlack?: boolean | undefined;
|
|
1349
1422
|
}, {
|
|
@@ -1351,6 +1424,7 @@ declare const imageInputSchema: z.ZodObject<{
|
|
|
1351
1424
|
tier?: "fast" | "smart" | "powerful" | "cheap" | "vision" | "video" | "embedding" | undefined;
|
|
1352
1425
|
seed?: number | undefined;
|
|
1353
1426
|
purpose?: string | undefined;
|
|
1427
|
+
outputFormat?: "jpeg" | "png" | "webp" | undefined;
|
|
1354
1428
|
loras?: {
|
|
1355
1429
|
path: string;
|
|
1356
1430
|
scale?: number | undefined;
|
|
@@ -1372,7 +1446,6 @@ declare const imageInputSchema: z.ZodObject<{
|
|
|
1372
1446
|
finetune?: string | undefined;
|
|
1373
1447
|
finetuneStrength?: number | undefined;
|
|
1374
1448
|
referenceImages?: (string | Uint8Array<ArrayBuffer>)[] | undefined;
|
|
1375
|
-
outputFormat?: "jpeg" | "png" | "webp" | undefined;
|
|
1376
1449
|
safetyTolerance?: number | undefined;
|
|
1377
1450
|
retryOnBlack?: boolean | undefined;
|
|
1378
1451
|
}>;
|
|
@@ -1873,6 +1946,13 @@ declare const podcastInputSchema: z.ZodObject<{
|
|
|
1873
1946
|
voices: Record<string, string>;
|
|
1874
1947
|
tier?: "fast" | "smart" | "powerful" | "cheap" | "vision" | "video" | "embedding" | undefined;
|
|
1875
1948
|
purpose?: string | undefined;
|
|
1949
|
+
pronunciations?: {
|
|
1950
|
+
word: string;
|
|
1951
|
+
matchInCompounds?: boolean | undefined;
|
|
1952
|
+
alias?: string | undefined;
|
|
1953
|
+
ipa?: string | undefined;
|
|
1954
|
+
lang?: string | undefined;
|
|
1955
|
+
}[] | undefined;
|
|
1876
1956
|
override?: {
|
|
1877
1957
|
provider?: string | undefined;
|
|
1878
1958
|
model?: string | undefined;
|
|
@@ -1885,13 +1965,6 @@ declare const podcastInputSchema: z.ZodObject<{
|
|
|
1885
1965
|
})[] | undefined;
|
|
1886
1966
|
labels?: Record<string, string> | undefined;
|
|
1887
1967
|
format?: string | undefined;
|
|
1888
|
-
pronunciations?: {
|
|
1889
|
-
word: string;
|
|
1890
|
-
matchInCompounds?: boolean | undefined;
|
|
1891
|
-
alias?: string | undefined;
|
|
1892
|
-
ipa?: string | undefined;
|
|
1893
|
-
lang?: string | undefined;
|
|
1894
|
-
}[] | undefined;
|
|
1895
1968
|
}, {
|
|
1896
1969
|
script: {
|
|
1897
1970
|
text: string;
|
|
@@ -1900,6 +1973,13 @@ declare const podcastInputSchema: z.ZodObject<{
|
|
|
1900
1973
|
voices: Record<string, string>;
|
|
1901
1974
|
tier?: "fast" | "smart" | "powerful" | "cheap" | "vision" | "video" | "embedding" | undefined;
|
|
1902
1975
|
purpose?: string | undefined;
|
|
1976
|
+
pronunciations?: {
|
|
1977
|
+
word: string;
|
|
1978
|
+
matchInCompounds?: boolean | undefined;
|
|
1979
|
+
alias?: string | undefined;
|
|
1980
|
+
ipa?: string | undefined;
|
|
1981
|
+
lang?: string | undefined;
|
|
1982
|
+
}[] | undefined;
|
|
1903
1983
|
override?: {
|
|
1904
1984
|
provider?: string | undefined;
|
|
1905
1985
|
model?: string | undefined;
|
|
@@ -1912,13 +1992,6 @@ declare const podcastInputSchema: z.ZodObject<{
|
|
|
1912
1992
|
})[] | undefined;
|
|
1913
1993
|
labels?: Record<string, string> | undefined;
|
|
1914
1994
|
format?: string | undefined;
|
|
1915
|
-
pronunciations?: {
|
|
1916
|
-
word: string;
|
|
1917
|
-
matchInCompounds?: boolean | undefined;
|
|
1918
|
-
alias?: string | undefined;
|
|
1919
|
-
ipa?: string | undefined;
|
|
1920
|
-
lang?: string | undefined;
|
|
1921
|
-
}[] | undefined;
|
|
1922
1995
|
}>;
|
|
1923
1996
|
declare const ttsInputSchema: z.ZodObject<{
|
|
1924
1997
|
tier: z.ZodOptional<z.ZodEnum<["fast", "smart", "powerful", "cheap", "vision", "video", "embedding"]>>;
|
|
@@ -1974,6 +2047,8 @@ declare const ttsInputSchema: z.ZodObject<{
|
|
|
1974
2047
|
ipa?: string | undefined;
|
|
1975
2048
|
lang?: string | undefined;
|
|
1976
2049
|
}>, "many">>;
|
|
2050
|
+
/** F055 — per-word timings (Azure only). Changes the route to batch synthesis. */
|
|
2051
|
+
wordTimings: z.ZodOptional<z.ZodBoolean>;
|
|
1977
2052
|
/** F037: voice to use if `voice` is one we know the provider has retired. Without
|
|
1978
2053
|
* it a retired voice throws VoiceUnavailableError rather than reaching the API.
|
|
1979
2054
|
*
|
|
@@ -1993,6 +2068,14 @@ declare const ttsInputSchema: z.ZodObject<{
|
|
|
1993
2068
|
voice: string;
|
|
1994
2069
|
tier?: "fast" | "smart" | "powerful" | "cheap" | "vision" | "video" | "embedding" | undefined;
|
|
1995
2070
|
purpose?: string | undefined;
|
|
2071
|
+
wordTimings?: boolean | undefined;
|
|
2072
|
+
pronunciations?: {
|
|
2073
|
+
word: string;
|
|
2074
|
+
matchInCompounds?: boolean | undefined;
|
|
2075
|
+
alias?: string | undefined;
|
|
2076
|
+
ipa?: string | undefined;
|
|
2077
|
+
lang?: string | undefined;
|
|
2078
|
+
}[] | undefined;
|
|
1996
2079
|
override?: {
|
|
1997
2080
|
provider?: string | undefined;
|
|
1998
2081
|
model?: string | undefined;
|
|
@@ -2006,13 +2089,6 @@ declare const ttsInputSchema: z.ZodObject<{
|
|
|
2006
2089
|
labels?: Record<string, string> | undefined;
|
|
2007
2090
|
lang?: string | undefined;
|
|
2008
2091
|
format?: string | undefined;
|
|
2009
|
-
pronunciations?: {
|
|
2010
|
-
word: string;
|
|
2011
|
-
matchInCompounds?: boolean | undefined;
|
|
2012
|
-
alias?: string | undefined;
|
|
2013
|
-
ipa?: string | undefined;
|
|
2014
|
-
lang?: string | undefined;
|
|
2015
|
-
}[] | undefined;
|
|
2016
2092
|
voiceFallback?: string | undefined;
|
|
2017
2093
|
rate?: number | undefined;
|
|
2018
2094
|
}, {
|
|
@@ -2020,6 +2096,14 @@ declare const ttsInputSchema: z.ZodObject<{
|
|
|
2020
2096
|
voice: string;
|
|
2021
2097
|
tier?: "fast" | "smart" | "powerful" | "cheap" | "vision" | "video" | "embedding" | undefined;
|
|
2022
2098
|
purpose?: string | undefined;
|
|
2099
|
+
wordTimings?: boolean | undefined;
|
|
2100
|
+
pronunciations?: {
|
|
2101
|
+
word: string;
|
|
2102
|
+
matchInCompounds?: boolean | undefined;
|
|
2103
|
+
alias?: string | undefined;
|
|
2104
|
+
ipa?: string | undefined;
|
|
2105
|
+
lang?: string | undefined;
|
|
2106
|
+
}[] | undefined;
|
|
2023
2107
|
override?: {
|
|
2024
2108
|
provider?: string | undefined;
|
|
2025
2109
|
model?: string | undefined;
|
|
@@ -2033,13 +2117,6 @@ declare const ttsInputSchema: z.ZodObject<{
|
|
|
2033
2117
|
labels?: Record<string, string> | undefined;
|
|
2034
2118
|
lang?: string | undefined;
|
|
2035
2119
|
format?: string | undefined;
|
|
2036
|
-
pronunciations?: {
|
|
2037
|
-
word: string;
|
|
2038
|
-
matchInCompounds?: boolean | undefined;
|
|
2039
|
-
alias?: string | undefined;
|
|
2040
|
-
ipa?: string | undefined;
|
|
2041
|
-
lang?: string | undefined;
|
|
2042
|
-
}[] | undefined;
|
|
2043
2120
|
voiceFallback?: string | undefined;
|
|
2044
2121
|
rate?: number | undefined;
|
|
2045
2122
|
}>;
|
|
@@ -2308,11 +2385,25 @@ declare function azureAdapter(config?: {
|
|
|
2308
2385
|
sttPricePerMin?: number;
|
|
2309
2386
|
/** STT base URL override (e.g. a resource custom domain). */
|
|
2310
2387
|
sttBaseUrl?: string;
|
|
2311
|
-
/** Resource name → custom-domain
|
|
2312
|
-
* (or env AZURE_SPEECH_RESOURCE).
|
|
2388
|
+
/** Resource name → custom-domain host `{resource}.cognitiveservices.azure.com`
|
|
2389
|
+
* (or env AZURE_SPEECH_RESOURCE).
|
|
2390
|
+
*
|
|
2391
|
+
* **STT works without it** — the regional host is a legitimate route for
|
|
2392
|
+
* `speechtotext/transcriptions` and is the tested default (F029).
|
|
2393
|
+
*
|
|
2394
|
+
* **BATCH SYNTHESIS REQUIRES IT.** Every example in Microsoft's batch-synthesis
|
|
2395
|
+
* docs uses the resource host; the regional host is shown nowhere, and cms measured
|
|
2396
|
+
* it answering 401 with a valid key (F055.3). Without this set, `wordTimings` fails
|
|
2397
|
+
* before the call rather than spending your time on Azure's "invalid subscription
|
|
2398
|
+
* key or wrong API endpoint" — which names the key first and sends you the wrong way. */
|
|
2313
2399
|
resource?: string;
|
|
2314
2400
|
/** Fast-transcription api-version (overrides the GA default). */
|
|
2315
2401
|
sttApiVersion?: string;
|
|
2402
|
+
/** F055 — how long to wait for a batch synthesis job. Default 180s (Microsoft's own
|
|
2403
|
+
* 95th percentile is 120s). */
|
|
2404
|
+
batchTimeoutMs?: number;
|
|
2405
|
+
/** F055 — poll interval for batch synthesis. Default 3s. */
|
|
2406
|
+
batchPollMs?: number;
|
|
2316
2407
|
/** phraseList biasing weight (0–2) applied when a call passes `phrases`. Default 1.5. */
|
|
2317
2408
|
sttBiasingWeight?: number;
|
|
2318
2409
|
}): ProviderAdapter;
|
|
@@ -2432,8 +2523,8 @@ declare const falStubAdapter: ProviderAdapter;
|
|
|
2432
2523
|
* wires the live adapters. */
|
|
2433
2524
|
declare const stubProviders: Record<string, ProviderAdapter>;
|
|
2434
2525
|
|
|
2435
|
-
declare const VERSION: "0.
|
|
2436
|
-
declare const SDK_TAG: "@broberg/ai-sdk@0.
|
|
2526
|
+
declare const VERSION: "0.45.1";
|
|
2527
|
+
declare const SDK_TAG: "@broberg/ai-sdk@0.45.1";
|
|
2437
2528
|
|
|
2438
2529
|
/** Built-in defaults. Every entry is overridable via AiConfig.defaults or a
|
|
2439
2530
|
* per-call override.
|
|
@@ -2844,4 +2935,4 @@ interface StreamTransportRequest extends TransportRequest {
|
|
|
2844
2935
|
*/
|
|
2845
2936
|
declare function streamTransport(req: StreamTransportRequest): AsyncIterable<string>;
|
|
2846
2937
|
|
|
2847
|
-
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 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, aiConfigSchema, 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 };
|
|
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 };
|
package/dist/index.js
CHANGED
|
@@ -1658,6 +1658,124 @@ function elevenlabsAdapter(config = {}) {
|
|
|
1658
1658
|
return { name: "elevenlabs", dialogue, tts, listVoices: listVoices2 };
|
|
1659
1659
|
}
|
|
1660
1660
|
|
|
1661
|
+
// src/providers/word-timings.ts
|
|
1662
|
+
var PUNCTUATION = /* @__PURE__ */ new Set([".", ",", "!", "?", ":", ";", "\u2026", "\u2014", "\u2013", '"', "'", "(", ")"]);
|
|
1663
|
+
var fold = (s) => s.toLowerCase();
|
|
1664
|
+
function alignWordTimings(text, words, opts = {}) {
|
|
1665
|
+
const out = [];
|
|
1666
|
+
const unaligned = [];
|
|
1667
|
+
const haystack = fold(text);
|
|
1668
|
+
const aliases = (opts.pronunciations ?? []).filter((p) => typeof p.alias === "string" && p.alias.trim() !== "").map((p) => ({ word: p.word, parts: p.alias.trim().split(/\s+/).map(fold) })).sort((a, b) => b.parts.length - a.parts.length);
|
|
1669
|
+
const spoken = words.filter((w) => !PUNCTUATION.has(w.Text.trim()));
|
|
1670
|
+
let cursor = 0;
|
|
1671
|
+
let i = 0;
|
|
1672
|
+
while (i < spoken.length) {
|
|
1673
|
+
let consumed = false;
|
|
1674
|
+
for (const a of aliases) {
|
|
1675
|
+
if (i + a.parts.length > spoken.length) continue;
|
|
1676
|
+
const matches = a.parts.every((part, k) => fold(spoken[i + k].Text) === part);
|
|
1677
|
+
if (!matches) continue;
|
|
1678
|
+
const at2 = haystack.indexOf(fold(a.word), cursor);
|
|
1679
|
+
if (at2 === -1) continue;
|
|
1680
|
+
const span = { start: at2, end: at2 + a.word.length };
|
|
1681
|
+
for (let k = 0; k < a.parts.length; k++) {
|
|
1682
|
+
const w2 = spoken[i + k];
|
|
1683
|
+
out.push({
|
|
1684
|
+
text: w2.Text,
|
|
1685
|
+
startMs: w2.AudioOffset,
|
|
1686
|
+
endMs: w2.AudioOffset + w2.Duration,
|
|
1687
|
+
sourceStart: span.start,
|
|
1688
|
+
sourceEnd: span.end
|
|
1689
|
+
});
|
|
1690
|
+
}
|
|
1691
|
+
cursor = span.end;
|
|
1692
|
+
i += a.parts.length;
|
|
1693
|
+
consumed = true;
|
|
1694
|
+
break;
|
|
1695
|
+
}
|
|
1696
|
+
if (consumed) continue;
|
|
1697
|
+
const w = spoken[i];
|
|
1698
|
+
const at = haystack.indexOf(fold(w.Text), cursor);
|
|
1699
|
+
if (at === -1) {
|
|
1700
|
+
unaligned.push(w.Text);
|
|
1701
|
+
} else {
|
|
1702
|
+
out.push({
|
|
1703
|
+
text: w.Text,
|
|
1704
|
+
startMs: w.AudioOffset,
|
|
1705
|
+
endMs: w.AudioOffset + w.Duration,
|
|
1706
|
+
sourceStart: at,
|
|
1707
|
+
sourceEnd: at + w.Text.length
|
|
1708
|
+
});
|
|
1709
|
+
cursor = at + w.Text.length;
|
|
1710
|
+
}
|
|
1711
|
+
i++;
|
|
1712
|
+
}
|
|
1713
|
+
return { words: out, unaligned };
|
|
1714
|
+
}
|
|
1715
|
+
|
|
1716
|
+
// src/providers/zip.ts
|
|
1717
|
+
import { inflateRawSync } from "zlib";
|
|
1718
|
+
var EOCD_SIG = 101010256;
|
|
1719
|
+
var CEN_SIG = 33639248;
|
|
1720
|
+
function readZipEntry(zip, name) {
|
|
1721
|
+
const view = new DataView(zip.buffer, zip.byteOffset, zip.byteLength);
|
|
1722
|
+
let eocd = -1;
|
|
1723
|
+
for (let i = zip.length - 22; i >= 0; i--) {
|
|
1724
|
+
if (view.getUint32(i, true) === EOCD_SIG) {
|
|
1725
|
+
eocd = i;
|
|
1726
|
+
break;
|
|
1727
|
+
}
|
|
1728
|
+
}
|
|
1729
|
+
if (eocd === -1) throw new Error("readZipEntry: not a ZIP archive (no end-of-central-directory record)");
|
|
1730
|
+
const entryCount = view.getUint16(eocd + 10, true);
|
|
1731
|
+
let p = view.getUint32(eocd + 16, true);
|
|
1732
|
+
for (let n = 0; n < entryCount; n++) {
|
|
1733
|
+
if (view.getUint32(p, true) !== CEN_SIG) {
|
|
1734
|
+
throw new Error(`readZipEntry: corrupt central directory at entry ${n}`);
|
|
1735
|
+
}
|
|
1736
|
+
const method = view.getUint16(p + 10, true);
|
|
1737
|
+
const compressedSize = view.getUint32(p + 20, true);
|
|
1738
|
+
const nameLen = view.getUint16(p + 28, true);
|
|
1739
|
+
const extraLen = view.getUint16(p + 30, true);
|
|
1740
|
+
const commentLen = view.getUint16(p + 32, true);
|
|
1741
|
+
const localOffset = view.getUint32(p + 42, true);
|
|
1742
|
+
const entryName = new TextDecoder().decode(zip.subarray(p + 46, p + 46 + nameLen));
|
|
1743
|
+
if (entryName === name) {
|
|
1744
|
+
const lNameLen = view.getUint16(localOffset + 26, true);
|
|
1745
|
+
const lExtraLen = view.getUint16(localOffset + 28, true);
|
|
1746
|
+
const start = localOffset + 30 + lNameLen + lExtraLen;
|
|
1747
|
+
const raw = zip.subarray(start, start + compressedSize);
|
|
1748
|
+
if (method === 0) return new Uint8Array(raw);
|
|
1749
|
+
if (method === 8) return new Uint8Array(inflateRawSync(raw));
|
|
1750
|
+
throw new Error(`readZipEntry: "${name}" uses unsupported compression method ${method}`);
|
|
1751
|
+
}
|
|
1752
|
+
p += 46 + nameLen + extraLen + commentLen;
|
|
1753
|
+
}
|
|
1754
|
+
return void 0;
|
|
1755
|
+
}
|
|
1756
|
+
function listZipEntries(zip) {
|
|
1757
|
+
const view = new DataView(zip.buffer, zip.byteOffset, zip.byteLength);
|
|
1758
|
+
let eocd = -1;
|
|
1759
|
+
for (let i = zip.length - 22; i >= 0; i--) {
|
|
1760
|
+
if (view.getUint32(i, true) === EOCD_SIG) {
|
|
1761
|
+
eocd = i;
|
|
1762
|
+
break;
|
|
1763
|
+
}
|
|
1764
|
+
}
|
|
1765
|
+
if (eocd === -1) throw new Error("listZipEntries: not a ZIP archive (no end-of-central-directory record)");
|
|
1766
|
+
const entryCount = view.getUint16(eocd + 10, true);
|
|
1767
|
+
let p = view.getUint32(eocd + 16, true);
|
|
1768
|
+
const names = [];
|
|
1769
|
+
for (let n = 0; n < entryCount; n++) {
|
|
1770
|
+
const nameLen = view.getUint16(p + 28, true);
|
|
1771
|
+
const extraLen = view.getUint16(p + 30, true);
|
|
1772
|
+
const commentLen = view.getUint16(p + 32, true);
|
|
1773
|
+
names.push(new TextDecoder().decode(zip.subarray(p + 46, p + 46 + nameLen)));
|
|
1774
|
+
p += 46 + nameLen + extraLen + commentLen;
|
|
1775
|
+
}
|
|
1776
|
+
return names;
|
|
1777
|
+
}
|
|
1778
|
+
|
|
1661
1779
|
// src/providers/azure.ts
|
|
1662
1780
|
var DEFAULT_STT_API_VERSION = "2025-10-15";
|
|
1663
1781
|
var DEFAULT_REGION = "westeurope";
|
|
@@ -1718,11 +1836,14 @@ function azureAdapter(config = {}) {
|
|
|
1718
1836
|
}
|
|
1719
1837
|
return classifyRegionName(region());
|
|
1720
1838
|
}
|
|
1721
|
-
function
|
|
1722
|
-
if (config.sttBaseUrl) return config.sttBaseUrl.replace(/\/$/, "");
|
|
1839
|
+
function sttHost() {
|
|
1840
|
+
if (config.sttBaseUrl) return { url: config.sttBaseUrl.replace(/\/$/, ""), source: "explicit" };
|
|
1723
1841
|
const resource = config.resource ?? process.env.AZURE_SPEECH_RESOURCE;
|
|
1724
|
-
if (resource) return `https://${resource}.cognitiveservices.azure.com
|
|
1725
|
-
return `https://${region()}.api.cognitive.microsoft.com
|
|
1842
|
+
if (resource) return { url: `https://${resource}.cognitiveservices.azure.com`, source: "resource" };
|
|
1843
|
+
return { url: `https://${region()}.api.cognitive.microsoft.com`, source: "regional-fallback" };
|
|
1844
|
+
}
|
|
1845
|
+
function sttBaseUrl() {
|
|
1846
|
+
return sttHost().url;
|
|
1726
1847
|
}
|
|
1727
1848
|
function priceFor(chars, model) {
|
|
1728
1849
|
const usage = freshUsage({
|
|
@@ -1739,10 +1860,9 @@ function azureAdapter(config = {}) {
|
|
|
1739
1860
|
usage.costUsd = chars / 1e3 * (config.pricePer1kChars ?? getMediaPrice("azure", "tts")?.usd ?? 0);
|
|
1740
1861
|
return usage;
|
|
1741
1862
|
}
|
|
1742
|
-
|
|
1863
|
+
function buildSsml(req) {
|
|
1743
1864
|
const voice = resolveAzureVoice(req.voiceId);
|
|
1744
1865
|
const lang = req.lang ?? localeOf(voice);
|
|
1745
|
-
const format = req.format ?? DEFAULT_FORMAT;
|
|
1746
1866
|
assertPronunciations(req.pronunciations, "azure");
|
|
1747
1867
|
const escaped = applyPronunciations(
|
|
1748
1868
|
xmlEscape(req.text),
|
|
@@ -1752,7 +1872,11 @@ function azureAdapter(config = {}) {
|
|
|
1752
1872
|
);
|
|
1753
1873
|
const effRate = req.rate ?? AZURE_DANISH_VOICE_LIST.find((v) => v.voiceId === voice)?.defaultRate;
|
|
1754
1874
|
const inner = effRate != null && effRate !== 1 ? `<prosody rate='${effRate}'>${escaped}</prosody>` : escaped;
|
|
1755
|
-
|
|
1875
|
+
return `<speak version='1.0' xml:lang='${lang}'><voice name='${voice}'>${inner}</voice></speak>`;
|
|
1876
|
+
}
|
|
1877
|
+
async function tts(req) {
|
|
1878
|
+
if (req.wordTimings) return ttsBatch(req);
|
|
1879
|
+
const format = req.format ?? DEFAULT_FORMAT;
|
|
1756
1880
|
const res = await fetchImpl(
|
|
1757
1881
|
`https://${region()}.tts.speech.microsoft.com/cognitiveservices/v1`,
|
|
1758
1882
|
{
|
|
@@ -1762,7 +1886,7 @@ function azureAdapter(config = {}) {
|
|
|
1762
1886
|
"Content-Type": "application/ssml+xml",
|
|
1763
1887
|
"X-Microsoft-OutputFormat": format
|
|
1764
1888
|
},
|
|
1765
|
-
body:
|
|
1889
|
+
body: buildSsml(req)
|
|
1766
1890
|
}
|
|
1767
1891
|
);
|
|
1768
1892
|
if (!res.ok) {
|
|
@@ -1772,6 +1896,88 @@ function azureAdapter(config = {}) {
|
|
|
1772
1896
|
const audio = new Uint8Array(await res.arrayBuffer());
|
|
1773
1897
|
return { audio, mimeType: "audio/mpeg", usage: priceFor(req.text.length, req.spec.model) };
|
|
1774
1898
|
}
|
|
1899
|
+
async function ttsBatch(req) {
|
|
1900
|
+
const picked = sttHost();
|
|
1901
|
+
if (picked.source === "regional-fallback") {
|
|
1902
|
+
throw new Error(
|
|
1903
|
+
`azure batch synthesis (wordTimings) needs the resource's custom subdomain, not the regional host ${picked.url}. Set AZURE_SPEECH_RESOURCE (or config.resource) to your Speech resource name so the call goes to {resource}.cognitiveservices.azure.com, and make sure custom subdomain is enabled on that resource. Azure answers 401 here with a VALID key, and its message blames the key \u2014 measured by cms 11 September 2026.`
|
|
1904
|
+
);
|
|
1905
|
+
}
|
|
1906
|
+
const host = picked.url;
|
|
1907
|
+
const api = "api-version=2024-04-01";
|
|
1908
|
+
const id = `wt-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`;
|
|
1909
|
+
const headers = { "Ocp-Apim-Subscription-Key": key(), "Content-Type": "application/json" };
|
|
1910
|
+
const put = await fetchImpl(`${host}/texttospeech/batchsyntheses/${id}?${api}`, {
|
|
1911
|
+
method: "PUT",
|
|
1912
|
+
headers,
|
|
1913
|
+
body: JSON.stringify({
|
|
1914
|
+
inputKind: "SSML",
|
|
1915
|
+
inputs: [{ content: buildSsml(req) }],
|
|
1916
|
+
properties: {
|
|
1917
|
+
wordBoundaryEnabled: true,
|
|
1918
|
+
// ONE audio file and ONE word list for the whole text. Without it a chunked
|
|
1919
|
+
// input yields per-chunk offsets that somebody has to re-base — the kind of
|
|
1920
|
+
// arithmetic that looks right and is 40 ms wrong by the end.
|
|
1921
|
+
concatenateResult: true,
|
|
1922
|
+
...req.format ? { outputFormat: req.format } : {}
|
|
1923
|
+
}
|
|
1924
|
+
})
|
|
1925
|
+
});
|
|
1926
|
+
if (!put.ok) {
|
|
1927
|
+
const body = await put.text().catch(() => "");
|
|
1928
|
+
throw new Error(`azure batch synthesis submit ${put.status}: ${body.slice(0, 300)}`);
|
|
1929
|
+
}
|
|
1930
|
+
const deadline = Date.now() + (config.batchTimeoutMs ?? 18e4);
|
|
1931
|
+
let resultUrl = "";
|
|
1932
|
+
for (; ; ) {
|
|
1933
|
+
const get = await fetchImpl(`${host}/texttospeech/batchsyntheses/${id}?${api}`, {
|
|
1934
|
+
headers: { "Ocp-Apim-Subscription-Key": key() }
|
|
1935
|
+
});
|
|
1936
|
+
if (!get.ok) {
|
|
1937
|
+
const body = await get.text().catch(() => "");
|
|
1938
|
+
throw new Error(`azure batch synthesis poll ${get.status}: ${body.slice(0, 300)}`);
|
|
1939
|
+
}
|
|
1940
|
+
const job = await get.json();
|
|
1941
|
+
if (job.status === "Succeeded") {
|
|
1942
|
+
resultUrl = job.outputs?.result ?? "";
|
|
1943
|
+
break;
|
|
1944
|
+
}
|
|
1945
|
+
if (job.status === "Failed") throw new Error(`azure batch synthesis ${id} failed`);
|
|
1946
|
+
if (Date.now() > deadline) {
|
|
1947
|
+
throw new Error(
|
|
1948
|
+
`azure batch synthesis ${id} still "${job.status}" after ${Math.round((config.batchTimeoutMs ?? 18e4) / 1e3)}s`
|
|
1949
|
+
);
|
|
1950
|
+
}
|
|
1951
|
+
await new Promise((r) => setTimeout(r, config.batchPollMs ?? 3e3));
|
|
1952
|
+
}
|
|
1953
|
+
if (!resultUrl) throw new Error(`azure batch synthesis ${id} succeeded without a result URL`);
|
|
1954
|
+
const zipRes = await fetchImpl(resultUrl, { headers: { "Ocp-Apim-Subscription-Key": key() } });
|
|
1955
|
+
if (!zipRes.ok) {
|
|
1956
|
+
throw new Error(`azure batch synthesis results ${zipRes.status}`);
|
|
1957
|
+
}
|
|
1958
|
+
const zip = new Uint8Array(await zipRes.arrayBuffer());
|
|
1959
|
+
const audio = readZipEntry(zip, "0001.wav");
|
|
1960
|
+
if (!audio) {
|
|
1961
|
+
throw new Error(`azure batch synthesis: no 0001.wav in results; archive holds ${listZipEntries(zip).join(", ")}`);
|
|
1962
|
+
}
|
|
1963
|
+
const wordsRaw = readZipEntry(zip, "0001.word.json");
|
|
1964
|
+
if (!wordsRaw) {
|
|
1965
|
+
throw new Error(
|
|
1966
|
+
`azure batch synthesis: wordTimings was requested but the archive has no 0001.word.json; it holds ${listZipEntries(zip).join(", ")}`
|
|
1967
|
+
);
|
|
1968
|
+
}
|
|
1969
|
+
const boundaries = JSON.parse(new TextDecoder().decode(wordsRaw));
|
|
1970
|
+
return {
|
|
1971
|
+
audio,
|
|
1972
|
+
// Batch defaults to riff PCM, not mp3 — saying audio/mpeg here would be a lie the
|
|
1973
|
+
// browser would act on.
|
|
1974
|
+
mimeType: req.format?.includes("mp3") ? "audio/mpeg" : "audio/wav",
|
|
1975
|
+
// Aligned against the ORIGINAL text, with the dictionary, so the offsets index the
|
|
1976
|
+
// manuscript rather than the SSML we sent.
|
|
1977
|
+
wordTimings: alignWordTimings(req.text, boundaries, { pronunciations: req.pronunciations }),
|
|
1978
|
+
usage: priceFor(req.text.length, req.spec.model)
|
|
1979
|
+
};
|
|
1980
|
+
}
|
|
1775
1981
|
async function transcribe(req) {
|
|
1776
1982
|
const locale = toAzureLocale(req.language);
|
|
1777
1983
|
const definition = { locales: [locale] };
|
|
@@ -3085,6 +3291,8 @@ var ttsInputSchema = z.object({
|
|
|
3085
3291
|
/** F051 — see TtsRequest.pronunciations. The alias/ipa exclusivity is enforced in
|
|
3086
3292
|
* the adapter, not here: the message must name the provider that cannot do it. */
|
|
3087
3293
|
pronunciations: z.array(pronunciationSchema).optional(),
|
|
3294
|
+
/** F055 — per-word timings (Azure only). Changes the route to batch synthesis. */
|
|
3295
|
+
wordTimings: z.boolean().optional(),
|
|
3088
3296
|
/** F037: voice to use if `voice` is one we know the provider has retired. Without
|
|
3089
3297
|
* it a retired voice throws VoiceUnavailableError rather than reaching the API.
|
|
3090
3298
|
*
|
|
@@ -3126,8 +3334,8 @@ var aiConfigSchema = z.object({
|
|
|
3126
3334
|
});
|
|
3127
3335
|
|
|
3128
3336
|
// src/version.ts
|
|
3129
|
-
var VERSION = "0.
|
|
3130
|
-
var SDK_TAG = "@broberg/ai-sdk@0.
|
|
3337
|
+
var VERSION = "0.45.1";
|
|
3338
|
+
var SDK_TAG = "@broberg/ai-sdk@0.45.1";
|
|
3131
3339
|
|
|
3132
3340
|
// src/cost/sinks/upmetrics.ts
|
|
3133
3341
|
function upmetricsSink(config) {
|
|
@@ -3682,6 +3890,7 @@ function createAI(config = {}) {
|
|
|
3682
3890
|
format: input.format,
|
|
3683
3891
|
rate: input.rate,
|
|
3684
3892
|
pronunciations: input.pronunciations,
|
|
3893
|
+
wordTimings: input.wordTimings,
|
|
3685
3894
|
spec
|
|
3686
3895
|
});
|
|
3687
3896
|
}
|
|
@@ -4150,6 +4359,7 @@ export {
|
|
|
4150
4359
|
VERSION,
|
|
4151
4360
|
VoiceUnavailableError,
|
|
4152
4361
|
aiConfigSchema,
|
|
4362
|
+
alignWordTimings,
|
|
4153
4363
|
anthropicAdapter,
|
|
4154
4364
|
anthropicApiAdapter,
|
|
4155
4365
|
anthropicSubprocessAdapter,
|