@voltro/ai 0.57.0 → 0.59.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -23,6 +23,7 @@ import { SpeechModelV4 } from '@ai-sdk/provider';
23
23
  import { Stream } from 'effect';
24
24
  import { Table } from '@voltro/database';
25
25
  import { ToolSet } from 'ai';
26
+ import { TranscriptionModelV4 } from '@ai-sdk/provider';
26
27
 
27
28
  /** Count of in-flight streams (diagnostics / leak detection). */
28
29
  export declare const activeStreamCount: () => number;
@@ -372,7 +373,7 @@ export declare interface AiUsageRecordInput {
372
373
  readonly model: string;
373
374
  readonly operation: string;
374
375
  readonly agent?: string | null;
375
- readonly usage: AiUsage;
376
+ readonly usage: AiUsage | MeteredUsage;
376
377
  /** Override the price table (negotiated/volume pricing or unknown model). */
377
378
  readonly price?: ModelPrice;
378
379
  /** The ACTUAL per-call cost reported by the provider/gateway (USD). When set,
@@ -572,6 +573,9 @@ export declare const buildCopilotSystemPrompt: (schema: CopilotSchema) => string
572
573
  * `execute` tools ignore it. */
573
574
  export declare const buildSdkTools: (tools: Record<string, AnyTool>, ctx?: ToolContext) => ToolSet;
574
575
 
576
+ /** The names `generateSpeech` resolves without a registration. */
577
+ export declare const BUILTIN_SPEECH_PROVIDERS: ReadonlyArray<string>;
578
+
575
579
  /**
576
580
  * Cancel an in-flight stream by id. Aborts its controller (the SDK call stops +
577
581
  * the stream emits a terminal `cancelled` error). Returns `true` if a live
@@ -580,9 +584,33 @@ export declare const buildSdkTools: (tools: Record<string, AnyTool>, ctx?: ToolC
580
584
  */
581
585
  export declare const cancelStream: (streamId: string) => boolean;
582
586
 
587
+ /**
588
+ * Can this runtime run this model?
589
+ *
590
+ * The finer form, for the common case of filtering a catalog. It answers about
591
+ * the MODALITY — whether the framework has a primitive at all — and not about
592
+ * whether a particular model is healthy, in the caller's region, or affordable.
593
+ * Those are different questions with different answers and different owners.
594
+ */
595
+ export declare const canExecute: (model: Pick<GatewayModelInfo, "modality">) => boolean;
596
+
597
+ /**
598
+ * Union the three overlapping fields into one answer per capability.
599
+ *
600
+ * Each source is half-right on its own: `tags` is editorial and lags, and
601
+ * `supported_parameters` / `modalities` are mechanical and sometimes omit what
602
+ * the model plainly does. Taking either alone produces false negatives — and a
603
+ * false "cannot" is worse than a false "can" here, because it removes a model
604
+ * from a picker with no way for the user to discover why.
605
+ */
606
+ export declare const capabilitiesOf: (raw: RawGatewayModel) => ModelCapabilities;
607
+
583
608
  /** Clear the global middleware stack. */
584
609
  export declare const clearAiMiddleware: () => void;
585
610
 
611
+ /** Test seam: forget every registration. */
612
+ export declare const clearSpeechProviders: () => void;
613
+
586
614
  export declare interface ConsumeOptions {
587
615
  /** Replay events with `seq` strictly greater than this (default 0 = from the
588
616
  * start). A reconnecting client passes the last `seq` it rendered. */
@@ -603,6 +631,8 @@ export declare interface ConsumeOptions {
603
631
  */
604
632
  export declare const consumeResumableStream: (streamId: string, store: ResumableStreamStore, opts?: ConsumeOptions) => Stream.Stream<SeqEvent>;
605
633
 
634
+ export declare const contextWindowOf: (raw: RawGatewayModel) => number | undefined;
635
+
606
636
  /** Hard cap on rows — a copilot query never returns an unbounded set. */
607
637
  export declare const COPILOT_MAX_LIMIT = 200;
608
638
 
@@ -707,7 +737,9 @@ export declare interface CostBreakdown {
707
737
  * the ACTUAL per-call cost (authoritative); `estimated` = derived from the
708
738
  * static price table (or zero for an unknown model). Analytics can flag
709
739
  * estimated rows + a model that priced at zero. */
710
- readonly costSource: 'gateway' | 'estimated';
740
+ readonly costSource: 'gateway' | 'estimated' | 'unpriced';
741
+ /** What `inputTokens`/`outputTokens` are counts OF. */
742
+ readonly unit: UsageUnit;
711
743
  }
712
744
 
713
745
  /** Open a new thread (the store generates the id). */
@@ -727,6 +759,8 @@ export declare interface DataCopilotDeps {
727
759
  }) => Promise<CopilotProposal>;
728
760
  }
729
761
 
762
+ export declare const dataPolicyOf: (raw: RawGatewayModel) => ModelDataPolicy;
763
+
730
764
  /**
731
765
  * Durable, multi-node resumable-stream store backed by the framework's own
732
766
  * `DataStore` — works on every dialect the framework supports. The producer
@@ -989,7 +1023,7 @@ export declare const embedMany: (texts: ReadonlyArray<string>, config?: Embeddin
989
1023
  * unknown model (or the `mock` provider) resolves to zero cost rather
990
1024
  * than throwing, so cost accounting never breaks a call.
991
1025
  */
992
- export declare const estimateCostUsd: (usage: AiUsage, opts: {
1026
+ export declare const estimateCostUsd: (usage: AiUsage | MeteredUsage, opts: {
993
1027
  readonly model: string;
994
1028
  readonly price?: ModelPrice;
995
1029
  readonly actualCostUsd?: number;
@@ -1102,6 +1136,15 @@ export declare interface EvalReport {
1102
1136
  /** Run every assertion against one output; return only the failures. Pure. */
1103
1137
  export declare const evaluateAssertions: (out: EvalReplayOutput, assertions: ReadonlyArray<EvalAssertion>) => ReadonlyArray<AssertionFailure>;
1104
1138
 
1139
+ /**
1140
+ * The modalities this build can execute.
1141
+ *
1142
+ * A model whose modality is not in here must not be offered: selecting it
1143
+ * produces a control that cannot run. When a primitive ships, the set grows and
1144
+ * an app that asked this question needs no change — which is the point.
1145
+ */
1146
+ export declare const executableModalities: () => ReadonlySet<ModelModality>;
1147
+
1105
1148
  declare const EXECUTOR_BRAND: "@voltro/ai/AgentExecutor";
1106
1149
 
1107
1150
  /** The `exposeAsTool` annotation shape (mirrors @voltro/protocol's, structural
@@ -1124,6 +1167,9 @@ export { extractReasoningMiddleware }
1124
1167
  */
1125
1168
  export declare const flattenMcpContent: (result: Record<string, unknown>, maxBytes: number) => string;
1126
1169
 
1170
+ /** One raw entry → the shape callers read. Exported for the parser's tests. */
1171
+ export declare const fromRaw: (raw: RawGatewayModel) => GatewayModelInfo;
1172
+
1127
1173
  /**
1128
1174
  * Pull the ACTUAL per-call cost (USD) out of an AI-SDK result's provider
1129
1175
  * metadata, when the Vercel AI Gateway reported it. The gateway attaches
@@ -1155,6 +1201,49 @@ export declare interface GatewayModelInfo {
1155
1201
  readonly outputPer1M: number;
1156
1202
  readonly cachedInputPer1M?: number;
1157
1203
  };
1204
+ /** What the model can do, unioned from `tags`, `supported_parameters` and
1205
+ * `modalities` — the three fields that each half-say it and regularly
1206
+ * disagree. */
1207
+ readonly capabilities?: ModelCapabilities;
1208
+ /** What it accepts and produces, verbatim from the response. */
1209
+ readonly modalities?: {
1210
+ readonly input: ReadonlyArray<string>;
1211
+ readonly output: ReadonlyArray<string>;
1212
+ };
1213
+ readonly contextWindow?: number;
1214
+ readonly maxOutputTokens?: number;
1215
+ /** ISO 8601. The wire carries UNIX seconds; see `gatewayCatalog.ts` for why
1216
+ * that is converted here rather than passed along. */
1217
+ readonly releasedAt?: string;
1218
+ readonly knowledgeCutoff?: string;
1219
+ /**
1220
+ * Whether the provider retains or trains on what is sent.
1221
+ *
1222
+ * It decides whether a model may see customer text, and it was the clearest
1223
+ * casualty of reading the provider type: a field the framework does not pass
1224
+ * through is a decision nobody makes.
1225
+ */
1226
+ readonly dataPolicy?: ModelDataPolicy;
1227
+ /**
1228
+ * The entry exactly as it came over the wire.
1229
+ *
1230
+ * The normalised fields above are the shared answer to the questions every
1231
+ * app on the gateway asks. `raw` is for the ones only one app asks — a chips
1232
+ * row built from `tags`, a pricing display that shows the vendor's own
1233
+ * structure, `zdr` and `no_training` kept apart rather than folded into
1234
+ * `dataPolicy`.
1235
+ *
1236
+ * Without it, an app with its own display fetches `/v1/models` a SECOND time
1237
+ * and maintains a SECOND parser of the same response — and then has to decide
1238
+ * again, at every new field the gateway adds, whether to wait for us. That is
1239
+ * the decision this field removes, which is why it is one field and not three
1240
+ * more normalised ones.
1241
+ *
1242
+ * Absent on the injected-provider path for the same reason `capabilities` is:
1243
+ * that seam never saw a response, and inventing an empty object would turn
1244
+ * "no wire data" into the claim "the wire carried nothing".
1245
+ */
1246
+ readonly raw?: RawGatewayModel;
1158
1247
  }
1159
1248
 
1160
1249
  /**
@@ -1322,10 +1411,6 @@ export declare interface GenerateObjectWithToolsOptions<A, I> {
1322
1411
  readonly toolContext?: ToolContext;
1323
1412
  }
1324
1413
 
1325
- /**
1326
- * Synthesize speech audio from text. Returns the audio bytes + media type and
1327
- * provider metadata; provider failures surface as `AiError`.
1328
- */
1329
1414
  export declare const generateSpeech: (options: GenerateSpeechOptions) => Effect.Effect<GenerateSpeechResult, AiError>;
1330
1415
 
1331
1416
  export declare interface GenerateSpeechOptions {
@@ -1333,7 +1418,7 @@ export declare interface GenerateSpeechOptions {
1333
1418
  readonly text: string;
1334
1419
  /** Override the provider/model (defaults to `providerFromEnv()`). `anthropic`
1335
1420
  * has no speech model; use `openai` or `gateway`. */
1336
- readonly provider?: ProviderConfig;
1421
+ readonly provider?: ProviderConfig | RegisteredSpeechConfig;
1337
1422
  /** Provider voice id. */
1338
1423
  readonly voice?: string;
1339
1424
  /** Desired audio container, e.g. `'mp3'` / `'wav'`. */
@@ -1350,8 +1435,21 @@ export declare interface GenerateSpeechOptions {
1350
1435
 
1351
1436
  export declare interface GenerateSpeechResult {
1352
1437
  readonly audio: GeneratedAudio;
1353
- /** The speech model spec reports no usage; present for surface parity. */
1354
- readonly usage?: MediaUsage;
1438
+ /**
1439
+ * What the call consumed.
1440
+ *
1441
+ * The speech model SPEC reports none, so a gateway-routed call still leaves
1442
+ * this undefined. A REGISTERED provider is the other case, and it is not a
1443
+ * property of the model any more: an app's own adapter knows the billed
1444
+ * quantity, because its vendor invoices on it — characters for ElevenLabs,
1445
+ * Cartesia and OpenAI, seconds for others.
1446
+ *
1447
+ * So it takes a `MeteredUsage` too, and that is what makes speech spend
1448
+ * reachable: without a shape that can say "4200 characters", the number the
1449
+ * adapter already has has nowhere to go, and `recordAiUsage` never sees the
1450
+ * call at all. Not un-priced — absent from the ledger.
1451
+ */
1452
+ readonly usage?: MediaUsage | MeteredUsage;
1355
1453
  readonly providerMetadata?: unknown;
1356
1454
  }
1357
1455
 
@@ -1517,6 +1615,18 @@ export declare interface HttpMcpTransportOptions {
1517
1615
 
1518
1616
  export declare interface InferenceDispatchDeps {
1519
1617
  readonly store: DataStore;
1618
+ /**
1619
+ * Resolve a transcription `storageRef` to bytes.
1620
+ *
1621
+ * Injected here for the same reason `transcribe` injects it: this package must
1622
+ * not learn about buckets, and the read carries the app's tenancy and signing.
1623
+ * Required only when an offloaded transcription used a ref — a queued call
1624
+ * with a `url` or inline bytes needs nothing.
1625
+ */
1626
+ readonly resolveTranscriptionRef?: (ref: string) => Promise<{
1627
+ bytes: Uint8Array;
1628
+ mediaType: string;
1629
+ }>;
1520
1630
  /**
1521
1631
  * Resume the parked run with the call's outcome.
1522
1632
  *
@@ -1604,6 +1714,8 @@ export declare const isEvalDefinition: (value: unknown) => value is EvalDefiniti
1604
1714
  */
1605
1715
  export declare const isExternalWrite: (tool: McpToolInfo, namespace: string, policy: McpToolPolicy) => boolean;
1606
1716
 
1717
+ export declare const isMeteredUsage: (u: AiUsage | MeteredUsage) => u is MeteredUsage;
1718
+
1607
1719
  /** Is this a rendered prompt or a bare string? The discriminator `aiStep` uses
1608
1720
  * so `prompt:` can accept either without a wrapper type at every call site. */
1609
1721
  export declare const isRenderedPrompt: (value: unknown) => value is RenderedPrompt;
@@ -1623,6 +1735,8 @@ export declare interface JudgeVerdict {
1623
1735
  readonly reason: string;
1624
1736
  }
1625
1737
 
1738
+ export declare const knowledgeCutoffOf: (raw: RawGatewayModel) => string | undefined;
1739
+
1626
1740
  /**
1627
1741
  * The real LLM judge: asks the model for a structured `{ pass, score, reason }`
1628
1742
  * verdict via `generateObject` (schema-constrained, so a malformed grade surfaces
@@ -1656,6 +1770,8 @@ export declare const loggingMiddleware: (opts?: {
1656
1770
  */
1657
1771
  export declare const makeMcpTool: (server: McpServer, tool: McpToolInfo, policy: McpToolPolicy, bounds: McpBounds) => AnyTool;
1658
1772
 
1773
+ export declare const maxOutputTokensOf: (raw: RawGatewayModel) => number | undefined;
1774
+
1659
1775
  export declare const MCP_BOUNDS_DEFAULTS: McpBounds;
1660
1776
 
1661
1777
  /** Identity this client reports in `initialize`. No version literal here — the
@@ -1971,6 +2087,23 @@ export declare type MessagePart = {
1971
2087
  readonly data: string;
1972
2088
  };
1973
2089
 
2090
+ /**
2091
+ * A tally in a unit that is not tokens.
2092
+ *
2093
+ * Deliberately NOT `AiUsage` with a unit tacked on. Writing a character count
2094
+ * into a field called `inputTokens` puts a lie in a ledger row that someone
2095
+ * reads later, and a dashboard summing that column across units is wrong in a
2096
+ * way nothing reports. The token shape is left exactly as it was — it is what
2097
+ * the AI SDK returns, and text callers change nothing.
2098
+ */
2099
+ export declare interface MeteredUsage {
2100
+ readonly unit: Exclude<UsageUnit, 'tokens'>;
2101
+ /** What went in — characters of text, say. */
2102
+ readonly input?: number | undefined;
2103
+ /** What came out — seconds of audio, say. */
2104
+ readonly output?: number | undefined;
2105
+ }
2106
+
1974
2107
  /** A scripted inline file the mock model emits on a turn (usually an image).
1975
2108
  * `data` is base64 (a short ascii string works for assertions). */
1976
2109
  export declare interface MockFile {
@@ -2027,6 +2160,20 @@ export declare interface MockTurn {
2027
2160
  };
2028
2161
  }
2029
2162
 
2163
+ export declare const modalitiesOf: (raw: RawGatewayModel) => {
2164
+ readonly input: ReadonlyArray<string>;
2165
+ readonly output: ReadonlyArray<string>;
2166
+ };
2167
+
2168
+ /**
2169
+ * The primitive that makes each modality runnable — the reason the entry is
2170
+ * here, written down so a reader can check it rather than trust it.
2171
+ */
2172
+ export declare const MODALITY_PRIMITIVE: Readonly<Partial<Record<ModelModality, string>>>;
2173
+
2174
+ /** `model_type` on the wire; the same union `ModelModality` names. */
2175
+ export declare const modalityOf: (raw: RawGatewayModel) => ModelModality;
2176
+
2030
2177
  /**
2031
2178
  * Built-in published list prices (USD per 1M tokens) for the major providers.
2032
2179
  *
@@ -2051,6 +2198,29 @@ export declare interface MockTurn {
2051
2198
  */
2052
2199
  export declare const MODEL_PRICING_DEFAULTS: Record<string, ModelPrice>;
2053
2200
 
2201
+ /** What a model can do, unioned from the three fields that each half-say it. */
2202
+ export declare interface ModelCapabilities {
2203
+ readonly vision: boolean;
2204
+ readonly reasoning: boolean;
2205
+ readonly toolUse: boolean;
2206
+ readonly structuredOutput: boolean;
2207
+ readonly webSearch: boolean;
2208
+ readonly caching: boolean;
2209
+ readonly imageOut: boolean;
2210
+ readonly audioOut: boolean;
2211
+ readonly videoOut: boolean;
2212
+ readonly audioIn: boolean;
2213
+ readonly videoIn: boolean;
2214
+ readonly fileInput: boolean;
2215
+ }
2216
+
2217
+ export declare interface ModelDataPolicy {
2218
+ /** Zero data retention. */
2219
+ readonly zeroRetention: PolicyCoverage;
2220
+ /** The provider does not train on what is sent. */
2221
+ readonly noTraining: PolicyCoverage;
2222
+ }
2223
+
2054
2224
  export declare type ModelModality = 'language' | 'embedding' | 'image' | 'video' | 'realtime' | 'reranking' | 'speech' | 'transcription' | 'unknown';
2055
2225
 
2056
2226
  export declare interface ModelPrice {
@@ -2063,6 +2233,13 @@ export declare interface ModelPrice {
2063
2233
  /** The current merged price map (defaults + installed overrides). Overrides win. */
2064
2234
  export declare const modelPricing: () => Record<string, ModelPrice>;
2065
2235
 
2236
+ /** Both shapes, flattened to the two counts + the unit they are counts of. */
2237
+ export declare const normaliseUsage: (usage: AiUsage | MeteredUsage) => {
2238
+ readonly unit: UsageUnit;
2239
+ readonly input: number;
2240
+ readonly output: number;
2241
+ };
2242
+
2066
2243
  /**
2067
2244
  * Wrap a `streamText` `Stream<AgentEvent>` with the same `voltro.ai.call` span +
2068
2245
  * metrics. The stream never fails — usage arrives on the terminal `done` event,
@@ -2113,6 +2290,9 @@ export declare const passesPolicy: (tag: string, policy: AppToolPolicy) => boole
2113
2290
  */
2114
2291
  export declare const patchStreamingMessage: (store: DataStore, id: string, parts: ReadonlyArray<MessagePart>) => Effect.Effect<void>;
2115
2292
 
2293
+ /** How much of a provider's retention promise applies. */
2294
+ export declare type PolicyCoverage = 'all' | 'some' | 'none';
2295
+
2116
2296
  /**
2117
2297
  * The effective {@link ModelPrice} for a model id, or `undefined` if unknown.
2118
2298
  * Looks up the merged map (overrides over defaults); a gateway `creator/model`
@@ -2275,6 +2455,9 @@ export declare type ProviderConfig = {
2275
2455
  /** base64 the mock video model yields INSTEAD of a URL — exercises the
2276
2456
  * bytes branch of `generateVideo`. Takes precedence over `videoUrl`. */
2277
2457
  readonly videoBase64?: string;
2458
+ /** The text the mock transcription model returns. Lets a test assert on
2459
+ * an exact transcript without a provider. */
2460
+ readonly transcript?: string;
2278
2461
  };
2279
2462
  } | {
2280
2463
  readonly name: 'anthropic';
@@ -2340,7 +2523,7 @@ export declare interface QueuedInference {
2340
2523
  readonly runId: string | null;
2341
2524
  readonly signalName: string;
2342
2525
  readonly stepName: string;
2343
- readonly kind: 'text' | 'object';
2526
+ readonly kind: 'text' | 'object' | 'transcription';
2344
2527
  readonly provider: string;
2345
2528
  readonly model: string;
2346
2529
  readonly prompt: string;
@@ -2352,9 +2535,52 @@ export declare interface QueuedInference {
2352
2535
  readonly maxTokens: number | null;
2353
2536
  readonly providerOptions: unknown;
2354
2537
  readonly jsonSchema: Record<string, unknown> | null;
2538
+ /** The source + options for a `transcription` call. */
2539
+ readonly transcription: Record<string, unknown> | null;
2355
2540
  readonly attempts: number;
2356
2541
  }
2357
2542
 
2543
+ /** One entry of the raw `/v1/models` response, every field optional — the
2544
+ * gateway adds fields, and a parser that requires one is a parser that breaks
2545
+ * on a Tuesday. */
2546
+ export declare interface RawGatewayModel {
2547
+ readonly id?: unknown;
2548
+ readonly name?: unknown;
2549
+ readonly description?: unknown;
2550
+ readonly model_type?: unknown;
2551
+ readonly tags?: unknown;
2552
+ readonly supported_parameters?: unknown;
2553
+ readonly modalities?: unknown;
2554
+ readonly context_window?: unknown;
2555
+ readonly max_tokens?: unknown;
2556
+ readonly released?: unknown;
2557
+ readonly knowledge?: unknown;
2558
+ readonly zdr?: unknown;
2559
+ readonly no_training?: unknown;
2560
+ readonly pricing?: unknown;
2561
+ }
2562
+
2563
+ /**
2564
+ * The catalog, read from `/v1/models` directly.
2565
+ *
2566
+ * `getAvailableModels` asks `@ai-sdk/gateway`'s provider, whose entry type
2567
+ * carries six of the response's twenty-one fields — so the other fifteen never
2568
+ * arrive, and an app that needs them fetches the endpoint a second time and
2569
+ * writes its own parser. This reads the response.
2570
+ *
2571
+ * The provider path is kept and still used when a `gatewayProvider` is injected
2572
+ * (tests) or a fetch fails: a catalog with fewer fields is worth more than no
2573
+ * catalog, and the fields that were always there stay there. A degraded read is
2574
+ * VISIBLE rather than silent — the entries simply carry no `capabilities`, which
2575
+ * is the difference between "not known" and "no".
2576
+ */
2577
+ export declare const readGatewayCatalog: (opts?: {
2578
+ readonly apiKey?: string;
2579
+ readonly baseURL?: string;
2580
+ readonly modality?: ModelModality;
2581
+ readonly fetchImpl?: typeof fetch;
2582
+ }) => Promise<ReadonlyArray<GatewayModelInfo>>;
2583
+
2358
2584
  /** A store wrapper that records the dependency set of everything read through
2359
2585
  * it — the provenance the semantic cache tags entries with. */
2360
2586
  export declare interface ReadRecorder<S extends QueryableStore> {
@@ -2431,6 +2657,58 @@ export declare const redisResumableStreamStore: (redis: ResumableRedis, opts?: {
2431
2657
  */
2432
2658
  export declare const refreshMcpToolset: (server: McpServer, policy: McpToolPolicy) => Effect.Effect<McpToolset, McpError>;
2433
2659
 
2660
+ /**
2661
+ * Synthesize speech audio from text. Returns the audio bytes + media type and
2662
+ * provider metadata; provider failures surface as `AiError`.
2663
+ */
2664
+ /**
2665
+ * A provider registered with `registerSpeechProvider` — named, not enumerated.
2666
+ *
2667
+ * The gateway serves nine speech models; a real voice catalog runs to hundreds
2668
+ * behind vendors it does not carry (Cartesia, ElevenLabs, Google Cloud TTS). An
2669
+ * app with its own adapter had nowhere to plug it in, so it called its own
2670
+ * synthesizer and skipped this primitive entirely — losing the shared error type
2671
+ * and the shared cost path with it.
2672
+ */
2673
+ export declare interface RegisteredSpeechConfig {
2674
+ readonly name: string;
2675
+ readonly model?: string;
2676
+ readonly voice?: string;
2677
+ }
2678
+
2679
+ /** Every registered name — what a picker asks for. */
2680
+ export declare const registeredSpeechProviders: () => ReadonlyArray<string>;
2681
+
2682
+ /**
2683
+ * Register a speech provider under a name.
2684
+ *
2685
+ * The name then works in `generateSpeech({ provider: { name, model, voice } })`.
2686
+ * Taking over a built-in name needs `{ replaceBuiltin: true }` — see the option.
2687
+ */
2688
+ export declare const registerSpeechProvider: (name: string, provider: SpeechProvider, options?: RegisterSpeechProviderOptions) => void;
2689
+
2690
+ export declare interface RegisterSpeechProviderOptions {
2691
+ /**
2692
+ * Take over a BUILT-IN name (`openai`, `gateway`, `anthropic`, `mock`).
2693
+ *
2694
+ * Refusing these outright was wrong, and the case that showed it is one the
2695
+ * refusal could not see: a provider name is not always an alias the app
2696
+ * chooses. `openai` is the value the GATEWAY puts in its model ids, so it is
2697
+ * what an app's stored catalog rows carry and what its picker submits. Told
2698
+ * to pick another name, such an app cannot — it would have to rewrite
2699
+ * persisted rows and live sessions — so it keeps its own dispatch instead,
2700
+ * and the primitive stays unused for every provider, not just the colliding
2701
+ * one.
2702
+ *
2703
+ * What the refusal was actually protecting is worth keeping: a SILENT shadow,
2704
+ * where the same config means different things depending on which module
2705
+ * loaded first. This flag keeps that property by putting the intent in the
2706
+ * call. An override is then a decision someone wrote down, not an accident of
2707
+ * import order.
2708
+ */
2709
+ readonly replaceBuiltin?: boolean;
2710
+ }
2711
+
2434
2712
  /**
2435
2713
  * Register (or reuse) the `AbortController` for `streamId`. `streamText` calls
2436
2714
  * this when given a `streamId`; it threads the returned controller's `signal`
@@ -2440,6 +2718,8 @@ export declare const refreshMcpToolset: (server: McpServer, policy: McpToolPolic
2440
2718
  */
2441
2719
  export declare const registerStream: (streamId: string) => AbortController;
2442
2720
 
2721
+ export declare const releasedAtOf: (raw: RawGatewayModel) => string | undefined;
2722
+
2443
2723
  /** What `.render(vars)` produces — a stamp plus the text to send. */
2444
2724
  export declare interface RenderedPrompt extends PromptStamp {
2445
2725
  readonly prompt: string;
@@ -2452,6 +2732,9 @@ export declare interface RenderedPrompt extends PromptStamp {
2452
2732
  readonly definition: PromptDefinition;
2453
2733
  }
2454
2734
 
2735
+ /** Whether `name` is a registration standing in front of a built-in. */
2736
+ export declare const replacesBuiltinSpeechProvider: (name: string) => boolean;
2737
+
2455
2738
  /**
2456
2739
  * Gate an AI call against a per-tenant USD budget with an ATOMIC reservation —
2457
2740
  * the cap precedent is billing's `requireEntitlement` / storage's
@@ -2502,8 +2785,21 @@ export declare interface RerankOptions<T> {
2502
2785
  readonly getText?: (doc: T) => string;
2503
2786
  /** Rerank model id. e.g. 'rerank-english-v3.0' (cohere) / 'rerank-2' (voyage). */
2504
2787
  readonly model?: string;
2505
- /** Provider. Default 'mock' (lexical overlap; deterministic). */
2506
- readonly provider?: 'mock' | 'cohere' | 'voyage';
2788
+ /**
2789
+ * Where to rerank. Default `'mock'` (lexical overlap; deterministic).
2790
+ *
2791
+ * A `ProviderConfig` — the shape every other primitive in this package takes
2792
+ * — routes through the gateway, so one `AI_GATEWAY_API_KEY` reaches
2793
+ * `cohere/rerank-v4-pro`, `voyage/rerank-2.5` and the rest of the catalog.
2794
+ * This was the one primitive that could not do that: it wanted the vendors'
2795
+ * own keys and had no way to express the gateway at all.
2796
+ *
2797
+ * The string shorthands stay for the direct-key route. They are NOT folded
2798
+ * into `ProviderConfig`: that union is shared with `generateText` and friends,
2799
+ * and adding `cohere` there would let somebody name a provider that has no
2800
+ * language model — a config error the type currently catches.
2801
+ */
2802
+ readonly provider?: 'mock' | 'cohere' | 'voyage' | ProviderConfig;
2507
2803
  /** Return only the top-N after reranking. Default: all. */
2508
2804
  readonly topN?: number;
2509
2805
  }
@@ -2557,6 +2853,15 @@ export declare const resolveSystem: (exec: AnyAgentExecutor, input: unknown) =>
2557
2853
  */
2558
2854
  export declare const resolveToolModuleHandlers: (mod: Record<string, unknown>) => number;
2559
2855
 
2856
+ /**
2857
+ * The transcription model for a provider config.
2858
+ *
2859
+ * Mirrors `resolveSpeechModel` deliberately — the two are the same seam in
2860
+ * opposite directions, and the gateway exposes both (`.transcription(id)`), so
2861
+ * one key reaches all nine transcription models in the catalog.
2862
+ */
2863
+ export declare const resolveTranscriptionModel: (config: ProviderConfig) => TranscriptionModelV4;
2864
+
2560
2865
  export declare const resolveVideoModel: (config: ProviderConfig) => Experimental_VideoModelV4;
2561
2866
 
2562
2867
  /**
@@ -2638,6 +2943,43 @@ export declare const resumableStreamText: (args: {
2638
2943
  readonly idleTimeoutMs?: number;
2639
2944
  }) => Stream.Stream<SeqEvent>;
2640
2945
 
2946
+ /**
2947
+ * Search, rerank, truncate — with the two numbers named.
2948
+ *
2949
+ * ```ts
2950
+ * const docs = yield* retrieveReranked({
2951
+ * search: (limit) => Effect.promise(() => searchDocs(q, limit)),
2952
+ * query: q,
2953
+ * getText: (d) => d.body,
2954
+ * provider: { name: 'gateway', model: 'cohere/rerank-v4-pro' },
2955
+ * })
2956
+ * ```
2957
+ *
2958
+ * Returns the documents, not `{ document, score }` — the score is the second
2959
+ * stage's working, and a caller that wants it calls `rerank` directly. Handing
2960
+ * back a wrapper would make every consumer unwrap it.
2961
+ */
2962
+ export declare const retrieveReranked: <T, E>(options: RetrieveRerankedOptions<T, E>) => Effect.Effect<ReadonlyArray<T>, E | AiError>;
2963
+
2964
+ export declare interface RetrieveRerankedOptions<T, E> {
2965
+ /**
2966
+ * The app's own first stage, given a limit.
2967
+ *
2968
+ * A function rather than a value, so the helper decides how DEEP to go: the
2969
+ * candidate count is the parameter being traded against accuracy, and a
2970
+ * caller that had already run the search would have chosen it.
2971
+ */
2972
+ readonly search: (limit: number) => Effect.Effect<ReadonlyArray<T>, E>;
2973
+ readonly query: string;
2974
+ readonly getText: (doc: T) => string;
2975
+ /** How many the first stage fetches. Default 50 — cheap and shallow. */
2976
+ readonly candidates?: number;
2977
+ /** How many survive the rerank. Default 8. */
2978
+ readonly topN?: number;
2979
+ readonly provider?: RerankOptions<T>['provider'];
2980
+ readonly model?: string;
2981
+ }
2982
+
2641
2983
  /**
2642
2984
  * The synthesized `<name>.send` body. Appends the user turn at `order`, then
2643
2985
  * drives a streaming assistant turn at `order + 1` via `runAssistant` (which
@@ -2912,6 +3254,29 @@ export declare const setProviderOverride: (config: ProviderConfig | null) => Pro
2912
3254
 
2913
3255
  export { simulateStreamingMiddleware }
2914
3256
 
3257
+ export declare interface SpeechProvider {
3258
+ /** The voices this provider offers, for a picker. Optional: a provider with a
3259
+ * fixed voice set the app already knows needs no listing. */
3260
+ readonly listVoices?: () => Effect.Effect<ReadonlyArray<VoiceInfo>, AiError>;
3261
+ readonly synthesize: (request: SpeechSynthesisRequest) => Effect.Effect<GenerateSpeechResult, AiError>;
3262
+ }
3263
+
3264
+ /** The provider registered under `name`, or `undefined`. */
3265
+ export declare const speechProvider: (name: string) => SpeechProvider | undefined;
3266
+
3267
+ /** What `generateSpeech` hands a registered provider. Mirrors its own options
3268
+ * minus the parts the framework resolves. */
3269
+ export declare interface SpeechSynthesisRequest {
3270
+ readonly text: string;
3271
+ readonly model?: string;
3272
+ readonly voice?: string;
3273
+ readonly outputFormat?: string;
3274
+ readonly instructions?: string;
3275
+ readonly speed?: number;
3276
+ readonly language?: string;
3277
+ readonly providerOptions?: AiProviderOptions;
3278
+ }
3279
+
2915
3280
  /**
2916
3281
  * Run the underlying stream ONCE and persist every event. Assigns a monotonic
2917
3282
  * `seq`, appends each event, and marks the stream done at the end — even if a
@@ -3148,12 +3513,115 @@ export declare type ToolHandler<A, O> = (input: A, ctx: ToolContext) => Effect.E
3148
3513
 
3149
3514
  export declare const toQueuedInference: (row: Record<string, unknown>) => QueuedInference;
3150
3515
 
3516
+ /**
3517
+ * Transcribe audio to text.
3518
+ *
3519
+ * ```ts
3520
+ * const { text, segments } = yield* transcribe({
3521
+ * source: { storageRef: recording.ref },
3522
+ * resolveRef: (ref) => ctx.storage.read(ref),
3523
+ * timestamps: 'segment',
3524
+ * })
3525
+ * ```
3526
+ */
3527
+ export declare const transcribe: (options: TranscribeOptions) => Effect.Effect<TranscriptionResult, AiError>;
3528
+
3529
+ export declare interface TranscribeOptions {
3530
+ readonly source: TranscriptionSource;
3531
+ /** Override the provider/model. `anthropic` has no transcription model. */
3532
+ readonly provider?: ProviderConfig;
3533
+ /** ISO 639-1. Given rather than detected — detection costs a pass and is
3534
+ * wrong on short or accented audio. */
3535
+ readonly language?: string;
3536
+ /** Vocabulary, proper nouns, spellings the model would otherwise guess. */
3537
+ readonly prompt?: string;
3538
+ readonly timestamps?: TranscriptionTimestamps;
3539
+ /** Separate speakers. Not every provider can; one that cannot returns
3540
+ * segments without `speaker` rather than failing. */
3541
+ readonly diarize?: boolean;
3542
+ readonly translateToEnglish?: boolean;
3543
+ /**
3544
+ * Resolve a `storageRef` to bytes.
3545
+ *
3546
+ * Injected, and required only when the source IS a ref — so an app that
3547
+ * passes a url or bytes wires nothing, and `@voltro/ai` never imports storage.
3548
+ */
3549
+ readonly resolveRef?: (ref: string) => Promise<{
3550
+ bytes: Uint8Array;
3551
+ mediaType: string;
3552
+ }>;
3553
+ readonly providerOptions?: AiProviderOptions;
3554
+ }
3555
+
3556
+ export declare interface TranscriptionResult {
3557
+ readonly text: string;
3558
+ readonly language?: string;
3559
+ readonly durationSec?: number;
3560
+ readonly segments?: ReadonlyArray<TranscriptionSegment>;
3561
+ /**
3562
+ * Usage, for the SAME ledger every other primitive reports to.
3563
+ *
3564
+ * Transcription is the one modality billed by DURATION rather than by tokens,
3565
+ * which is exactly why it must not be the one modality missing from the
3566
+ * budget: a spend limit that silently excludes the long-running thing is not
3567
+ * a spend limit.
3568
+ */
3569
+ readonly usage?: MediaUsage;
3570
+ readonly providerMetadata?: unknown;
3571
+ }
3572
+
3573
+ export declare interface TranscriptionSegment {
3574
+ readonly start: number;
3575
+ readonly end: number;
3576
+ readonly text: string;
3577
+ /** Present only when `diarize` was asked for AND the provider supports it. */
3578
+ readonly speaker?: string;
3579
+ }
3580
+
3581
+ /**
3582
+ * Where the audio is.
3583
+ *
3584
+ * `storageRef` is the one to reach for: the file is already in the app's
3585
+ * storage, and resolving it there keeps tenancy, signing and lifetime with the
3586
+ * component that owns them. The resolver is injected rather than imported so
3587
+ * this module stays free of a storage dependency — `@voltro/ai` must not learn
3588
+ * about buckets to transcribe a file.
3589
+ */
3590
+ export declare type TranscriptionSource =
3591
+ /** Resolved by the app's own storage — the case to reach for. */
3592
+ {
3593
+ readonly storageRef: string;
3594
+ }
3595
+ /** Fetched by this function. Convenient, but the bytes do pass through this
3596
+ * process: the SDK takes bytes or base64 and never a location. */
3597
+ | {
3598
+ readonly url: string;
3599
+ }
3600
+ /** Already in hand. The one that costs memory by construction. */
3601
+ | {
3602
+ readonly bytes: Uint8Array;
3603
+ readonly mediaType: string;
3604
+ };
3605
+
3606
+ /** How much timestamp detail to ask for. Providers charge for it. */
3607
+ export declare type TranscriptionTimestamps = 'none' | 'segment' | 'word';
3608
+
3151
3609
  /**
3152
3610
  * Drop a stream's controller. `streamText` calls this in its finaliser when the
3153
3611
  * stream ends — success, error, cancel, or consumer interrupt. Idempotent.
3154
3612
  */
3155
3613
  export declare const unregisterStream: (streamId: string) => void;
3156
3614
 
3615
+ /**
3616
+ * What a call was billed in.
3617
+ *
3618
+ * Text is tokens. Speech is not: every speech vendor meters CHARACTERS of input
3619
+ * or SECONDS of audio, and none of them meters tokens. A ledger that only knows
3620
+ * tokens therefore cannot hold a speech call at all — which is why speech spend
3621
+ * was invisible rather than merely un-priced.
3622
+ */
3623
+ export declare type UsageUnit = 'tokens' | 'characters' | 'seconds';
3624
+
3157
3625
  /**
3158
3626
  * Validate a proposal against the schema and (on success) build a read-only
3159
3627
  * `QueryDescriptor`. EVERY table/column the proposal names must exist in the
@@ -3162,6 +3630,17 @@ export declare const unregisterStream: (streamId: string) => void;
3162
3630
  */
3163
3631
  export declare const validateCopilotProposal: (proposal: CopilotProposal, schema: CopilotSchema) => CopilotValidation;
3164
3632
 
3633
+ /** One voice a registered provider offers. */
3634
+ export declare interface VoiceInfo {
3635
+ readonly id: string;
3636
+ readonly name: string;
3637
+ readonly language?: string;
3638
+ /** Free-form, provider-shaped: gender, accent, age, whatever it publishes.
3639
+ * Not normalised — every vendor's taxonomy is different, and flattening them
3640
+ * would lose the half a picker actually shows. */
3641
+ readonly labels?: Readonly<Record<string, string>>;
3642
+ }
3643
+
3165
3644
  /**
3166
3645
  * Wrap an `Effect`-returning generate call with a `voltro.ai.call` span +
3167
3646
  * per-call metrics (count, error count, duration histogram, token + cost