@voltro/ai 0.56.0 → 0.58.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;
@@ -580,9 +581,33 @@ export declare const buildSdkTools: (tools: Record<string, AnyTool>, ctx?: ToolC
580
581
  */
581
582
  export declare const cancelStream: (streamId: string) => boolean;
582
583
 
584
+ /**
585
+ * Can this runtime run this model?
586
+ *
587
+ * The finer form, for the common case of filtering a catalog. It answers about
588
+ * the MODALITY — whether the framework has a primitive at all — and not about
589
+ * whether a particular model is healthy, in the caller's region, or affordable.
590
+ * Those are different questions with different answers and different owners.
591
+ */
592
+ export declare const canExecute: (model: Pick<GatewayModelInfo, "modality">) => boolean;
593
+
594
+ /**
595
+ * Union the three overlapping fields into one answer per capability.
596
+ *
597
+ * Each source is half-right on its own: `tags` is editorial and lags, and
598
+ * `supported_parameters` / `modalities` are mechanical and sometimes omit what
599
+ * the model plainly does. Taking either alone produces false negatives — and a
600
+ * false "cannot" is worse than a false "can" here, because it removes a model
601
+ * from a picker with no way for the user to discover why.
602
+ */
603
+ export declare const capabilitiesOf: (raw: RawGatewayModel) => ModelCapabilities;
604
+
583
605
  /** Clear the global middleware stack. */
584
606
  export declare const clearAiMiddleware: () => void;
585
607
 
608
+ /** Test seam: forget every registration. */
609
+ export declare const clearSpeechProviders: () => void;
610
+
586
611
  export declare interface ConsumeOptions {
587
612
  /** Replay events with `seq` strictly greater than this (default 0 = from the
588
613
  * start). A reconnecting client passes the last `seq` it rendered. */
@@ -603,6 +628,8 @@ export declare interface ConsumeOptions {
603
628
  */
604
629
  export declare const consumeResumableStream: (streamId: string, store: ResumableStreamStore, opts?: ConsumeOptions) => Stream.Stream<SeqEvent>;
605
630
 
631
+ export declare const contextWindowOf: (raw: RawGatewayModel) => number | undefined;
632
+
606
633
  /** Hard cap on rows — a copilot query never returns an unbounded set. */
607
634
  export declare const COPILOT_MAX_LIMIT = 200;
608
635
 
@@ -727,6 +754,8 @@ export declare interface DataCopilotDeps {
727
754
  }) => Promise<CopilotProposal>;
728
755
  }
729
756
 
757
+ export declare const dataPolicyOf: (raw: RawGatewayModel) => ModelDataPolicy;
758
+
730
759
  /**
731
760
  * Durable, multi-node resumable-stream store backed by the framework's own
732
761
  * `DataStore` — works on every dialect the framework supports. The producer
@@ -734,11 +763,24 @@ export declare interface DataCopilotDeps {
734
763
  * `streamId`, comparing the returned row's id to ours: we inserted ⇒ we won.
735
764
  * That is the same at-most-once arbiter `_voltro_idempotency` uses.
736
765
  *
737
- * The two tables (`streamEventsTable`, `streamStateTable`) register into the
738
- * app's migration set like the agent/usage tables — add them to the database
739
- * barrel (or they ride along with a `*.agent.tsx` app). Building this store
740
- * BOUNDS them (see `registerStreamLogRetention`); `gcResumableStreams` remains
741
- * for a tighter, done-aware collection an app can run on its own schedule.
766
+ * The two tables (`streamEventsTable`, `streamStateTable`) are created for you:
767
+ * the framework's table assembly adds them for any app that DEPENDS on
768
+ * `@voltro/ai`, so `voltro dev` / `voltro db apply` / `voltro migrate` all plan
769
+ * them. Nothing to add anywhere.
770
+ *
771
+ * This sentence used to say the opposite — "add them to the database barrel (or
772
+ * they ride along with a `*.agent.tsx` app)" — and both halves were wrong.
773
+ * Discovery is FILE-based (`*.entity.ts`), so a re-export from a barrel changes
774
+ * no plan; and this store is an ordinary call reachable from an action, so an
775
+ * agent file is not the gate either. The tables were simply missing from the
776
+ * assembly. What that cost is worth stating, because it is the failure shape of
777
+ * every "the app adds it" contract: `voltro db plan` answered *schema is up to
778
+ * date* while two tables the feature cannot run without were absent, and the
779
+ * first user's turn died inside the store instead of at boot.
780
+ *
781
+ * Building this store BOUNDS them (see `registerStreamLogRetention`);
782
+ * `gcResumableStreams` remains for a tighter, done-aware collection an app can
783
+ * run on its own schedule.
742
784
  */
743
785
  export declare const dataStoreResumableStreamStore: (store: DataStore) => ResumableStreamStore;
744
786
 
@@ -1089,6 +1131,15 @@ export declare interface EvalReport {
1089
1131
  /** Run every assertion against one output; return only the failures. Pure. */
1090
1132
  export declare const evaluateAssertions: (out: EvalReplayOutput, assertions: ReadonlyArray<EvalAssertion>) => ReadonlyArray<AssertionFailure>;
1091
1133
 
1134
+ /**
1135
+ * The modalities this build can execute.
1136
+ *
1137
+ * A model whose modality is not in here must not be offered: selecting it
1138
+ * produces a control that cannot run. When a primitive ships, the set grows and
1139
+ * an app that asked this question needs no change — which is the point.
1140
+ */
1141
+ export declare const executableModalities: () => ReadonlySet<ModelModality>;
1142
+
1092
1143
  declare const EXECUTOR_BRAND: "@voltro/ai/AgentExecutor";
1093
1144
 
1094
1145
  /** The `exposeAsTool` annotation shape (mirrors @voltro/protocol's, structural
@@ -1111,6 +1162,9 @@ export { extractReasoningMiddleware }
1111
1162
  */
1112
1163
  export declare const flattenMcpContent: (result: Record<string, unknown>, maxBytes: number) => string;
1113
1164
 
1165
+ /** One raw entry → the shape callers read. Exported for the parser's tests. */
1166
+ export declare const fromRaw: (raw: RawGatewayModel) => GatewayModelInfo;
1167
+
1114
1168
  /**
1115
1169
  * Pull the ACTUAL per-call cost (USD) out of an AI-SDK result's provider
1116
1170
  * metadata, when the Vercel AI Gateway reported it. The gateway attaches
@@ -1142,6 +1196,29 @@ export declare interface GatewayModelInfo {
1142
1196
  readonly outputPer1M: number;
1143
1197
  readonly cachedInputPer1M?: number;
1144
1198
  };
1199
+ /** What the model can do, unioned from `tags`, `supported_parameters` and
1200
+ * `modalities` — the three fields that each half-say it and regularly
1201
+ * disagree. */
1202
+ readonly capabilities?: ModelCapabilities;
1203
+ /** What it accepts and produces, verbatim from the response. */
1204
+ readonly modalities?: {
1205
+ readonly input: ReadonlyArray<string>;
1206
+ readonly output: ReadonlyArray<string>;
1207
+ };
1208
+ readonly contextWindow?: number;
1209
+ readonly maxOutputTokens?: number;
1210
+ /** ISO 8601. The wire carries UNIX seconds; see `gatewayCatalog.ts` for why
1211
+ * that is converted here rather than passed along. */
1212
+ readonly releasedAt?: string;
1213
+ readonly knowledgeCutoff?: string;
1214
+ /**
1215
+ * Whether the provider retains or trains on what is sent.
1216
+ *
1217
+ * It decides whether a model may see customer text, and it was the clearest
1218
+ * casualty of reading the provider type: a field the framework does not pass
1219
+ * through is a decision nobody makes.
1220
+ */
1221
+ readonly dataPolicy?: ModelDataPolicy;
1145
1222
  }
1146
1223
 
1147
1224
  /**
@@ -1309,10 +1386,6 @@ export declare interface GenerateObjectWithToolsOptions<A, I> {
1309
1386
  readonly toolContext?: ToolContext;
1310
1387
  }
1311
1388
 
1312
- /**
1313
- * Synthesize speech audio from text. Returns the audio bytes + media type and
1314
- * provider metadata; provider failures surface as `AiError`.
1315
- */
1316
1389
  export declare const generateSpeech: (options: GenerateSpeechOptions) => Effect.Effect<GenerateSpeechResult, AiError>;
1317
1390
 
1318
1391
  export declare interface GenerateSpeechOptions {
@@ -1320,7 +1393,7 @@ export declare interface GenerateSpeechOptions {
1320
1393
  readonly text: string;
1321
1394
  /** Override the provider/model (defaults to `providerFromEnv()`). `anthropic`
1322
1395
  * has no speech model; use `openai` or `gateway`. */
1323
- readonly provider?: ProviderConfig;
1396
+ readonly provider?: ProviderConfig | RegisteredSpeechConfig;
1324
1397
  /** Provider voice id. */
1325
1398
  readonly voice?: string;
1326
1399
  /** Desired audio container, e.g. `'mp3'` / `'wav'`. */
@@ -1504,6 +1577,18 @@ export declare interface HttpMcpTransportOptions {
1504
1577
 
1505
1578
  export declare interface InferenceDispatchDeps {
1506
1579
  readonly store: DataStore;
1580
+ /**
1581
+ * Resolve a transcription `storageRef` to bytes.
1582
+ *
1583
+ * Injected here for the same reason `transcribe` injects it: this package must
1584
+ * not learn about buckets, and the read carries the app's tenancy and signing.
1585
+ * Required only when an offloaded transcription used a ref — a queued call
1586
+ * with a `url` or inline bytes needs nothing.
1587
+ */
1588
+ readonly resolveTranscriptionRef?: (ref: string) => Promise<{
1589
+ bytes: Uint8Array;
1590
+ mediaType: string;
1591
+ }>;
1507
1592
  /**
1508
1593
  * Resume the parked run with the call's outcome.
1509
1594
  *
@@ -1610,6 +1695,8 @@ export declare interface JudgeVerdict {
1610
1695
  readonly reason: string;
1611
1696
  }
1612
1697
 
1698
+ export declare const knowledgeCutoffOf: (raw: RawGatewayModel) => string | undefined;
1699
+
1613
1700
  /**
1614
1701
  * The real LLM judge: asks the model for a structured `{ pass, score, reason }`
1615
1702
  * verdict via `generateObject` (schema-constrained, so a malformed grade surfaces
@@ -1643,6 +1730,8 @@ export declare const loggingMiddleware: (opts?: {
1643
1730
  */
1644
1731
  export declare const makeMcpTool: (server: McpServer, tool: McpToolInfo, policy: McpToolPolicy, bounds: McpBounds) => AnyTool;
1645
1732
 
1733
+ export declare const maxOutputTokensOf: (raw: RawGatewayModel) => number | undefined;
1734
+
1646
1735
  export declare const MCP_BOUNDS_DEFAULTS: McpBounds;
1647
1736
 
1648
1737
  /** Identity this client reports in `initialize`. No version literal here — the
@@ -2014,6 +2103,20 @@ export declare interface MockTurn {
2014
2103
  };
2015
2104
  }
2016
2105
 
2106
+ export declare const modalitiesOf: (raw: RawGatewayModel) => {
2107
+ readonly input: ReadonlyArray<string>;
2108
+ readonly output: ReadonlyArray<string>;
2109
+ };
2110
+
2111
+ /**
2112
+ * The primitive that makes each modality runnable — the reason the entry is
2113
+ * here, written down so a reader can check it rather than trust it.
2114
+ */
2115
+ export declare const MODALITY_PRIMITIVE: Readonly<Partial<Record<ModelModality, string>>>;
2116
+
2117
+ /** `model_type` on the wire; the same union `ModelModality` names. */
2118
+ export declare const modalityOf: (raw: RawGatewayModel) => ModelModality;
2119
+
2017
2120
  /**
2018
2121
  * Built-in published list prices (USD per 1M tokens) for the major providers.
2019
2122
  *
@@ -2038,6 +2141,29 @@ export declare interface MockTurn {
2038
2141
  */
2039
2142
  export declare const MODEL_PRICING_DEFAULTS: Record<string, ModelPrice>;
2040
2143
 
2144
+ /** What a model can do, unioned from the three fields that each half-say it. */
2145
+ export declare interface ModelCapabilities {
2146
+ readonly vision: boolean;
2147
+ readonly reasoning: boolean;
2148
+ readonly toolUse: boolean;
2149
+ readonly structuredOutput: boolean;
2150
+ readonly webSearch: boolean;
2151
+ readonly caching: boolean;
2152
+ readonly imageOut: boolean;
2153
+ readonly audioOut: boolean;
2154
+ readonly videoOut: boolean;
2155
+ readonly audioIn: boolean;
2156
+ readonly videoIn: boolean;
2157
+ readonly fileInput: boolean;
2158
+ }
2159
+
2160
+ export declare interface ModelDataPolicy {
2161
+ /** Zero data retention. */
2162
+ readonly zeroRetention: PolicyCoverage;
2163
+ /** The provider does not train on what is sent. */
2164
+ readonly noTraining: PolicyCoverage;
2165
+ }
2166
+
2041
2167
  export declare type ModelModality = 'language' | 'embedding' | 'image' | 'video' | 'realtime' | 'reranking' | 'speech' | 'transcription' | 'unknown';
2042
2168
 
2043
2169
  export declare interface ModelPrice {
@@ -2100,6 +2226,9 @@ export declare const passesPolicy: (tag: string, policy: AppToolPolicy) => boole
2100
2226
  */
2101
2227
  export declare const patchStreamingMessage: (store: DataStore, id: string, parts: ReadonlyArray<MessagePart>) => Effect.Effect<void>;
2102
2228
 
2229
+ /** How much of a provider's retention promise applies. */
2230
+ export declare type PolicyCoverage = 'all' | 'some' | 'none';
2231
+
2103
2232
  /**
2104
2233
  * The effective {@link ModelPrice} for a model id, or `undefined` if unknown.
2105
2234
  * Looks up the merged map (overrides over defaults); a gateway `creator/model`
@@ -2262,6 +2391,9 @@ export declare type ProviderConfig = {
2262
2391
  /** base64 the mock video model yields INSTEAD of a URL — exercises the
2263
2392
  * bytes branch of `generateVideo`. Takes precedence over `videoUrl`. */
2264
2393
  readonly videoBase64?: string;
2394
+ /** The text the mock transcription model returns. Lets a test assert on
2395
+ * an exact transcript without a provider. */
2396
+ readonly transcript?: string;
2265
2397
  };
2266
2398
  } | {
2267
2399
  readonly name: 'anthropic';
@@ -2327,7 +2459,7 @@ export declare interface QueuedInference {
2327
2459
  readonly runId: string | null;
2328
2460
  readonly signalName: string;
2329
2461
  readonly stepName: string;
2330
- readonly kind: 'text' | 'object';
2462
+ readonly kind: 'text' | 'object' | 'transcription';
2331
2463
  readonly provider: string;
2332
2464
  readonly model: string;
2333
2465
  readonly prompt: string;
@@ -2339,9 +2471,52 @@ export declare interface QueuedInference {
2339
2471
  readonly maxTokens: number | null;
2340
2472
  readonly providerOptions: unknown;
2341
2473
  readonly jsonSchema: Record<string, unknown> | null;
2474
+ /** The source + options for a `transcription` call. */
2475
+ readonly transcription: Record<string, unknown> | null;
2342
2476
  readonly attempts: number;
2343
2477
  }
2344
2478
 
2479
+ /** One entry of the raw `/v1/models` response, every field optional — the
2480
+ * gateway adds fields, and a parser that requires one is a parser that breaks
2481
+ * on a Tuesday. */
2482
+ export declare interface RawGatewayModel {
2483
+ readonly id?: unknown;
2484
+ readonly name?: unknown;
2485
+ readonly description?: unknown;
2486
+ readonly model_type?: unknown;
2487
+ readonly tags?: unknown;
2488
+ readonly supported_parameters?: unknown;
2489
+ readonly modalities?: unknown;
2490
+ readonly context_window?: unknown;
2491
+ readonly max_tokens?: unknown;
2492
+ readonly released?: unknown;
2493
+ readonly knowledge?: unknown;
2494
+ readonly zdr?: unknown;
2495
+ readonly no_training?: unknown;
2496
+ readonly pricing?: unknown;
2497
+ }
2498
+
2499
+ /**
2500
+ * The catalog, read from `/v1/models` directly.
2501
+ *
2502
+ * `getAvailableModels` asks `@ai-sdk/gateway`'s provider, whose entry type
2503
+ * carries six of the response's twenty-one fields — so the other fifteen never
2504
+ * arrive, and an app that needs them fetches the endpoint a second time and
2505
+ * writes its own parser. This reads the response.
2506
+ *
2507
+ * The provider path is kept and still used when a `gatewayProvider` is injected
2508
+ * (tests) or a fetch fails: a catalog with fewer fields is worth more than no
2509
+ * catalog, and the fields that were always there stay there. A degraded read is
2510
+ * VISIBLE rather than silent — the entries simply carry no `capabilities`, which
2511
+ * is the difference between "not known" and "no".
2512
+ */
2513
+ export declare const readGatewayCatalog: (opts?: {
2514
+ readonly apiKey?: string;
2515
+ readonly baseURL?: string;
2516
+ readonly modality?: ModelModality;
2517
+ readonly fetchImpl?: typeof fetch;
2518
+ }) => Promise<ReadonlyArray<GatewayModelInfo>>;
2519
+
2345
2520
  /** A store wrapper that records the dependency set of everything read through
2346
2521
  * it — the provenance the semantic cache tags entries with. */
2347
2522
  export declare interface ReadRecorder<S extends QueryableStore> {
@@ -2418,6 +2593,38 @@ export declare const redisResumableStreamStore: (redis: ResumableRedis, opts?: {
2418
2593
  */
2419
2594
  export declare const refreshMcpToolset: (server: McpServer, policy: McpToolPolicy) => Effect.Effect<McpToolset, McpError>;
2420
2595
 
2596
+ /**
2597
+ * Synthesize speech audio from text. Returns the audio bytes + media type and
2598
+ * provider metadata; provider failures surface as `AiError`.
2599
+ */
2600
+ /**
2601
+ * A provider registered with `registerSpeechProvider` — named, not enumerated.
2602
+ *
2603
+ * The gateway serves nine speech models; a real voice catalog runs to hundreds
2604
+ * behind vendors it does not carry (Cartesia, ElevenLabs, Google Cloud TTS). An
2605
+ * app with its own adapter had nowhere to plug it in, so it called its own
2606
+ * synthesizer and skipped this primitive entirely — losing the shared error type
2607
+ * and the shared cost path with it.
2608
+ */
2609
+ export declare interface RegisteredSpeechConfig {
2610
+ readonly name: string;
2611
+ readonly model?: string;
2612
+ readonly voice?: string;
2613
+ }
2614
+
2615
+ /** Every registered name — what a picker asks for. */
2616
+ export declare const registeredSpeechProviders: () => ReadonlyArray<string>;
2617
+
2618
+ /**
2619
+ * Register a speech provider under a name.
2620
+ *
2621
+ * The name then works in `generateSpeech({ provider: { name, model, voice } })`.
2622
+ * It must not collide with a built-in (`mock`, `openai`, `gateway`,
2623
+ * `anthropic`): silently shadowing one would make a config mean something
2624
+ * different depending on import order.
2625
+ */
2626
+ export declare const registerSpeechProvider: (name: string, provider: SpeechProvider) => void;
2627
+
2421
2628
  /**
2422
2629
  * Register (or reuse) the `AbortController` for `streamId`. `streamText` calls
2423
2630
  * this when given a `streamId`; it threads the returned controller's `signal`
@@ -2427,6 +2634,8 @@ export declare const refreshMcpToolset: (server: McpServer, policy: McpToolPolic
2427
2634
  */
2428
2635
  export declare const registerStream: (streamId: string) => AbortController;
2429
2636
 
2637
+ export declare const releasedAtOf: (raw: RawGatewayModel) => string | undefined;
2638
+
2430
2639
  /** What `.render(vars)` produces — a stamp plus the text to send. */
2431
2640
  export declare interface RenderedPrompt extends PromptStamp {
2432
2641
  readonly prompt: string;
@@ -2489,8 +2698,21 @@ export declare interface RerankOptions<T> {
2489
2698
  readonly getText?: (doc: T) => string;
2490
2699
  /** Rerank model id. e.g. 'rerank-english-v3.0' (cohere) / 'rerank-2' (voyage). */
2491
2700
  readonly model?: string;
2492
- /** Provider. Default 'mock' (lexical overlap; deterministic). */
2493
- readonly provider?: 'mock' | 'cohere' | 'voyage';
2701
+ /**
2702
+ * Where to rerank. Default `'mock'` (lexical overlap; deterministic).
2703
+ *
2704
+ * A `ProviderConfig` — the shape every other primitive in this package takes
2705
+ * — routes through the gateway, so one `AI_GATEWAY_API_KEY` reaches
2706
+ * `cohere/rerank-v4-pro`, `voyage/rerank-2.5` and the rest of the catalog.
2707
+ * This was the one primitive that could not do that: it wanted the vendors'
2708
+ * own keys and had no way to express the gateway at all.
2709
+ *
2710
+ * The string shorthands stay for the direct-key route. They are NOT folded
2711
+ * into `ProviderConfig`: that union is shared with `generateText` and friends,
2712
+ * and adding `cohere` there would let somebody name a provider that has no
2713
+ * language model — a config error the type currently catches.
2714
+ */
2715
+ readonly provider?: 'mock' | 'cohere' | 'voyage' | ProviderConfig;
2494
2716
  /** Return only the top-N after reranking. Default: all. */
2495
2717
  readonly topN?: number;
2496
2718
  }
@@ -2544,6 +2766,15 @@ export declare const resolveSystem: (exec: AnyAgentExecutor, input: unknown) =>
2544
2766
  */
2545
2767
  export declare const resolveToolModuleHandlers: (mod: Record<string, unknown>) => number;
2546
2768
 
2769
+ /**
2770
+ * The transcription model for a provider config.
2771
+ *
2772
+ * Mirrors `resolveSpeechModel` deliberately — the two are the same seam in
2773
+ * opposite directions, and the gateway exposes both (`.transcription(id)`), so
2774
+ * one key reaches all nine transcription models in the catalog.
2775
+ */
2776
+ export declare const resolveTranscriptionModel: (config: ProviderConfig) => TranscriptionModelV4;
2777
+
2547
2778
  export declare const resolveVideoModel: (config: ProviderConfig) => Experimental_VideoModelV4;
2548
2779
 
2549
2780
  /**
@@ -2625,6 +2856,43 @@ export declare const resumableStreamText: (args: {
2625
2856
  readonly idleTimeoutMs?: number;
2626
2857
  }) => Stream.Stream<SeqEvent>;
2627
2858
 
2859
+ /**
2860
+ * Search, rerank, truncate — with the two numbers named.
2861
+ *
2862
+ * ```ts
2863
+ * const docs = yield* retrieveReranked({
2864
+ * search: (limit) => Effect.promise(() => searchDocs(q, limit)),
2865
+ * query: q,
2866
+ * getText: (d) => d.body,
2867
+ * provider: { name: 'gateway', model: 'cohere/rerank-v4-pro' },
2868
+ * })
2869
+ * ```
2870
+ *
2871
+ * Returns the documents, not `{ document, score }` — the score is the second
2872
+ * stage's working, and a caller that wants it calls `rerank` directly. Handing
2873
+ * back a wrapper would make every consumer unwrap it.
2874
+ */
2875
+ export declare const retrieveReranked: <T, E>(options: RetrieveRerankedOptions<T, E>) => Effect.Effect<ReadonlyArray<T>, E | AiError>;
2876
+
2877
+ export declare interface RetrieveRerankedOptions<T, E> {
2878
+ /**
2879
+ * The app's own first stage, given a limit.
2880
+ *
2881
+ * A function rather than a value, so the helper decides how DEEP to go: the
2882
+ * candidate count is the parameter being traded against accuracy, and a
2883
+ * caller that had already run the search would have chosen it.
2884
+ */
2885
+ readonly search: (limit: number) => Effect.Effect<ReadonlyArray<T>, E>;
2886
+ readonly query: string;
2887
+ readonly getText: (doc: T) => string;
2888
+ /** How many the first stage fetches. Default 50 — cheap and shallow. */
2889
+ readonly candidates?: number;
2890
+ /** How many survive the rerank. Default 8. */
2891
+ readonly topN?: number;
2892
+ readonly provider?: RerankOptions<T>['provider'];
2893
+ readonly model?: string;
2894
+ }
2895
+
2628
2896
  /**
2629
2897
  * The synthesized `<name>.send` body. Appends the user turn at `order`, then
2630
2898
  * drives a streaming assistant turn at `order + 1` via `runAssistant` (which
@@ -2899,6 +3167,29 @@ export declare const setProviderOverride: (config: ProviderConfig | null) => Pro
2899
3167
 
2900
3168
  export { simulateStreamingMiddleware }
2901
3169
 
3170
+ export declare interface SpeechProvider {
3171
+ /** The voices this provider offers, for a picker. Optional: a provider with a
3172
+ * fixed voice set the app already knows needs no listing. */
3173
+ readonly listVoices?: () => Effect.Effect<ReadonlyArray<VoiceInfo>, AiError>;
3174
+ readonly synthesize: (request: SpeechSynthesisRequest) => Effect.Effect<GenerateSpeechResult, AiError>;
3175
+ }
3176
+
3177
+ /** The provider registered under `name`, or `undefined`. */
3178
+ export declare const speechProvider: (name: string) => SpeechProvider | undefined;
3179
+
3180
+ /** What `generateSpeech` hands a registered provider. Mirrors its own options
3181
+ * minus the parts the framework resolves. */
3182
+ export declare interface SpeechSynthesisRequest {
3183
+ readonly text: string;
3184
+ readonly model?: string;
3185
+ readonly voice?: string;
3186
+ readonly outputFormat?: string;
3187
+ readonly instructions?: string;
3188
+ readonly speed?: number;
3189
+ readonly language?: string;
3190
+ readonly providerOptions?: AiProviderOptions;
3191
+ }
3192
+
2902
3193
  /**
2903
3194
  * Run the underlying stream ONCE and persist every event. Assigns a monotonic
2904
3195
  * `seq`, appends each event, and marks the stream done at the end — even if a
@@ -3135,6 +3426,99 @@ export declare type ToolHandler<A, O> = (input: A, ctx: ToolContext) => Effect.E
3135
3426
 
3136
3427
  export declare const toQueuedInference: (row: Record<string, unknown>) => QueuedInference;
3137
3428
 
3429
+ /**
3430
+ * Transcribe audio to text.
3431
+ *
3432
+ * ```ts
3433
+ * const { text, segments } = yield* transcribe({
3434
+ * source: { storageRef: recording.ref },
3435
+ * resolveRef: (ref) => ctx.storage.read(ref),
3436
+ * timestamps: 'segment',
3437
+ * })
3438
+ * ```
3439
+ */
3440
+ export declare const transcribe: (options: TranscribeOptions) => Effect.Effect<TranscriptionResult, AiError>;
3441
+
3442
+ export declare interface TranscribeOptions {
3443
+ readonly source: TranscriptionSource;
3444
+ /** Override the provider/model. `anthropic` has no transcription model. */
3445
+ readonly provider?: ProviderConfig;
3446
+ /** ISO 639-1. Given rather than detected — detection costs a pass and is
3447
+ * wrong on short or accented audio. */
3448
+ readonly language?: string;
3449
+ /** Vocabulary, proper nouns, spellings the model would otherwise guess. */
3450
+ readonly prompt?: string;
3451
+ readonly timestamps?: TranscriptionTimestamps;
3452
+ /** Separate speakers. Not every provider can; one that cannot returns
3453
+ * segments without `speaker` rather than failing. */
3454
+ readonly diarize?: boolean;
3455
+ readonly translateToEnglish?: boolean;
3456
+ /**
3457
+ * Resolve a `storageRef` to bytes.
3458
+ *
3459
+ * Injected, and required only when the source IS a ref — so an app that
3460
+ * passes a url or bytes wires nothing, and `@voltro/ai` never imports storage.
3461
+ */
3462
+ readonly resolveRef?: (ref: string) => Promise<{
3463
+ bytes: Uint8Array;
3464
+ mediaType: string;
3465
+ }>;
3466
+ readonly providerOptions?: AiProviderOptions;
3467
+ }
3468
+
3469
+ export declare interface TranscriptionResult {
3470
+ readonly text: string;
3471
+ readonly language?: string;
3472
+ readonly durationSec?: number;
3473
+ readonly segments?: ReadonlyArray<TranscriptionSegment>;
3474
+ /**
3475
+ * Usage, for the SAME ledger every other primitive reports to.
3476
+ *
3477
+ * Transcription is the one modality billed by DURATION rather than by tokens,
3478
+ * which is exactly why it must not be the one modality missing from the
3479
+ * budget: a spend limit that silently excludes the long-running thing is not
3480
+ * a spend limit.
3481
+ */
3482
+ readonly usage?: MediaUsage;
3483
+ readonly providerMetadata?: unknown;
3484
+ }
3485
+
3486
+ export declare interface TranscriptionSegment {
3487
+ readonly start: number;
3488
+ readonly end: number;
3489
+ readonly text: string;
3490
+ /** Present only when `diarize` was asked for AND the provider supports it. */
3491
+ readonly speaker?: string;
3492
+ }
3493
+
3494
+ /**
3495
+ * Where the audio is.
3496
+ *
3497
+ * `storageRef` is the one to reach for: the file is already in the app's
3498
+ * storage, and resolving it there keeps tenancy, signing and lifetime with the
3499
+ * component that owns them. The resolver is injected rather than imported so
3500
+ * this module stays free of a storage dependency — `@voltro/ai` must not learn
3501
+ * about buckets to transcribe a file.
3502
+ */
3503
+ export declare type TranscriptionSource =
3504
+ /** Resolved by the app's own storage — the case to reach for. */
3505
+ {
3506
+ readonly storageRef: string;
3507
+ }
3508
+ /** Fetched by this function. Convenient, but the bytes do pass through this
3509
+ * process: the SDK takes bytes or base64 and never a location. */
3510
+ | {
3511
+ readonly url: string;
3512
+ }
3513
+ /** Already in hand. The one that costs memory by construction. */
3514
+ | {
3515
+ readonly bytes: Uint8Array;
3516
+ readonly mediaType: string;
3517
+ };
3518
+
3519
+ /** How much timestamp detail to ask for. Providers charge for it. */
3520
+ export declare type TranscriptionTimestamps = 'none' | 'segment' | 'word';
3521
+
3138
3522
  /**
3139
3523
  * Drop a stream's controller. `streamText` calls this in its finaliser when the
3140
3524
  * stream ends — success, error, cancel, or consumer interrupt. Idempotent.
@@ -3149,6 +3533,17 @@ export declare const unregisterStream: (streamId: string) => void;
3149
3533
  */
3150
3534
  export declare const validateCopilotProposal: (proposal: CopilotProposal, schema: CopilotSchema) => CopilotValidation;
3151
3535
 
3536
+ /** One voice a registered provider offers. */
3537
+ export declare interface VoiceInfo {
3538
+ readonly id: string;
3539
+ readonly name: string;
3540
+ readonly language?: string;
3541
+ /** Free-form, provider-shaped: gender, accent, age, whatever it publishes.
3542
+ * Not normalised — every vendor's taxonomy is different, and flattening them
3543
+ * would lose the half a picker actually shows. */
3544
+ readonly labels?: Readonly<Record<string, string>>;
3545
+ }
3546
+
3152
3547
  /**
3153
3548
  * Wrap an `Effect`-returning generate call with a `voltro.ai.call` span +
3154
3549
  * per-call metrics (count, error count, duration histogram, token + cost