@voltro/ai 0.1.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/CHANGELOG.md +52 -0
- package/LICENSE +57 -0
- package/README.md +26 -0
- package/SECURITY.md +56 -0
- package/THIRD-PARTY-NOTICES.md +929 -0
- package/dist/agent.d.ts +101 -0
- package/dist/agent.js +2 -0
- package/dist/agentDescriptor-DCd4F9zj.js +45 -0
- package/dist/events.d.ts +122 -0
- package/dist/events.js +54 -0
- package/dist/index.d.ts +1996 -0
- package/dist/index.js +1263 -0
- package/dist/provider-BOC5wBxx.js +294 -0
- package/dist/test.d.ts +125 -0
- package/dist/test.js +40 -0
- package/package.json +64 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,1996 @@
|
|
|
1
|
+
import { ActionProcedureDescriptor } from '@voltro/protocol';
|
|
2
|
+
import { ColumnDefinition } from '@voltro/database';
|
|
3
|
+
import { Context } from 'effect';
|
|
4
|
+
import { DataStore } from '@voltro/database';
|
|
5
|
+
import { defaultSettingsMiddleware } from 'ai';
|
|
6
|
+
import { Effect } from 'effect';
|
|
7
|
+
import { EmbeddingModelV4 } from '@ai-sdk/provider';
|
|
8
|
+
import { Experimental_VideoModelV4 } from '@ai-sdk/provider';
|
|
9
|
+
import { extractReasoningMiddleware } from 'ai';
|
|
10
|
+
import { GatewayLanguageModelEntry } from '@ai-sdk/gateway';
|
|
11
|
+
import { ImageModelV4 } from '@ai-sdk/provider';
|
|
12
|
+
import { LanguageModelMiddleware } from 'ai';
|
|
13
|
+
import { LanguageModelV4 } from '@ai-sdk/provider';
|
|
14
|
+
import { ModelMessage } from 'ai';
|
|
15
|
+
import { QueryDescriptor } from '@voltro/database';
|
|
16
|
+
import { QueryProcedureDescriptor } from '@voltro/protocol';
|
|
17
|
+
import { Runtime } from 'effect';
|
|
18
|
+
import { Schema } from 'effect';
|
|
19
|
+
import { simulateStreamingMiddleware } from 'ai';
|
|
20
|
+
import { SpeechModelV4 } from '@ai-sdk/provider';
|
|
21
|
+
import { Stream } from 'effect';
|
|
22
|
+
import { Table } from '@voltro/database';
|
|
23
|
+
import { ToolSet } from 'ai';
|
|
24
|
+
|
|
25
|
+
/** Count of in-flight streams (diagnostics / leak detection). */
|
|
26
|
+
export declare const activeStreamCount: () => number;
|
|
27
|
+
|
|
28
|
+
declare const AGENT_DESCRIPTOR_BRAND: "@voltro/ai/AgentDescriptor";
|
|
29
|
+
|
|
30
|
+
declare const AGENT_DESCRIPTOR_SYMBOL: unique symbol;
|
|
31
|
+
|
|
32
|
+
/** Per-turn message table — the `source` of the reactive `<name>.messages`
|
|
33
|
+
* query, and what `runAssistant` delta-patches as it streams. */
|
|
34
|
+
export declare const AGENT_MESSAGES_TABLE = "agent_messages";
|
|
35
|
+
|
|
36
|
+
/** Thread metadata table (one row per conversation). */
|
|
37
|
+
export declare const AGENT_THREADS_TABLE = "agent_threads";
|
|
38
|
+
|
|
39
|
+
/** Stamp an agent-initiated call's actor with `via: 'agent'` so audit + trace
|
|
40
|
+
* + provenance (08) can distinguish "user X" from "user X via agent". The
|
|
41
|
+
* actor id stays the calling subject's — the agent never escalates identity. */
|
|
42
|
+
export declare const agentActor: (subjectId: string | null) => {
|
|
43
|
+
readonly id: string | null;
|
|
44
|
+
readonly via: "agent";
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
export declare interface AgentDescriptor<S extends Schema.Schema.Any = DefaultAgentInputSchema> {
|
|
48
|
+
readonly [AGENT_DESCRIPTOR_SYMBOL]: true;
|
|
49
|
+
readonly _brand: typeof AGENT_DESCRIPTOR_BRAND;
|
|
50
|
+
/** Wire id → rpc tags `<name>.send` / `<name>.messages`. */
|
|
51
|
+
readonly name: string;
|
|
52
|
+
/** Input schema (default `{ prompt: string }`). The send route's wire input
|
|
53
|
+
* extends this with `{ threadId, order }`. */
|
|
54
|
+
readonly input?: S;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** Error taxonomy. `retryable` on the event drives client/agent retry. */
|
|
58
|
+
export declare const AgentErrorCode: Schema.Literal<["provider", "tool", "timeout", "cancelled", "decode", "internal"]>;
|
|
59
|
+
|
|
60
|
+
export declare type AgentErrorCode = typeof AgentErrorCode.Type;
|
|
61
|
+
|
|
62
|
+
export declare const AgentEvent: Schema.Union<[Schema.Struct<{
|
|
63
|
+
_tag: Schema.Literal<["token"]>;
|
|
64
|
+
text: typeof Schema.String;
|
|
65
|
+
}>, Schema.Struct<{
|
|
66
|
+
_tag: Schema.Literal<["reasoning"]>;
|
|
67
|
+
text: typeof Schema.String;
|
|
68
|
+
}>, Schema.Struct<{
|
|
69
|
+
_tag: Schema.Literal<["toolCall"]>;
|
|
70
|
+
id: typeof Schema.String;
|
|
71
|
+
name: typeof Schema.String;
|
|
72
|
+
input: typeof Schema.Unknown;
|
|
73
|
+
}>, Schema.Struct<{
|
|
74
|
+
_tag: Schema.Literal<["toolResult"]>;
|
|
75
|
+
id: typeof Schema.String;
|
|
76
|
+
name: typeof Schema.String;
|
|
77
|
+
output: Schema.optional<typeof Schema.Unknown>;
|
|
78
|
+
error: Schema.optional<typeof Schema.String>;
|
|
79
|
+
}>, Schema.Struct<{
|
|
80
|
+
_tag: Schema.Literal<["source"]>;
|
|
81
|
+
sourceType: Schema.Literal<["url", "document"]>;
|
|
82
|
+
id: typeof Schema.String;
|
|
83
|
+
url: Schema.optional<typeof Schema.String>;
|
|
84
|
+
title: Schema.optional<typeof Schema.String>;
|
|
85
|
+
mediaType: Schema.optional<typeof Schema.String>;
|
|
86
|
+
filename: Schema.optional<typeof Schema.String>;
|
|
87
|
+
}>, Schema.Struct<{
|
|
88
|
+
_tag: Schema.Literal<["file"]>;
|
|
89
|
+
mediaType: typeof Schema.String;
|
|
90
|
+
data: typeof Schema.String;
|
|
91
|
+
}>, Schema.Struct<{
|
|
92
|
+
_tag: Schema.Literal<["message"]>;
|
|
93
|
+
role: typeof Schema.String;
|
|
94
|
+
content: typeof Schema.String;
|
|
95
|
+
}>, Schema.Struct<{
|
|
96
|
+
_tag: Schema.Literal<["error"]>;
|
|
97
|
+
code: Schema.Literal<["provider", "tool", "timeout", "cancelled", "decode", "internal"]>;
|
|
98
|
+
message: typeof Schema.String;
|
|
99
|
+
retryable: typeof Schema.Boolean;
|
|
100
|
+
}>, Schema.Struct<{
|
|
101
|
+
_tag: Schema.Literal<["done"]>;
|
|
102
|
+
finishReason: typeof Schema.String;
|
|
103
|
+
usage: Schema.Struct<{
|
|
104
|
+
inputTokens: Schema.optional<typeof Schema.Number>;
|
|
105
|
+
outputTokens: Schema.optional<typeof Schema.Number>;
|
|
106
|
+
totalTokens: Schema.optional<typeof Schema.Number>;
|
|
107
|
+
}>;
|
|
108
|
+
}>]>;
|
|
109
|
+
|
|
110
|
+
export declare type AgentEvent = typeof AgentEvent.Type;
|
|
111
|
+
|
|
112
|
+
export declare interface AgentExecutor<S extends Schema.Schema.Any = DefaultAgentInputSchema> {
|
|
113
|
+
readonly [EXECUTOR_SYMBOL]: true;
|
|
114
|
+
readonly _brand: typeof EXECUTOR_BRAND;
|
|
115
|
+
readonly descriptor: AgentDescriptor<S>;
|
|
116
|
+
readonly config: AgentExecutorConfig<AgentInput<S>>;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export declare interface AgentExecutorConfig<Input = {
|
|
120
|
+
readonly prompt: string;
|
|
121
|
+
}> {
|
|
122
|
+
/** Static prompt, or a function of the decoded input. */
|
|
123
|
+
readonly system?: SystemPrompt<Input>;
|
|
124
|
+
/** Tools the model may call. Same shape `runAssistant` takes. */
|
|
125
|
+
readonly tools?: Record<string, AnyTool>;
|
|
126
|
+
/** Per-agent provider override, INCLUDING its own key:
|
|
127
|
+
* `{ name, model, apiKey? }`. Each field falls back to env when omitted —
|
|
128
|
+
* `AI_PROVIDER` / `AI_MODEL` and the provider's standard key var
|
|
129
|
+
* (`OPENAI_API_KEY` / `ANTHROPIC_API_KEY` / `AI_GATEWAY_API_KEY`). The key
|
|
130
|
+
* stays server-side (this file never reaches the browser).
|
|
131
|
+
*
|
|
132
|
+
* May ALSO be a function of the decoded input — for a UI model picker,
|
|
133
|
+
* cost/size routing, plan-tier models, etc. (mirrors `system`). Return
|
|
134
|
+
* `undefined` to fall back to env for that call. Runs server-side.
|
|
135
|
+
*
|
|
136
|
+
* SECURITY: the input is client-controlled. NEVER feed it straight into the
|
|
137
|
+
* config (`(i) => ({ model: i.model })`) — a caller could pick an
|
|
138
|
+
* expensive/arbitrary model. Constrain the picker with a `Schema.Literal`
|
|
139
|
+
* in the descriptor input + map to server-owned configs; read keys from
|
|
140
|
+
* `process.env`, never from the input:
|
|
141
|
+
* ```ts
|
|
142
|
+
* // descriptor: input: Schema.Struct({ prompt: Schema.String, tier: Schema.Literal('fast','smart') })
|
|
143
|
+
* model: (input) => input.tier === 'smart'
|
|
144
|
+
* ? { name: 'openai', model: 'gpt-5.5' }
|
|
145
|
+
* : { name: 'openai', model: 'gpt-4o-mini' },
|
|
146
|
+
* ``` */
|
|
147
|
+
readonly model?: ProviderConfig | ((input: Input) => ProviderConfig | undefined);
|
|
148
|
+
/** Max LLM↔tool round-trips before the loop stops. */
|
|
149
|
+
readonly maxSteps?: number;
|
|
150
|
+
/** Provider-specific options forwarded to the SDK — most importantly REASONING
|
|
151
|
+
* EFFORT. A routing/help agent on a reasoning model (`gpt-5.x`) should pin it
|
|
152
|
+
* low so a one-line answer doesn't cost the default high-effort 10s+:
|
|
153
|
+
* providerOptions: { openai: { reasoningEffort: 'low' } }
|
|
154
|
+
* See `AiProviderOptions`. */
|
|
155
|
+
readonly providerOptions?: AiProviderOptions;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/** The decoded input an agent's executor sees, derived from its schema. */
|
|
159
|
+
export declare type AgentInput<S extends Schema.Schema.Any> = Schema.Schema.Type<S>;
|
|
160
|
+
|
|
161
|
+
/** The agent's input schema (default `{ prompt: string }`). */
|
|
162
|
+
export declare const agentInputSchema: (desc: AnyAgentDescriptor) => Schema.Schema.Any;
|
|
163
|
+
|
|
164
|
+
export declare interface AgentMessage {
|
|
165
|
+
readonly id: string;
|
|
166
|
+
readonly threadId: string;
|
|
167
|
+
readonly tenantId: string | null;
|
|
168
|
+
readonly role: AgentMessageRole;
|
|
169
|
+
readonly content: string;
|
|
170
|
+
readonly streaming: boolean;
|
|
171
|
+
readonly order: number;
|
|
172
|
+
readonly stepOrder: number;
|
|
173
|
+
readonly parts: ReadonlyArray<MessagePart>;
|
|
174
|
+
readonly createdAt: Date;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
export declare type AgentMessageRole = 'user' | 'assistant' | 'system' | 'tool';
|
|
178
|
+
|
|
179
|
+
export declare const agentMessageSchema: Schema.Struct<{
|
|
180
|
+
id: typeof Schema.String;
|
|
181
|
+
threadId: typeof Schema.String;
|
|
182
|
+
role: typeof Schema.String;
|
|
183
|
+
content: typeof Schema.String;
|
|
184
|
+
streaming: typeof Schema.Boolean;
|
|
185
|
+
order: typeof Schema.Number;
|
|
186
|
+
stepOrder: typeof Schema.Number;
|
|
187
|
+
parts: Schema.Array$<Schema.TypeLiteral<{
|
|
188
|
+
type: typeof Schema.String;
|
|
189
|
+
}, readonly [{
|
|
190
|
+
readonly key: typeof Schema.String;
|
|
191
|
+
readonly value: typeof Schema.Unknown;
|
|
192
|
+
}]>>;
|
|
193
|
+
}>;
|
|
194
|
+
|
|
195
|
+
export declare const agentMessagesTable: Table<typeof AGENT_MESSAGES_TABLE, Record<string, ColumnDefinition<unknown>>, false>;
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* The two browser-safe route descriptors synthesized for an agent. The SAME
|
|
199
|
+
* source the runtime synthesis (`agentSynthesis.ts`) and codegen both consume,
|
|
200
|
+
* so "what the server serves" and "what the client is typed for" can't drift.
|
|
201
|
+
*/
|
|
202
|
+
export declare const agentRouteDescriptors: (desc: AnyAgentDescriptor) => {
|
|
203
|
+
send: ActionProcedureDescriptor<`${string}.send`, Schema.Schema.Any, Schema.Struct<{
|
|
204
|
+
text: typeof Schema.String;
|
|
205
|
+
}>, typeof Schema.Never>;
|
|
206
|
+
messages: QueryProcedureDescriptor<`${string}.messages`, Schema.Struct<{
|
|
207
|
+
threadId: typeof Schema.String;
|
|
208
|
+
}>, Schema.Array$<Schema.Struct<{
|
|
209
|
+
id: typeof Schema.String;
|
|
210
|
+
threadId: typeof Schema.String;
|
|
211
|
+
role: typeof Schema.String;
|
|
212
|
+
content: typeof Schema.String;
|
|
213
|
+
streaming: typeof Schema.Boolean;
|
|
214
|
+
order: typeof Schema.Number;
|
|
215
|
+
stepOrder: typeof Schema.Number;
|
|
216
|
+
parts: Schema.Array$<Schema.TypeLiteral<{
|
|
217
|
+
type: typeof Schema.String;
|
|
218
|
+
}, readonly [{
|
|
219
|
+
readonly key: typeof Schema.String;
|
|
220
|
+
readonly value: typeof Schema.Unknown;
|
|
221
|
+
}]>>;
|
|
222
|
+
}>>, typeof Schema.Never>;
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
export declare interface AgentThread {
|
|
226
|
+
readonly id: string;
|
|
227
|
+
readonly tenantId: string | null;
|
|
228
|
+
readonly title: string | null;
|
|
229
|
+
readonly createdAt: Date;
|
|
230
|
+
readonly updatedAt: Date;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
export declare const agentThreadsTable: Table<typeof AGENT_THREADS_TABLE, Record<string, ColumnDefinition<unknown>>, false>;
|
|
234
|
+
|
|
235
|
+
export declare const AI_BUDGET_TABLE = "_voltro_ai_budget";
|
|
236
|
+
|
|
237
|
+
export declare const AI_USAGE_TABLE = "_voltro_ai_usage";
|
|
238
|
+
|
|
239
|
+
/** Thrown when an AI call would push a tenant over its USD budget. */
|
|
240
|
+
export declare class AiBudgetExceeded extends AiBudgetExceeded_base {
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
declare const AiBudgetExceeded_base: Schema.TaggedErrorClass<AiBudgetExceeded, "AiBudgetExceeded", {
|
|
244
|
+
readonly _tag: Schema.tag<"AiBudgetExceeded">;
|
|
245
|
+
} & {
|
|
246
|
+
tenantId: Schema.NullOr<typeof Schema.String>;
|
|
247
|
+
limitUsd: typeof Schema.Number;
|
|
248
|
+
spentUsd: typeof Schema.Number;
|
|
249
|
+
attemptedUsd: typeof Schema.Number;
|
|
250
|
+
}>;
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* ONE reservation-counter row per `(tenantId, window)` budget bucket. It is the
|
|
254
|
+
* arbiter for the per-tenant USD cap: {@link requireAiBudget} RESERVES headroom
|
|
255
|
+
* on this row ATOMICALLY (a compare-and-set loop over the UNIQUE `bucket`
|
|
256
|
+
* below) BEFORE the provider call, so concurrent calls — same replica or
|
|
257
|
+
* across replicas — cannot overshoot the cap. This replaces the old
|
|
258
|
+
* check-then-act SUM over the ledger, which N concurrent callers could all read
|
|
259
|
+
* under-cap and all pass.
|
|
260
|
+
*
|
|
261
|
+
* `bucket` is a stable string key derived from the tenant + the budget window
|
|
262
|
+
* (see `budgetBucketKey`), so a rolling `since` window rotates to a fresh
|
|
263
|
+
* counter. `.maxLength(191)` keeps the UNIQUE a plain cross-dialect BTREE.
|
|
264
|
+
* The counter is a RESERVATION ledger separate from the descriptive
|
|
265
|
+
* `_voltro_ai_usage` spend ledger; it too is bounded by the retention sweep.
|
|
266
|
+
*/
|
|
267
|
+
export declare const aiBudgetTable: Table<typeof AI_BUDGET_TABLE, Record<string, ColumnDefinition<unknown>>, false>;
|
|
268
|
+
|
|
269
|
+
/** Attributes stamped on the span + used as metric labels. Derived from the
|
|
270
|
+
* resolved provider config; carries NO prompt/response content or secrets. */
|
|
271
|
+
export declare interface AiCallAttributes {
|
|
272
|
+
readonly provider: string;
|
|
273
|
+
readonly model: string;
|
|
274
|
+
/** 'generateText' | 'generateObject' | 'generateObjectWithTools' | 'streamText' | custom. */
|
|
275
|
+
readonly operation: string;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/** Derive span/metric attributes from a resolved provider config + operation. */
|
|
279
|
+
export declare const aiCallAttributes: (config: ProviderConfig, operation: string) => AiCallAttributes;
|
|
280
|
+
|
|
281
|
+
/** One logged AI call. `usage` is the SDK's token-usage object (present for
|
|
282
|
+
* generate; a stream logs the call at start, before usage is known). */
|
|
283
|
+
export declare interface AiCallLog {
|
|
284
|
+
readonly phase: 'generate' | 'stream';
|
|
285
|
+
readonly model: string;
|
|
286
|
+
readonly usage?: unknown;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* Typed AI failure. `reason: 'generation'` — the provider call failed
|
|
291
|
+
* (network / API / quota). `reason: 'decode'` — the model returned output
|
|
292
|
+
* that didn't satisfy the requested schema. Handlers can
|
|
293
|
+
* `Effect.catchTag('AiError', …)` and, for 'generation', retry.
|
|
294
|
+
*/
|
|
295
|
+
export declare class AiError extends AiError_base {
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
declare const AiError_base: Schema.TaggedErrorClass<AiError, "AiError", {
|
|
299
|
+
readonly _tag: Schema.tag<"AiError">;
|
|
300
|
+
} & {
|
|
301
|
+
reason: Schema.Literal<["generation", "decode"]>;
|
|
302
|
+
message: typeof Schema.String;
|
|
303
|
+
}>;
|
|
304
|
+
|
|
305
|
+
/** Provider-specific knobs forwarded verbatim to the Vercel AI SDK's
|
|
306
|
+
* `providerOptions` on generateText / streamText / generateObject. The outer
|
|
307
|
+
* key is the provider id, the inner record its options. The headline use is
|
|
308
|
+
* REASONING EFFORT on reasoning models — `gpt-5.x` defaults to a high effort
|
|
309
|
+
* that costs many seconds per call even for a one-line answer, so a routing/
|
|
310
|
+
* help agent should pin it low:
|
|
311
|
+
*
|
|
312
|
+
* providerOptions: { openai: { reasoningEffort: 'low' } } // or 'minimal'
|
|
313
|
+
* providerOptions: { anthropic: { thinking: { type: 'disabled' } } }
|
|
314
|
+
*
|
|
315
|
+
* Shape matches the SDK's `ProviderOptions` (Record<provider, Record<key,
|
|
316
|
+
* JSONValue>>). The value is the standard JSON value (mutable object + array,
|
|
317
|
+
* so it stays assignable to the SDK's `SharedV2ProviderOptions` under
|
|
318
|
+
* `exactOptionalPropertyTypes`) — NOT `unknown`, which the SDK rejects. */
|
|
319
|
+
declare type AiJsonValue = null | boolean | number | string | {
|
|
320
|
+
[key: string]: AiJsonValue;
|
|
321
|
+
} | AiJsonValue[];
|
|
322
|
+
|
|
323
|
+
/** A language-model middleware (the AI SDK's `LanguageModelMiddleware`): may
|
|
324
|
+
* implement any of `transformParams` / `wrapGenerate` / `wrapStream`. */
|
|
325
|
+
export declare type AiMiddleware = LanguageModelMiddleware;
|
|
326
|
+
|
|
327
|
+
export declare type AiProviderOptions = Record<string, Record<string, AiJsonValue>>;
|
|
328
|
+
|
|
329
|
+
export declare class AiService extends AiService_base {
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
declare const AiService_base: Context.TagClass<AiService, "@voltro/AiService", AiServiceImpl>;
|
|
333
|
+
|
|
334
|
+
export declare interface AiServiceImpl {
|
|
335
|
+
readonly generateText: (options: GenerateTextOptions) => Effect.Effect<GenerateTextResult, AiError>;
|
|
336
|
+
readonly generateObject: <A, I>(options: GenerateObjectOptions<A, I>) => Effect.Effect<GenerateObjectResult<A>, AiError>;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
/**
|
|
340
|
+
* Sum recorded spend (USD) — optionally scoped to a tenant and/or a
|
|
341
|
+
* `since` window. Reads the ledger table; pair with a reactive query
|
|
342
|
+
* (`source: '_voltro_ai_usage'`) for a live spend meter.
|
|
343
|
+
*/
|
|
344
|
+
export declare const aiSpendUsd: (store: DataStore, opts?: {
|
|
345
|
+
readonly tenantId?: string | null;
|
|
346
|
+
readonly since?: Date;
|
|
347
|
+
}) => Effect.Effect<number>;
|
|
348
|
+
|
|
349
|
+
/** Token tally as returned by `generateText` / `generateObject` / `streamText`. */
|
|
350
|
+
export declare interface AiUsage {
|
|
351
|
+
readonly inputTokens: number | undefined;
|
|
352
|
+
readonly outputTokens: number | undefined;
|
|
353
|
+
readonly totalTokens?: number | undefined;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
export declare interface AiUsageRecordInput {
|
|
357
|
+
readonly tenantId?: string | null;
|
|
358
|
+
readonly provider: ProviderName | string;
|
|
359
|
+
readonly model: string;
|
|
360
|
+
readonly operation: string;
|
|
361
|
+
readonly agent?: string | null;
|
|
362
|
+
readonly usage: AiUsage;
|
|
363
|
+
/** Override the price table (negotiated/volume pricing or unknown model). */
|
|
364
|
+
readonly price?: ModelPrice;
|
|
365
|
+
/** The ACTUAL per-call cost reported by the provider/gateway (USD). When set,
|
|
366
|
+
* it's persisted verbatim (costSource='gateway') — the fix for gateway-routed
|
|
367
|
+
* models the static table prices at zero. Extract it from the AI-SDK result's
|
|
368
|
+
* `providerMetadata.gateway.cost` (see `gatewayCostUsd`). */
|
|
369
|
+
readonly actualCostUsd?: number;
|
|
370
|
+
/** Override the timestamp (tests; default `new Date()`). */
|
|
371
|
+
readonly calledAt?: Date;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
/**
|
|
375
|
+
* One row per priced AI call. Registered into the auto-migrate set
|
|
376
|
+
* alongside the agent tables when an app ships any `*.agent.tsx`; a
|
|
377
|
+
* non-agent app that wants plain-call cost tracking imports
|
|
378
|
+
* `aiUsageTable` into its `database/index.ts` barrel. `.reactive()` so
|
|
379
|
+
* a live spend meter can subscribe to it.
|
|
380
|
+
*/
|
|
381
|
+
export declare const aiUsageTable: Table<typeof AI_USAGE_TABLE, Record<string, ColumnDefinition<unknown>>, true>;
|
|
382
|
+
|
|
383
|
+
export declare type AnthropicModel = 'claude-opus-4-8' | 'claude-sonnet-4-6' | 'claude-haiku-4-5-20251001' | (string & {});
|
|
384
|
+
|
|
385
|
+
/** A descriptor of any input shape — for the type-erased runtime helpers. */
|
|
386
|
+
export declare type AnyAgentDescriptor = AgentDescriptor<Schema.Schema.Any>;
|
|
387
|
+
|
|
388
|
+
/** An executor of any input shape — for the type-erased runtime helpers. */
|
|
389
|
+
export declare type AnyAgentExecutor = AgentExecutor<Schema.Schema.Any>;
|
|
390
|
+
|
|
391
|
+
/** A heterogeneous tool — the shape `buildSdkTools` / `runAssistant` consume.
|
|
392
|
+
* `defineTool` keeps full input typing on its `execute`/`handler` body, then
|
|
393
|
+
* erases to this shape so a `Record<string, AnyTool>` accepts tools of
|
|
394
|
+
* differing input/output types (Effect's `Schema` encoded type is invariant,
|
|
395
|
+
* so a heterogeneous collection can't preserve per-tool generics). */
|
|
396
|
+
export declare interface AnyTool {
|
|
397
|
+
readonly name: string;
|
|
398
|
+
readonly description: string;
|
|
399
|
+
readonly input: Schema.Schema.Any;
|
|
400
|
+
readonly output?: Schema.Schema.Any;
|
|
401
|
+
readonly execute?: (input: never) => Effect.Effect<unknown, never, unknown>;
|
|
402
|
+
readonly handler?: ToolHandler<never, unknown>;
|
|
403
|
+
readonly timeoutMs?: number;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
/** Append a message + bump the thread's `updatedAt` (best-effort). */
|
|
407
|
+
export declare const appendMessage: (store: DataStore, input: {
|
|
408
|
+
readonly threadId: string;
|
|
409
|
+
readonly role: AgentMessageRole;
|
|
410
|
+
readonly content: string;
|
|
411
|
+
readonly tenantId?: string | null;
|
|
412
|
+
readonly order?: number;
|
|
413
|
+
readonly stepOrder?: number;
|
|
414
|
+
readonly parts?: ReadonlyArray<MessagePart>;
|
|
415
|
+
readonly streaming?: boolean;
|
|
416
|
+
}) => Effect.Effect<AgentMessage>;
|
|
417
|
+
|
|
418
|
+
/**
|
|
419
|
+
* Insert the assistant row that the run will stream into. Starts empty +
|
|
420
|
+
* `streaming:true` so the reactive feed renders it as the live bubble. Returns
|
|
421
|
+
* the new row id (the store generates it).
|
|
422
|
+
*/
|
|
423
|
+
export declare const appendStreamingMessage: (store: DataStore, input: {
|
|
424
|
+
readonly threadId: string;
|
|
425
|
+
readonly tenantId?: string | null;
|
|
426
|
+
readonly order: number;
|
|
427
|
+
}) => Effect.Effect<string>;
|
|
428
|
+
|
|
429
|
+
/**
|
|
430
|
+
* Wrap `model` with the global stack plus any per-call `middleware`, in that
|
|
431
|
+
* order (global first). Returns `model` untouched when there's nothing to
|
|
432
|
+
* apply — no wrapper, no behavior change. Used by `resolveModel`.
|
|
433
|
+
*/
|
|
434
|
+
export declare const applyMiddleware: (model: LanguageModelV4, middleware?: AiMiddleware | ReadonlyArray<AiMiddleware>) => LanguageModelV4;
|
|
435
|
+
|
|
436
|
+
/** The descriptor fields tool synthesis reads. */
|
|
437
|
+
export declare interface AppToolDescriptor {
|
|
438
|
+
readonly kind: 'query' | 'mutation' | 'action' | string;
|
|
439
|
+
readonly name: string;
|
|
440
|
+
readonly input: Schema.Schema.Any;
|
|
441
|
+
readonly output?: Schema.Schema.Any;
|
|
442
|
+
readonly exposeAsTool?: ExposeAsToolLike;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
/** A descriptor paired with its ctx-bound executor (the real handler). */
|
|
446
|
+
export declare interface AppToolEntry {
|
|
447
|
+
readonly descriptor: AppToolDescriptor;
|
|
448
|
+
readonly invoke: (input: unknown) => Promise<unknown> | unknown;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
export declare interface AppToolPolicy {
|
|
452
|
+
/** Allowlist — tags (with `*` glob) or a RegExp. Omitted = all exposed. */
|
|
453
|
+
readonly allow?: ReadonlyArray<string> | RegExp;
|
|
454
|
+
/** Denylist — wins over allow. For destructive/high-blast tags. */
|
|
455
|
+
readonly deny?: ReadonlyArray<string> | RegExp;
|
|
456
|
+
/** Default confirm posture for writes when the descriptor doesn't say.
|
|
457
|
+
* Default true (writes confirm). */
|
|
458
|
+
readonly requireConfirmForWrites?: boolean;
|
|
459
|
+
/** Include WRITE tools (mutation/action) in the executable toolset.
|
|
460
|
+
* Default false — writes are opt-in + confirm (the safety default). Reads
|
|
461
|
+
* are always included. */
|
|
462
|
+
readonly includeWrites?: boolean;
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
/**
|
|
466
|
+
* The EXECUTABLE toolset for the agent loop. Read tools are always included;
|
|
467
|
+
* WRITE tools only when `policy.includeWrites` is true (writes are opt-in —
|
|
468
|
+
* the safety default). Each tool's handler runs the descriptor's bound
|
|
469
|
+
* `invoke` (the real handler under the caller's subject). Confirm is enforced
|
|
470
|
+
* by the agent loop / UI using `synthesizeToolSpecs` — the pure executable
|
|
471
|
+
* tool can't gate a human prompt.
|
|
472
|
+
*/
|
|
473
|
+
export declare const appTools: (entries: ReadonlyArray<AppToolEntry>, policy?: AppToolPolicy) => ReadonlyArray<AnyTool>;
|
|
474
|
+
|
|
475
|
+
/** Render the capability map into a system prompt that enumerates EXACTLY the
|
|
476
|
+
* tables + columns the model may use — the soft in-grammar steer that pairs
|
|
477
|
+
* with the hard validator. */
|
|
478
|
+
export declare const buildCopilotSystemPrompt: (schema: CopilotSchema) => string;
|
|
479
|
+
|
|
480
|
+
/** Adapt our tools to the AI SDK's ToolSet (keyed by tool name). When a
|
|
481
|
+
* `ctx` is supplied it is threaded into `*.tool.tsx` executor bodies; inline
|
|
482
|
+
* `execute` tools ignore it. */
|
|
483
|
+
export declare const buildSdkTools: (tools: Record<string, AnyTool>, ctx?: ToolContext) => ToolSet;
|
|
484
|
+
|
|
485
|
+
/**
|
|
486
|
+
* Cancel an in-flight stream by id. Aborts its controller (the SDK call stops +
|
|
487
|
+
* the stream emits a terminal `cancelled` error). Returns `true` if a live
|
|
488
|
+
* stream was found and aborted, `false` if the id is unknown (already finished
|
|
489
|
+
* or never started) — so a double-click "Stop" is a harmless no-op.
|
|
490
|
+
*/
|
|
491
|
+
export declare const cancelStream: (streamId: string) => boolean;
|
|
492
|
+
|
|
493
|
+
/** Clear the global middleware stack. */
|
|
494
|
+
export declare const clearAiMiddleware: () => void;
|
|
495
|
+
|
|
496
|
+
export declare interface ConsumeOptions {
|
|
497
|
+
/** Replay events with `seq` strictly greater than this (default 0 = from the
|
|
498
|
+
* start). A reconnecting client passes the last `seq` it rendered. */
|
|
499
|
+
readonly fromSeq?: number;
|
|
500
|
+
/** Tail poll interval in ms while the producer is still running (default 50). */
|
|
501
|
+
readonly pollMs?: number;
|
|
502
|
+
/** End the consumer if no new event arrives for this long AND the producer
|
|
503
|
+
* hasn't finished — a safety valve against a crashed producer that never
|
|
504
|
+
* marked done. Default: undefined (wait indefinitely). */
|
|
505
|
+
readonly idleTimeoutMs?: number;
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
/**
|
|
509
|
+
* Consume a resumable stream: replay persisted events past `fromSeq`, then tail
|
|
510
|
+
* live ones until the producer marks it done. This is what a streaming rpc
|
|
511
|
+
* returns to each client — a fresh subscription (or a reconnect) just calls it
|
|
512
|
+
* again with the right `fromSeq`.
|
|
513
|
+
*/
|
|
514
|
+
export declare const consumeResumableStream: (streamId: string, store: ResumableStreamStore, opts?: ConsumeOptions) => Stream.Stream<SeqEvent>;
|
|
515
|
+
|
|
516
|
+
/** Hard cap on rows — a copilot query never returns an unbounded set. */
|
|
517
|
+
export declare const COPILOT_MAX_LIMIT = 200;
|
|
518
|
+
|
|
519
|
+
export declare interface CopilotFilter {
|
|
520
|
+
readonly column: string;
|
|
521
|
+
readonly op: CopilotOp;
|
|
522
|
+
readonly value: unknown;
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
export declare type CopilotOp = 'eq' | 'neq' | 'gt' | 'gte' | 'lt' | 'lte' | 'contains' | 'in';
|
|
526
|
+
|
|
527
|
+
/** The model's proposed query — a constrained shape, never free SQL. */
|
|
528
|
+
export declare interface CopilotProposal {
|
|
529
|
+
readonly table: string;
|
|
530
|
+
/** Projection; omit/empty = all columns. */
|
|
531
|
+
readonly columns?: ReadonlyArray<string>;
|
|
532
|
+
readonly filters?: ReadonlyArray<CopilotFilter>;
|
|
533
|
+
readonly orderBy?: {
|
|
534
|
+
readonly column: string;
|
|
535
|
+
readonly direction?: 'asc' | 'desc';
|
|
536
|
+
};
|
|
537
|
+
readonly limit?: number;
|
|
538
|
+
/** Optional aggregate (validated; the descriptor build for aggregates is a
|
|
539
|
+
* refinement — see `aggregate` on the result). */
|
|
540
|
+
readonly aggregate?: {
|
|
541
|
+
readonly fn: 'count' | 'sum' | 'avg' | 'min' | 'max';
|
|
542
|
+
readonly column?: string;
|
|
543
|
+
};
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
/** The effect/Schema the model's structured output is constrained to (pass as
|
|
547
|
+
* `generateObject({ schema: CopilotProposalSchema })`). Mirrors
|
|
548
|
+
* {@link CopilotProposal}. */
|
|
549
|
+
export declare const CopilotProposalSchema: Schema.Struct<{
|
|
550
|
+
table: typeof Schema.String;
|
|
551
|
+
columns: Schema.optional<Schema.Array$<typeof Schema.String>>;
|
|
552
|
+
filters: Schema.optional<Schema.Array$<Schema.Struct<{
|
|
553
|
+
column: typeof Schema.String;
|
|
554
|
+
op: Schema.Literal<["eq", "neq", "gt", "gte", "lt", "lte", "contains", "in"]>;
|
|
555
|
+
value: typeof Schema.Unknown;
|
|
556
|
+
}>>>;
|
|
557
|
+
orderBy: Schema.optional<Schema.Struct<{
|
|
558
|
+
column: typeof Schema.String;
|
|
559
|
+
direction: Schema.optional<Schema.Literal<["asc", "desc"]>>;
|
|
560
|
+
}>>;
|
|
561
|
+
limit: Schema.optional<typeof Schema.Number>;
|
|
562
|
+
aggregate: Schema.optional<Schema.Struct<{
|
|
563
|
+
fn: Schema.Literal<["count", "sum", "avg", "min", "max"]>;
|
|
564
|
+
column: Schema.optional<typeof Schema.String>;
|
|
565
|
+
}>>;
|
|
566
|
+
}>;
|
|
567
|
+
|
|
568
|
+
/** Why a proposal was refused. Typed so a copilot tool/action can surface it on
|
|
569
|
+
* its `error:` channel and the client can show "I can't answer that". */
|
|
570
|
+
export declare class CopilotRejected extends CopilotRejected_base {
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
declare const CopilotRejected_base: Schema.TaggedErrorClass<CopilotRejected, "CopilotRejected", {
|
|
574
|
+
readonly _tag: Schema.tag<"CopilotRejected">;
|
|
575
|
+
} & {
|
|
576
|
+
reason: Schema.Literal<["unknown-table", "unknown-column", "bad-op", "bad-aggregate", "empty"]>;
|
|
577
|
+
detail: typeof Schema.String;
|
|
578
|
+
}>;
|
|
579
|
+
|
|
580
|
+
export declare interface CopilotSchema {
|
|
581
|
+
readonly tables: ReadonlyArray<CopilotSchemaTable>;
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
/** The manifest slice the validator needs (mirrors the capability map's
|
|
585
|
+
* `tables`). Decoupled — pass the live manifest's tables. */
|
|
586
|
+
export declare interface CopilotSchemaColumn {
|
|
587
|
+
readonly name: string;
|
|
588
|
+
readonly type?: string;
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
export declare interface CopilotSchemaTable {
|
|
592
|
+
readonly name: string;
|
|
593
|
+
readonly columns: ReadonlyArray<CopilotSchemaColumn>;
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
export declare type CopilotValidation = {
|
|
597
|
+
readonly ok: true;
|
|
598
|
+
readonly descriptor: QueryDescriptor;
|
|
599
|
+
readonly table: string;
|
|
600
|
+
readonly aggregate?: CopilotProposal['aggregate'];
|
|
601
|
+
} | {
|
|
602
|
+
readonly ok: false;
|
|
603
|
+
readonly rejection: CopilotRejected;
|
|
604
|
+
};
|
|
605
|
+
|
|
606
|
+
/** Cosine similarity between two vectors (re-export of the AI SDK helper).
|
|
607
|
+
* Useful for app-side reranking / thresholding without a DB round-trip. */
|
|
608
|
+
export declare const cosineSimilarity: (a: ReadonlyArray<number>, b: ReadonlyArray<number>) => number;
|
|
609
|
+
|
|
610
|
+
export declare interface CostBreakdown {
|
|
611
|
+
readonly inputTokens: number;
|
|
612
|
+
readonly outputTokens: number;
|
|
613
|
+
readonly inputCostUsd: number;
|
|
614
|
+
readonly outputCostUsd: number;
|
|
615
|
+
readonly totalCostUsd: number;
|
|
616
|
+
/** Where `totalCostUsd` came from: `gateway` = the provider/gateway reported
|
|
617
|
+
* the ACTUAL per-call cost (authoritative); `estimated` = derived from the
|
|
618
|
+
* static price table (or zero for an unknown model). Analytics can flag
|
|
619
|
+
* estimated rows + a model that priced at zero. */
|
|
620
|
+
readonly costSource: 'gateway' | 'estimated';
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
/** Open a new thread (the store generates the id). */
|
|
624
|
+
export declare const createThread: (store: DataStore, input?: {
|
|
625
|
+
readonly tenantId?: string | null;
|
|
626
|
+
readonly title?: string | null;
|
|
627
|
+
}) => Effect.Effect<AgentThread>;
|
|
628
|
+
|
|
629
|
+
export declare interface DataCopilotDeps {
|
|
630
|
+
/** Turn the NL question (+ the grammar system prompt) into a structured
|
|
631
|
+
* proposal. Wire to `@voltro/ai`'s `generateObject` with
|
|
632
|
+
* `CopilotProposalSchema`; injected so the orchestrator is unit-testable
|
|
633
|
+
* without a live model. */
|
|
634
|
+
readonly propose: (args: {
|
|
635
|
+
readonly system: string;
|
|
636
|
+
readonly prompt: string;
|
|
637
|
+
}) => Promise<CopilotProposal>;
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
/**
|
|
641
|
+
* Durable, multi-node resumable-stream store backed by the framework's own
|
|
642
|
+
* `DataStore` — works on every dialect the framework supports. The producer
|
|
643
|
+
* election uses `insertIgnore` (INSERT … ON CONFLICT DO NOTHING) on the UNIQUE
|
|
644
|
+
* `streamId`, comparing the returned row's id to ours: we inserted ⇒ we won.
|
|
645
|
+
* That is the same at-most-once arbiter `_voltro_idempotency` uses.
|
|
646
|
+
*
|
|
647
|
+
* The two tables (`streamEventsTable`, `streamStateTable`) register into the
|
|
648
|
+
* app's migration set like the agent/usage tables — add them to the database
|
|
649
|
+
* barrel (or they ride along with a `*.agent.tsx` app). Run `gcResumableStreams`
|
|
650
|
+
* periodically to drop finished streams past a retention window.
|
|
651
|
+
*/
|
|
652
|
+
export declare const dataStoreResumableStreamStore: (store: DataStore) => ResumableStreamStore;
|
|
653
|
+
|
|
654
|
+
declare const DEFAULT_INPUT: Schema.Struct<{
|
|
655
|
+
prompt: typeof Schema.String;
|
|
656
|
+
}>;
|
|
657
|
+
|
|
658
|
+
/** Default input schema type when `input` is omitted (`{ prompt: string }`). */
|
|
659
|
+
export declare type DefaultAgentInputSchema = typeof DEFAULT_INPUT;
|
|
660
|
+
|
|
661
|
+
/**
|
|
662
|
+
* Default service implementation backed by `providerFromEnv`. Apps that
|
|
663
|
+
* want runtime provider switching wire their own AiServiceImpl into a
|
|
664
|
+
* Layer and provide it instead.
|
|
665
|
+
*/
|
|
666
|
+
export declare const defaultAiService: AiServiceImpl;
|
|
667
|
+
|
|
668
|
+
export { defaultSettingsMiddleware }
|
|
669
|
+
|
|
670
|
+
/**
|
|
671
|
+
* Declare an agent's browser-safe wire contract: its `name` and `input`
|
|
672
|
+
* schema. The decoded input type is inferred from the schema (like
|
|
673
|
+
* `defineQuery`). Behaviour (system prompt, tools, model + key, maxSteps) lives
|
|
674
|
+
* in the paired `*.agent.server.tsx` via `defineAgentExecutor`.
|
|
675
|
+
*
|
|
676
|
+
* ```ts
|
|
677
|
+
* // support.agent.tsx (browser-safe descriptor)
|
|
678
|
+
* export const support = defineAgent({
|
|
679
|
+
* name: 'support',
|
|
680
|
+
* input: Schema.Struct({ prompt: Schema.String, locale: Schema.optional(Schema.String) }),
|
|
681
|
+
* })
|
|
682
|
+
* ```
|
|
683
|
+
*/
|
|
684
|
+
export declare const defineAgent: <S extends Schema.Schema.Any = DefaultAgentInputSchema>(config: {
|
|
685
|
+
readonly name: string;
|
|
686
|
+
readonly input?: S;
|
|
687
|
+
}) => AgentDescriptor<S>;
|
|
688
|
+
|
|
689
|
+
/**
|
|
690
|
+
* Bind a server executor to its descriptor. Default-export the result from
|
|
691
|
+
* `<name>.agent.server.tsx`:
|
|
692
|
+
*
|
|
693
|
+
* ```ts
|
|
694
|
+
* import { defineAgentExecutor } from '@voltro/ai'
|
|
695
|
+
* import { support } from './support.agent'
|
|
696
|
+
* import { searchDocs } from '../tools/searchDocs.tool'
|
|
697
|
+
*
|
|
698
|
+
* export default defineAgentExecutor(support, {
|
|
699
|
+
* system: (input) => `…locale=${input.locale}`,
|
|
700
|
+
* tools: { searchDocs },
|
|
701
|
+
* model: { name: 'openai', model: 'gpt-5.5', apiKey: process.env.SUPPORT_OPENAI_KEY },
|
|
702
|
+
* maxSteps: 6,
|
|
703
|
+
* })
|
|
704
|
+
* ```
|
|
705
|
+
*/
|
|
706
|
+
export declare const defineAgentExecutor: <S extends Schema.Schema.Any = DefaultAgentInputSchema>(descriptor: AgentDescriptor<S>, config: AgentExecutorConfig<AgentInput<S>>) => AgentExecutor<S>;
|
|
707
|
+
|
|
708
|
+
export declare const defineTool: <A, I, O>(def: {
|
|
709
|
+
readonly name: string;
|
|
710
|
+
readonly description: string;
|
|
711
|
+
readonly input: Schema.Schema<A, I>;
|
|
712
|
+
readonly output?: Schema.Schema.Any;
|
|
713
|
+
readonly execute?: (input: A) => Effect.Effect<O, never, unknown>;
|
|
714
|
+
readonly handler?: ToolHandler<A, O>;
|
|
715
|
+
readonly timeoutMs?: number;
|
|
716
|
+
}) => AnyTool;
|
|
717
|
+
|
|
718
|
+
/**
|
|
719
|
+
* Embed a single string to a vector. Returns the embedding through Effect's
|
|
720
|
+
* success channel; provider failures surface as a typed
|
|
721
|
+
* `AiError({ reason: 'generation' })`. Defaults to the env-configured
|
|
722
|
+
* provider (mock unless `AI_EMBED_PROVIDER` is set).
|
|
723
|
+
*
|
|
724
|
+
* ```ts
|
|
725
|
+
* const vec = yield* embed('how do I deploy?')
|
|
726
|
+
* ```
|
|
727
|
+
*/
|
|
728
|
+
export declare const embed: (text: string, config?: EmbeddingProviderConfig) => Effect.Effect<ReadonlyArray<number>, AiError>;
|
|
729
|
+
|
|
730
|
+
export declare interface EmbeddingProviderConfig {
|
|
731
|
+
readonly name: EmbeddingProviderName;
|
|
732
|
+
/** Model id. For 'openai': e.g. 'text-embedding-3-small'. Ignored by the
|
|
733
|
+
* mock provider (which derives its output from `mockDim`). */
|
|
734
|
+
readonly model: string;
|
|
735
|
+
/** Mock-only: the deterministic output-vector length. Default 8. */
|
|
736
|
+
readonly mockDim?: number;
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
/**
|
|
740
|
+
* Read the embedding-provider config from env:
|
|
741
|
+
* AI_EMBED_PROVIDER=mock|openai|voyage|cohere (default: mock)
|
|
742
|
+
* AI_EMBED_MODEL=<model-id> (default per provider)
|
|
743
|
+
* AI_EMBED_MOCK_DIM=<n> (mock-only, default 8)
|
|
744
|
+
*/
|
|
745
|
+
export declare const embeddingProviderFromEnv: (env?: Record<string, string | undefined>) => EmbeddingProviderConfig;
|
|
746
|
+
|
|
747
|
+
export declare type EmbeddingProviderName = 'mock' | 'openai' | 'voyage' | 'cohere';
|
|
748
|
+
|
|
749
|
+
/**
|
|
750
|
+
* Embed many strings in one batched call. Order-preserving: result[i] is the
|
|
751
|
+
* embedding of texts[i]. Cheaper than N `embed()` calls — the SDK batches up
|
|
752
|
+
* to the provider's per-call limit.
|
|
753
|
+
*/
|
|
754
|
+
export declare const embedMany: (texts: ReadonlyArray<string>, config?: EmbeddingProviderConfig) => Effect.Effect<ReadonlyArray<ReadonlyArray<number>>, AiError>;
|
|
755
|
+
|
|
756
|
+
/**
|
|
757
|
+
* Derive a USD cost from a token `usage` tally + a model price. An
|
|
758
|
+
* unknown model (or the `mock` provider) resolves to zero cost rather
|
|
759
|
+
* than throwing, so cost accounting never breaks a call.
|
|
760
|
+
*/
|
|
761
|
+
export declare const estimateCostUsd: (usage: AiUsage, opts: {
|
|
762
|
+
readonly model: string;
|
|
763
|
+
readonly price?: ModelPrice;
|
|
764
|
+
readonly actualCostUsd?: number;
|
|
765
|
+
}) => CostBreakdown;
|
|
766
|
+
|
|
767
|
+
declare const EXECUTOR_BRAND: "@voltro/ai/AgentExecutor";
|
|
768
|
+
|
|
769
|
+
declare const EXECUTOR_SYMBOL: unique symbol;
|
|
770
|
+
|
|
771
|
+
/** The `exposeAsTool` annotation shape (mirrors @voltro/protocol's, structural
|
|
772
|
+
* so @voltro/ai needs no protocol dep). `true` alone can't expose — a tool
|
|
773
|
+
* needs a description, so only the object form (with `description`) exposes. */
|
|
774
|
+
export declare type ExposeAsToolLike = boolean | {
|
|
775
|
+
readonly description: string;
|
|
776
|
+
readonly confirm?: boolean;
|
|
777
|
+
readonly maxPerRun?: number;
|
|
778
|
+
};
|
|
779
|
+
|
|
780
|
+
export { extractReasoningMiddleware }
|
|
781
|
+
|
|
782
|
+
/**
|
|
783
|
+
* Pull the ACTUAL per-call cost (USD) out of an AI-SDK result's provider
|
|
784
|
+
* metadata, when the Vercel AI Gateway reported it. The gateway attaches
|
|
785
|
+
* `providerMetadata.gateway.cost` (a USD number or numeric string) to
|
|
786
|
+
* generate/stream results; returns `undefined` when absent (direct providers,
|
|
787
|
+
* the mock) so the caller falls back to the price table.
|
|
788
|
+
*/
|
|
789
|
+
export declare const gatewayCostUsd: (providerMetadata: unknown) => number | undefined;
|
|
790
|
+
|
|
791
|
+
/** Creator namespaces the Vercel AI Gateway routes to. Open union — the named
|
|
792
|
+
* arms are autocomplete, any other creator string still works. */
|
|
793
|
+
export declare type GatewayCreator = 'openai' | 'anthropic' | 'google' | (string & {});
|
|
794
|
+
|
|
795
|
+
/** The gateway addresses models as `creator/model` ids. The template-literal
|
|
796
|
+
* type enforces that shape at COMPILE time — what `providerFromEnv` otherwise
|
|
797
|
+
* only catches as a runtime throw (`{ name: 'gateway', model: 'gpt-5.5' }` is
|
|
798
|
+
* now a type error, not a boot crash). */
|
|
799
|
+
export declare type GatewayModel = `${GatewayCreator}/${string}`;
|
|
800
|
+
|
|
801
|
+
export declare interface GatewayModelInfo {
|
|
802
|
+
/** `creator/model` id used to address the model (e.g. `openai/gpt-5.5`). */
|
|
803
|
+
readonly id: string;
|
|
804
|
+
readonly name: string;
|
|
805
|
+
readonly description?: string;
|
|
806
|
+
readonly modality: ModelModality;
|
|
807
|
+
/** Pricing normalised to USD per 1,000,000 tokens (matches `cost.ModelPrice`). */
|
|
808
|
+
readonly pricing?: {
|
|
809
|
+
readonly inputPer1M: number;
|
|
810
|
+
readonly outputPer1M: number;
|
|
811
|
+
readonly cachedInputPer1M?: number;
|
|
812
|
+
};
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
/**
|
|
816
|
+
* Drop finished resumable streams whose state row predates `olderThan` —
|
|
817
|
+
* deletes the state row + all its event rows. Run it on a schedule (a cron /
|
|
818
|
+
* sweep) so the log doesn't grow without bound. Returns the count of streams
|
|
819
|
+
* collected. Only DONE streams are eligible (an in-flight one is never GC'd).
|
|
820
|
+
*/
|
|
821
|
+
export declare const gcResumableStreams: (store: DataStore, opts: {
|
|
822
|
+
readonly olderThan: Date;
|
|
823
|
+
}) => Effect.Effect<number>;
|
|
824
|
+
|
|
825
|
+
export declare interface GeneratedAudio {
|
|
826
|
+
readonly data: Uint8Array | string;
|
|
827
|
+
readonly mediaType: string;
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
/** One generated image. `data` is the raw bytes (the SDK also exposes base64;
|
|
831
|
+
* the union keeps a base64 string valid for callers that pass one through). */
|
|
832
|
+
export declare interface GeneratedImage {
|
|
833
|
+
readonly data: Uint8Array | string;
|
|
834
|
+
readonly mediaType: string;
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
/** The produced video. Providers return EITHER a `url` (the common case — video
|
|
838
|
+
* files are large) OR inline `data` bytes; exactly one is populated. */
|
|
839
|
+
export declare interface GeneratedVideo {
|
|
840
|
+
readonly data?: Uint8Array;
|
|
841
|
+
readonly url?: string;
|
|
842
|
+
readonly mediaType: string;
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
/**
|
|
846
|
+
* Generate one or more images from a text prompt. Returns the decoded image
|
|
847
|
+
* bytes + media type, a usage tally, and provider metadata. Provider failures
|
|
848
|
+
* surface as `AiError({ reason: 'generation' })`.
|
|
849
|
+
*/
|
|
850
|
+
export declare const generateImage: (options: GenerateImageOptions) => Effect.Effect<GenerateImageResult, AiError>;
|
|
851
|
+
|
|
852
|
+
export declare interface GenerateImageOptions {
|
|
853
|
+
/** What to draw. */
|
|
854
|
+
readonly prompt: string;
|
|
855
|
+
/** Override the provider/model for a single call (defaults to `providerFromEnv()`).
|
|
856
|
+
* `anthropic` has no image model; use `openai` or `gateway`. */
|
|
857
|
+
readonly provider?: ProviderConfig;
|
|
858
|
+
/** Number of images to generate. Default 1 (provider-dependent). */
|
|
859
|
+
readonly n?: number;
|
|
860
|
+
/** Exact pixel size, `{width}x{height}` (mutually exclusive with `aspectRatio`
|
|
861
|
+
* per provider). */
|
|
862
|
+
readonly size?: `${number}x${number}`;
|
|
863
|
+
/** Aspect ratio, `{width}:{height}`. */
|
|
864
|
+
readonly aspectRatio?: `${number}:${number}`;
|
|
865
|
+
/** Provider-specific options forwarded to the SDK. */
|
|
866
|
+
readonly providerOptions?: AiProviderOptions;
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
export declare interface GenerateImageResult {
|
|
870
|
+
readonly images: ReadonlyArray<GeneratedImage>;
|
|
871
|
+
readonly usage?: MediaUsage;
|
|
872
|
+
/** Raw provider/gateway metadata — feed to `gatewayCostUsd(...)` for the
|
|
873
|
+
* gateway's real per-call cost (see {@link GenerateTextResult.providerMetadata}). */
|
|
874
|
+
readonly providerMetadata?: unknown;
|
|
875
|
+
}
|
|
876
|
+
|
|
877
|
+
export declare const generateObject: <A, I>(options: GenerateObjectOptions<A, I>) => Effect.Effect<GenerateObjectResult<A>, AiError>;
|
|
878
|
+
|
|
879
|
+
export declare interface GenerateObjectOptions<A, I> {
|
|
880
|
+
/** Effect Schema the model output must satisfy. Converted to JSON
|
|
881
|
+
* Schema for the SDK, then decoded back through this schema so the
|
|
882
|
+
* result carries branded types + refinements (not just JSON shape). */
|
|
883
|
+
readonly schema: Schema.Schema<A, I>;
|
|
884
|
+
readonly prompt: string;
|
|
885
|
+
readonly system?: string;
|
|
886
|
+
readonly provider?: ProviderConfig;
|
|
887
|
+
/** Ordered provider/model fallback chain — see {@link ProviderFallbacks}. A
|
|
888
|
+
* `decode` failure does NOT fall through (another provider won't fix a schema
|
|
889
|
+
* mismatch); only a `generation` failure does. */
|
|
890
|
+
readonly fallbacks?: ProviderFallbacks;
|
|
891
|
+
readonly maxTokens?: number;
|
|
892
|
+
/** Provider-specific options forwarded to the SDK (e.g. reasoning effort).
|
|
893
|
+
* See `AiProviderOptions`. */
|
|
894
|
+
readonly providerOptions?: AiProviderOptions;
|
|
895
|
+
}
|
|
896
|
+
|
|
897
|
+
export declare interface GenerateObjectResult<A> {
|
|
898
|
+
readonly object: A;
|
|
899
|
+
readonly usage: {
|
|
900
|
+
readonly inputTokens: number | undefined;
|
|
901
|
+
readonly outputTokens: number | undefined;
|
|
902
|
+
readonly totalTokens: number | undefined;
|
|
903
|
+
};
|
|
904
|
+
/** Raw provider/gateway metadata (see {@link GenerateTextResult.providerMetadata}):
|
|
905
|
+
* feed to `gatewayCostUsd` for the gateway's real per-call cost. */
|
|
906
|
+
readonly providerMetadata?: unknown;
|
|
907
|
+
}
|
|
908
|
+
|
|
909
|
+
/**
|
|
910
|
+
* Run an adaptive tool-calling loop AND constrain the final answer to an
|
|
911
|
+
* Effect Schema — the batch-agent analogue of `generateObject` that also gets
|
|
912
|
+
* tool access (the Convex agents' "call Jira tools on demand, then emit the
|
|
913
|
+
* structured summary" pattern).
|
|
914
|
+
*
|
|
915
|
+
* Mechanism: the AI SDK's `generateText` drives the LLM↔tool loop
|
|
916
|
+
* (`stopWhen: stepCountIs(maxSteps)`); `experimental_output: Output.object`
|
|
917
|
+
* makes the terminal step emit a JSON object constrained to the schema's
|
|
918
|
+
* JSON Schema. The SDK's parsed `experimental_output` is then re-decoded
|
|
919
|
+
* through the SAME Effect Schema, so brands/refinements hold and malformed
|
|
920
|
+
* output surfaces as `AiError({ reason: 'decode' })`.
|
|
921
|
+
*
|
|
922
|
+
* Tool bodies inherit the caller's services: the ambient Effect runtime is
|
|
923
|
+
* captured (`Effect.runtime`) and threaded into the tool context, so a tool
|
|
924
|
+
* may `yield* JiraService` and reach the layer provided at the
|
|
925
|
+
* action/workflow boundary.
|
|
926
|
+
*/
|
|
927
|
+
export declare const generateObjectWithTools: <A, I>(options: GenerateObjectWithToolsOptions<A, I>) => Effect.Effect<GenerateObjectResult<A>, AiError>;
|
|
928
|
+
|
|
929
|
+
export declare interface GenerateObjectWithToolsOptions<A, I> {
|
|
930
|
+
/** Effect Schema the FINAL object must satisfy. The model runs the tool
|
|
931
|
+
* loop, then emits one object matching this schema; it is decoded back
|
|
932
|
+
* through the schema so refinements/brands hold. */
|
|
933
|
+
readonly schema: Schema.Schema<A, I>;
|
|
934
|
+
readonly prompt: string;
|
|
935
|
+
readonly system?: string;
|
|
936
|
+
readonly provider?: ProviderConfig;
|
|
937
|
+
/** Ordered provider/model fallback chain — see {@link ProviderFallbacks}. */
|
|
938
|
+
readonly fallbacks?: ProviderFallbacks;
|
|
939
|
+
readonly maxTokens?: number;
|
|
940
|
+
/** Provider-specific options forwarded to the SDK (e.g. reasoning effort).
|
|
941
|
+
* See `AiProviderOptions`. */
|
|
942
|
+
readonly providerOptions?: AiProviderOptions;
|
|
943
|
+
/** Tools the model may call while reasoning toward the final object. The
|
|
944
|
+
* SDK drives the call→execute→feed-back loop up to `maxSteps`. Tool bodies
|
|
945
|
+
* reach the caller's services through the ambient runtime (see below). */
|
|
946
|
+
readonly tools: Record<string, AnyTool>;
|
|
947
|
+
/** Max LLM↔tool round-trips before the model must produce the object.
|
|
948
|
+
* Default 8. */
|
|
949
|
+
readonly maxSteps?: number;
|
|
950
|
+
/** Request context threaded into `*.tool.tsx` executor bodies. The caller's
|
|
951
|
+
* ambient Effect runtime is captured automatically and merged in, so tool
|
|
952
|
+
* bodies may `yield*` services (`JiraService`, the store layer, …). */
|
|
953
|
+
readonly toolContext?: ToolContext;
|
|
954
|
+
}
|
|
955
|
+
|
|
956
|
+
/**
|
|
957
|
+
* Synthesize speech audio from text. Returns the audio bytes + media type and
|
|
958
|
+
* provider metadata; provider failures surface as `AiError`.
|
|
959
|
+
*/
|
|
960
|
+
export declare const generateSpeech: (options: GenerateSpeechOptions) => Effect.Effect<GenerateSpeechResult, AiError>;
|
|
961
|
+
|
|
962
|
+
export declare interface GenerateSpeechOptions {
|
|
963
|
+
/** Text to speak. */
|
|
964
|
+
readonly text: string;
|
|
965
|
+
/** Override the provider/model (defaults to `providerFromEnv()`). `anthropic`
|
|
966
|
+
* has no speech model; use `openai` or `gateway`. */
|
|
967
|
+
readonly provider?: ProviderConfig;
|
|
968
|
+
/** Provider voice id. */
|
|
969
|
+
readonly voice?: string;
|
|
970
|
+
/** Desired audio container, e.g. `'mp3'` / `'wav'`. */
|
|
971
|
+
readonly outputFormat?: 'mp3' | 'wav' | (string & {});
|
|
972
|
+
/** Free-text delivery instructions, e.g. "speak slowly". */
|
|
973
|
+
readonly instructions?: string;
|
|
974
|
+
/** Speaking rate multiplier. */
|
|
975
|
+
readonly speed?: number;
|
|
976
|
+
/** ISO 639-1 language code (or `'auto'`). */
|
|
977
|
+
readonly language?: string;
|
|
978
|
+
/** Provider-specific options forwarded to the SDK. */
|
|
979
|
+
readonly providerOptions?: AiProviderOptions;
|
|
980
|
+
}
|
|
981
|
+
|
|
982
|
+
export declare interface GenerateSpeechResult {
|
|
983
|
+
readonly audio: GeneratedAudio;
|
|
984
|
+
/** The speech model spec reports no usage; present for surface parity. */
|
|
985
|
+
readonly usage?: MediaUsage;
|
|
986
|
+
readonly providerMetadata?: unknown;
|
|
987
|
+
}
|
|
988
|
+
|
|
989
|
+
/**
|
|
990
|
+
* Generate text from a single-turn prompt. Returns the model's response
|
|
991
|
+
* plus a usage tally; provider-specific extras (tool calls, finish reason)
|
|
992
|
+
* are dropped — a richer surface will follow when the framework grows
|
|
993
|
+
* dedicated *.agent.tsx primitives that need them.
|
|
994
|
+
*/
|
|
995
|
+
export declare const generateText: (options: GenerateTextOptions) => Effect.Effect<GenerateTextResult, AiError>;
|
|
996
|
+
|
|
997
|
+
export declare interface GenerateTextOptions {
|
|
998
|
+
/** The user prompt. The SDK auto-wraps it as the single user message. */
|
|
999
|
+
readonly prompt: string;
|
|
1000
|
+
/** Optional system message. Steers the model independently of the prompt. */
|
|
1001
|
+
readonly system?: string;
|
|
1002
|
+
/** Override the provider/model for a single call. */
|
|
1003
|
+
readonly provider?: ProviderConfig;
|
|
1004
|
+
/** Ordered provider/model fallback chain — see {@link ProviderFallbacks}. */
|
|
1005
|
+
readonly fallbacks?: ProviderFallbacks;
|
|
1006
|
+
/** Soft ceiling on output tokens. Provider-dependent. */
|
|
1007
|
+
readonly maxTokens?: number;
|
|
1008
|
+
/** Provider-specific options forwarded to the SDK (e.g. reasoning effort).
|
|
1009
|
+
* See `AiProviderOptions`. */
|
|
1010
|
+
readonly providerOptions?: AiProviderOptions;
|
|
1011
|
+
}
|
|
1012
|
+
|
|
1013
|
+
export declare interface GenerateTextResult {
|
|
1014
|
+
readonly text: string;
|
|
1015
|
+
readonly usage: {
|
|
1016
|
+
readonly inputTokens: number | undefined;
|
|
1017
|
+
readonly outputTokens: number | undefined;
|
|
1018
|
+
readonly totalTokens: number | undefined;
|
|
1019
|
+
};
|
|
1020
|
+
/** Raw provider/gateway metadata from the SDK result. The Vercel AI Gateway
|
|
1021
|
+
* reports the ACTUAL per-call cost here (`gateway.cost`); pass this to
|
|
1022
|
+
* `gatewayCostUsd(...)` → `recordAiUsage({ actualCostUsd })` to record the
|
|
1023
|
+
* real charge for a gateway-routed model the static price table can't price.
|
|
1024
|
+
* `undefined` for direct providers / the mock. */
|
|
1025
|
+
readonly providerMetadata?: unknown;
|
|
1026
|
+
}
|
|
1027
|
+
|
|
1028
|
+
/**
|
|
1029
|
+
* Generate a video from a text prompt (optionally conditioned on reference
|
|
1030
|
+
* images/videos). Returns the URL the provider yields OR inline bytes, plus
|
|
1031
|
+
* provider metadata; provider failures surface as `AiError`.
|
|
1032
|
+
*
|
|
1033
|
+
* Unlike image/speech, this calls the resolved `VideoModelV4` DIRECTLY rather
|
|
1034
|
+
* than the SDK's `experimental_generateVideo`. That helper force-DOWNLOADS a
|
|
1035
|
+
* URL-typed video into bytes (a potentially multi-GB fetch), discarding the
|
|
1036
|
+
* URL; calling the model spec directly lets us return whichever form the
|
|
1037
|
+
* provider produced (`{ url }` or `{ data }`), matching {@link GeneratedVideo}.
|
|
1038
|
+
* No SDK-less gateway fallback is needed — the installed `ai` ships a video
|
|
1039
|
+
* primitive and the gateway provider exposes a native `.video()` model.
|
|
1040
|
+
*/
|
|
1041
|
+
export declare const generateVideo: (options: GenerateVideoOptions) => Effect.Effect<GenerateVideoResult, AiError>;
|
|
1042
|
+
|
|
1043
|
+
export declare interface GenerateVideoInputs {
|
|
1044
|
+
/** Reference / starting-frame image URL (image-to-video). */
|
|
1045
|
+
readonly image?: string;
|
|
1046
|
+
/** Reference image/video URLs (reference-to-video). */
|
|
1047
|
+
readonly references?: ReadonlyArray<string>;
|
|
1048
|
+
}
|
|
1049
|
+
|
|
1050
|
+
export declare interface GenerateVideoOptions {
|
|
1051
|
+
/** What to animate. */
|
|
1052
|
+
readonly prompt: string;
|
|
1053
|
+
/** Override the provider/model (defaults to `providerFromEnv()`). Only the
|
|
1054
|
+
* Vercel AI Gateway (`gateway`) ships a video model among the wired
|
|
1055
|
+
* providers; `openai` / `anthropic` raise an `AiError`. */
|
|
1056
|
+
readonly provider?: ProviderConfig;
|
|
1057
|
+
readonly params?: GenerateVideoParams;
|
|
1058
|
+
readonly inputs?: GenerateVideoInputs;
|
|
1059
|
+
/** Provider-specific options forwarded to the model. */
|
|
1060
|
+
readonly providerOptions?: AiProviderOptions;
|
|
1061
|
+
}
|
|
1062
|
+
|
|
1063
|
+
export declare interface GenerateVideoParams {
|
|
1064
|
+
readonly aspectRatio?: `${number}:${number}`;
|
|
1065
|
+
readonly durationSeconds?: number;
|
|
1066
|
+
readonly resolution?: `${number}x${number}`;
|
|
1067
|
+
readonly fps?: number;
|
|
1068
|
+
readonly seed?: number;
|
|
1069
|
+
}
|
|
1070
|
+
|
|
1071
|
+
export declare interface GenerateVideoResult {
|
|
1072
|
+
readonly video: GeneratedVideo;
|
|
1073
|
+
/** The video model spec reports no usage; present for surface parity. */
|
|
1074
|
+
readonly usage?: MediaUsage;
|
|
1075
|
+
readonly providerMetadata?: unknown;
|
|
1076
|
+
}
|
|
1077
|
+
|
|
1078
|
+
/** The currently-installed global middleware stack. */
|
|
1079
|
+
export declare const getAiMiddleware: () => ReadonlyArray<AiMiddleware>;
|
|
1080
|
+
|
|
1081
|
+
/**
|
|
1082
|
+
* Fetch the live model catalog from the Vercel AI Gateway. Uses the default
|
|
1083
|
+
* `gateway` (reads `AI_GATEWAY_API_KEY`) unless an explicit `apiKey`/`baseURL`
|
|
1084
|
+
* is given. Optionally filter by modality (the picker usually wants `language`).
|
|
1085
|
+
* `gatewayProvider` is injectable for tests.
|
|
1086
|
+
*/
|
|
1087
|
+
export declare const getAvailableModels: (opts?: {
|
|
1088
|
+
readonly apiKey?: string;
|
|
1089
|
+
readonly baseURL?: string;
|
|
1090
|
+
readonly modality?: ModelModality;
|
|
1091
|
+
readonly gatewayProvider?: {
|
|
1092
|
+
getAvailableModels: () => Promise<{
|
|
1093
|
+
models: GatewayLanguageModelEntry[];
|
|
1094
|
+
}>;
|
|
1095
|
+
};
|
|
1096
|
+
}) => Promise<ReadonlyArray<GatewayModelInfo>>;
|
|
1097
|
+
|
|
1098
|
+
/** Load a thread's messages in chronological order (for reload / resume). */
|
|
1099
|
+
export declare const getMessages: (store: DataStore, threadId: string) => Effect.Effect<ReadonlyArray<AgentMessage>>;
|
|
1100
|
+
|
|
1101
|
+
/** The currently-installed override (or null). */
|
|
1102
|
+
export declare const getProviderOverride: () => ProviderConfig | null;
|
|
1103
|
+
|
|
1104
|
+
export declare const getThread: (store: DataStore, threadId: string) => Effect.Effect<AgentThread | null>;
|
|
1105
|
+
|
|
1106
|
+
/** FNV-1a-seeded deterministic float vector in [-1, 1], length `dim`. */
|
|
1107
|
+
export declare const hashToVector: (text: string, dim: number) => number[];
|
|
1108
|
+
|
|
1109
|
+
/**
|
|
1110
|
+
* Convert a persisted thread (`agent_messages` rows) into the model-message
|
|
1111
|
+
* list that gives an assistant turn its CONVERSATION MEMORY. Without this the
|
|
1112
|
+
* model only ever sees the latest prompt + system — it can't remember earlier
|
|
1113
|
+
* turns. Keeps user/assistant text turns (skips the live streaming row, empty
|
|
1114
|
+
* rows, and system/tool rows — `system` is passed separately), ordered by the
|
|
1115
|
+
* thread's monotonic `order`/`stepOrder`.
|
|
1116
|
+
*/
|
|
1117
|
+
export declare const historyToModelMessages: (history: ReadonlyArray<AgentMessage>) => ModelMessage[];
|
|
1118
|
+
|
|
1119
|
+
/** Discovery predicate for `*.agent.tsx` exports. */
|
|
1120
|
+
export declare const isAgentDescriptor: (x: unknown) => x is AnyAgentDescriptor;
|
|
1121
|
+
|
|
1122
|
+
/** Discovery predicate for `*.agent.server.tsx` default exports. */
|
|
1123
|
+
export declare const isAgentExecutor: (x: unknown) => x is AnyAgentExecutor;
|
|
1124
|
+
|
|
1125
|
+
/** Whether a stream with this id is currently registered (in flight). */
|
|
1126
|
+
export declare const isStreamActive: (streamId: string) => boolean;
|
|
1127
|
+
|
|
1128
|
+
/** Is `x` a `defineTool(...)` result? */
|
|
1129
|
+
export declare const isTool: (x: unknown) => x is AnyTool;
|
|
1130
|
+
|
|
1131
|
+
/** The synthesized `<name>.messages` body — load the thread's messages. */
|
|
1132
|
+
export declare const loadAgentMessages: (store: DataStore, threadId: string) => Effect.Effect<ReadonlyArray<AgentMessage>>;
|
|
1133
|
+
|
|
1134
|
+
/**
|
|
1135
|
+
* A middleware that logs every generate/stream call's model (and token usage
|
|
1136
|
+
* for generate). Dependency-free demonstration of the seam and a useful dev
|
|
1137
|
+
* default. Pass `log` to route into `@voltro/logger` or a metrics sink instead
|
|
1138
|
+
* of `console`.
|
|
1139
|
+
*
|
|
1140
|
+
* ```ts
|
|
1141
|
+
* setAiMiddleware(loggingMiddleware())
|
|
1142
|
+
* ```
|
|
1143
|
+
*/
|
|
1144
|
+
export declare const loggingMiddleware: (opts?: {
|
|
1145
|
+
readonly log?: (event: AiCallLog) => void;
|
|
1146
|
+
}) => AiMiddleware;
|
|
1147
|
+
|
|
1148
|
+
/** Token tally, mirroring `GenerateTextResult['usage']`. Only `generateImage`
|
|
1149
|
+
* reports one (the speech + video model specs carry no usage), which is why
|
|
1150
|
+
* it's optional on every media result. */
|
|
1151
|
+
export declare interface MediaUsage {
|
|
1152
|
+
readonly inputTokens: number | undefined;
|
|
1153
|
+
readonly outputTokens: number | undefined;
|
|
1154
|
+
readonly totalTokens: number | undefined;
|
|
1155
|
+
}
|
|
1156
|
+
|
|
1157
|
+
/**
|
|
1158
|
+
* Single-process resumable-stream store. Holds each stream's log in memory —
|
|
1159
|
+
* perfect for dev, tests, and single-node self-host. A multi-node deployment
|
|
1160
|
+
* needs `dataStoreResumableStreamStore` (or a Redis backend) so every node
|
|
1161
|
+
* shares the log and the producer election.
|
|
1162
|
+
*/
|
|
1163
|
+
export declare const memoryResumableStreamStore: () => ResumableStreamStore;
|
|
1164
|
+
|
|
1165
|
+
/**
|
|
1166
|
+
* One structured part of an assistant message. Either streamed assistant text
|
|
1167
|
+
* or a record of a tool call + its result. Stored in `agent_messages.parts`
|
|
1168
|
+
* and rendered by the frontend (`toUIMessages`).
|
|
1169
|
+
*/
|
|
1170
|
+
export declare type MessagePart = {
|
|
1171
|
+
readonly type: 'text';
|
|
1172
|
+
readonly text: string;
|
|
1173
|
+
} | {
|
|
1174
|
+
readonly type: 'tool';
|
|
1175
|
+
readonly toolCallId: string;
|
|
1176
|
+
readonly toolName: string;
|
|
1177
|
+
/** 'input-available' once the model emitted args; 'output-available' once the tool ran. */
|
|
1178
|
+
readonly state: 'input-available' | 'output-available' | 'output-error';
|
|
1179
|
+
readonly input?: unknown;
|
|
1180
|
+
readonly output?: unknown;
|
|
1181
|
+
readonly errorText?: string;
|
|
1182
|
+
}
|
|
1183
|
+
/** The model's reasoning ("thinking"), separate from the answer text — a UI
|
|
1184
|
+
* renders it in a collapsible pane. Accumulated like `text`. */
|
|
1185
|
+
| {
|
|
1186
|
+
readonly type: 'reasoning';
|
|
1187
|
+
readonly text: string;
|
|
1188
|
+
}
|
|
1189
|
+
/** A cited source (RAG / web-search grounding). `url` variant carries `url`;
|
|
1190
|
+
* `document` variant carries `mediaType`/`filename`. Rendered as a citation
|
|
1191
|
+
* chip / footnote. */
|
|
1192
|
+
| {
|
|
1193
|
+
readonly type: 'source';
|
|
1194
|
+
readonly sourceType: 'url' | 'document';
|
|
1195
|
+
readonly id: string;
|
|
1196
|
+
readonly url?: string;
|
|
1197
|
+
readonly title?: string;
|
|
1198
|
+
readonly mediaType?: string;
|
|
1199
|
+
readonly filename?: string;
|
|
1200
|
+
}
|
|
1201
|
+
/** An inline file the model produced (usually an image): IANA `mediaType` +
|
|
1202
|
+
* base64 `data`. A UI renders `image/*` inline. */
|
|
1203
|
+
| {
|
|
1204
|
+
readonly type: 'file';
|
|
1205
|
+
readonly mediaType: string;
|
|
1206
|
+
readonly data: string;
|
|
1207
|
+
};
|
|
1208
|
+
|
|
1209
|
+
/** A scripted inline file the mock model emits on a turn (usually an image).
|
|
1210
|
+
* `data` is base64 (a short ascii string works for assertions). */
|
|
1211
|
+
export declare interface MockFile {
|
|
1212
|
+
readonly mediaType: string;
|
|
1213
|
+
readonly data: string;
|
|
1214
|
+
}
|
|
1215
|
+
|
|
1216
|
+
/** A consumable sequence of model turns, plus a recorder for asserting on
|
|
1217
|
+
* what the model was asked. Each `doGenerate`/`doStream` advances the
|
|
1218
|
+
* cursor by one turn; running past the end repeats the last turn's text
|
|
1219
|
+
* (or empties) so a loop that over-steps doesn't hang. */
|
|
1220
|
+
export declare class MockScript {
|
|
1221
|
+
private readonly turns;
|
|
1222
|
+
private cursor;
|
|
1223
|
+
readonly calls: Array<{
|
|
1224
|
+
readonly prompt: unknown;
|
|
1225
|
+
readonly tools: ReadonlyArray<string>;
|
|
1226
|
+
}>;
|
|
1227
|
+
constructor(turns: ReadonlyArray<MockTurn>);
|
|
1228
|
+
record(prompt: unknown, tools: ReadonlyArray<string>): void;
|
|
1229
|
+
next(): MockTurn;
|
|
1230
|
+
reset(): void;
|
|
1231
|
+
}
|
|
1232
|
+
|
|
1233
|
+
/** A scripted citation the mock model emits on a turn (RAG grounding). */
|
|
1234
|
+
export declare interface MockSource {
|
|
1235
|
+
readonly sourceType?: 'url' | 'document';
|
|
1236
|
+
readonly id?: string;
|
|
1237
|
+
readonly url?: string;
|
|
1238
|
+
readonly title?: string;
|
|
1239
|
+
readonly mediaType?: string;
|
|
1240
|
+
readonly filename?: string;
|
|
1241
|
+
}
|
|
1242
|
+
|
|
1243
|
+
/** A scripted tool call the mock model emits on a turn. */
|
|
1244
|
+
export declare interface MockToolCall {
|
|
1245
|
+
readonly name: string;
|
|
1246
|
+
readonly input: unknown;
|
|
1247
|
+
}
|
|
1248
|
+
|
|
1249
|
+
/** One model turn. A turn can carry text (streamed word-by-word), scripted
|
|
1250
|
+
* tool calls (which trigger the SDK's tool loop), cited sources, inline files,
|
|
1251
|
+
* or an error. A turn with tool calls + no text drives one LLM↔tool
|
|
1252
|
+
* round-trip. */
|
|
1253
|
+
export declare interface MockTurn {
|
|
1254
|
+
readonly text?: string;
|
|
1255
|
+
/** Reasoning ("thinking") the mock streams before the answer text. */
|
|
1256
|
+
readonly reasoning?: string;
|
|
1257
|
+
readonly toolCalls?: ReadonlyArray<MockToolCall>;
|
|
1258
|
+
readonly sources?: ReadonlyArray<MockSource>;
|
|
1259
|
+
readonly files?: ReadonlyArray<MockFile>;
|
|
1260
|
+
readonly error?: {
|
|
1261
|
+
readonly message: string;
|
|
1262
|
+
};
|
|
1263
|
+
}
|
|
1264
|
+
|
|
1265
|
+
/**
|
|
1266
|
+
* Built-in published list prices (USD per 1M tokens) for the major providers.
|
|
1267
|
+
*
|
|
1268
|
+
* ⚠️ POINT-IN-TIME DEFAULTS — these are public LIST prices as of **January
|
|
1269
|
+
* 2026** and WILL drift as providers re-price. They are a sane default for the
|
|
1270
|
+
* budget guard + cost dashboard, NOT a contract. Override them without editing
|
|
1271
|
+
* the framework three ways:
|
|
1272
|
+
* - per call: `estimateCostUsd(usage, { model, price })` / `recordAiUsage({ price })`
|
|
1273
|
+
* - app-wide: `setModelPricing({ 'my-model': { inputPer1M, outputPer1M } })`
|
|
1274
|
+
* (merged OVER these defaults — a user entry wins)
|
|
1275
|
+
* - authoritative: for a GATEWAY-routed model, pass the gateway's REPORTED
|
|
1276
|
+
* per-call cost via `actualCostUsd: gatewayCostUsd(meta)`
|
|
1277
|
+
* (`costSource: 'gateway'`), which beats any static rate here.
|
|
1278
|
+
*
|
|
1279
|
+
* The `mock` provider + any model absent from the merged map price at zero
|
|
1280
|
+
* (never throws — cost accounting must not break a call).
|
|
1281
|
+
*
|
|
1282
|
+
* Covers the framework's first-class DIRECT providers — `anthropic` (`claude-*`)
|
|
1283
|
+
* and `openai` (`gpt-*`) — plus the common GATEWAY creators reachable by their
|
|
1284
|
+
* bare model id (`google` Gemini, and the same OpenAI/Anthropic ids the gateway
|
|
1285
|
+
* routes with a `creator/` prefix, matched below by their bare id after the `/`).
|
|
1286
|
+
*/
|
|
1287
|
+
export declare const MODEL_PRICING_DEFAULTS: Record<string, ModelPrice>;
|
|
1288
|
+
|
|
1289
|
+
export declare type ModelModality = 'language' | 'embedding' | 'image' | 'video' | 'realtime' | 'reranking' | 'speech' | 'transcription' | 'unknown';
|
|
1290
|
+
|
|
1291
|
+
export declare interface ModelPrice {
|
|
1292
|
+
/** USD per 1,000,000 input (prompt) tokens. */
|
|
1293
|
+
readonly inputPer1M: number;
|
|
1294
|
+
/** USD per 1,000,000 output (completion) tokens. */
|
|
1295
|
+
readonly outputPer1M: number;
|
|
1296
|
+
}
|
|
1297
|
+
|
|
1298
|
+
/** The current merged price map (defaults + installed overrides). Overrides win. */
|
|
1299
|
+
export declare const modelPricing: () => Record<string, ModelPrice>;
|
|
1300
|
+
|
|
1301
|
+
/**
|
|
1302
|
+
* Wrap a `streamText` `Stream<AgentEvent>` with the same `voltro.ai.call` span +
|
|
1303
|
+
* metrics. The stream never fails — usage arrives on the terminal `done` event,
|
|
1304
|
+
* error status on a terminal `error` event — so the metrics are recorded from a
|
|
1305
|
+
* `Stream.tap` on the terminal event, and the whole stream is spanned via
|
|
1306
|
+
* `Stream.withSpan`. A `cancelled` error counts as an error (non-`ok`) call.
|
|
1307
|
+
*/
|
|
1308
|
+
export declare const observeStream: (attributes: AiCallAttributes, stream: Stream.Stream<AgentEvent>) => Stream.Stream<AgentEvent>;
|
|
1309
|
+
|
|
1310
|
+
export declare type OpenAIModel = 'gpt-5.5' | 'gpt-4o' | 'gpt-4o-mini' | (string & {});
|
|
1311
|
+
|
|
1312
|
+
/** Does this tag pass the allow/deny policy? deny wins; allow (when set) gates. */
|
|
1313
|
+
export declare const passesPolicy: (tag: string, policy: AppToolPolicy) => boolean;
|
|
1314
|
+
|
|
1315
|
+
/**
|
|
1316
|
+
* Patch the streaming row's `parts` (the live snapshot). Call-site throttles
|
|
1317
|
+
* this to ~100ms so the subscription doesn't fan out one update per token.
|
|
1318
|
+
* Mirrors `content` to the concatenated text so the durable row stays readable
|
|
1319
|
+
* even if a consumer ignores `parts`.
|
|
1320
|
+
*/
|
|
1321
|
+
export declare const patchStreamingMessage: (store: DataStore, id: string, parts: ReadonlyArray<MessagePart>) => Effect.Effect<void>;
|
|
1322
|
+
|
|
1323
|
+
/**
|
|
1324
|
+
* The effective {@link ModelPrice} for a model id, or `undefined` if unknown.
|
|
1325
|
+
* Looks up the merged map (overrides over defaults); a gateway `creator/model`
|
|
1326
|
+
* id also matches on its bare `model` segment, so `openai/gpt-4o` prices like
|
|
1327
|
+
* `gpt-4o` when the caller didn't pass a `price` / `actualCostUsd`.
|
|
1328
|
+
*/
|
|
1329
|
+
export declare const priceForModel: (model: string) => ModelPrice | undefined;
|
|
1330
|
+
|
|
1331
|
+
/**
|
|
1332
|
+
* A resolved provider choice — a DISCRIMINATED UNION on `name`. Each provider
|
|
1333
|
+
* only carries the fields that apply to it: the mock-only `mockText` / `script`
|
|
1334
|
+
* cannot appear on a real provider, and a real provider always carries a
|
|
1335
|
+
* `model` typed to that provider's ids. `apiKey` / `baseURL` switch
|
|
1336
|
+
* `resolveModel` to a per-config provider instance (per-agent key / BYOK);
|
|
1337
|
+
* omit them to fall back to the provider's standard env var (OPENAI_API_KEY /
|
|
1338
|
+
* ANTHROPIC_API_KEY / AI_GATEWAY_API_KEY).
|
|
1339
|
+
*/
|
|
1340
|
+
export declare type ProviderConfig = {
|
|
1341
|
+
readonly name: 'mock';
|
|
1342
|
+
/** Optional for mock — defaults to `'mock'`. */
|
|
1343
|
+
readonly model?: string;
|
|
1344
|
+
/** The exact text the mock returns (instead of echoing the prompt).
|
|
1345
|
+
* Lets tests/demos drive `generateObject` with canned JSON. */
|
|
1346
|
+
readonly mockText?: string;
|
|
1347
|
+
/** A multi-turn script. Takes precedence over `mockText` — each
|
|
1348
|
+
* `doGenerate`/`doStream` consumes the next turn, so the SDK's tool
|
|
1349
|
+
* loop runs deterministically. */
|
|
1350
|
+
readonly script?: MockScript;
|
|
1351
|
+
/** Provider metadata the mock attaches to its `doGenerate` result — lets a
|
|
1352
|
+
* test exercise the gateway-cost path (`gatewayCostUsd`) deterministically
|
|
1353
|
+
* by faking `{ gateway: { cost } }`. Also surfaced by the mock speech +
|
|
1354
|
+
* video media models. */
|
|
1355
|
+
readonly providerMetadata?: AiProviderOptions;
|
|
1356
|
+
/** Canned output for the media models (`resolveImageModel` /
|
|
1357
|
+
* `resolveSpeechModel` / `resolveVideoModel`). Every field is optional and
|
|
1358
|
+
* falls back to a deterministic default, so `useMockAi` yields valid media
|
|
1359
|
+
* without configuration; set a field to assert on exact bytes / URL. */
|
|
1360
|
+
readonly mockMedia?: {
|
|
1361
|
+
/** base64 image payload `generateImage` decodes to bytes (default: a
|
|
1362
|
+
* 1×1 PNG header, so media-type detection resolves to `image/png`). */
|
|
1363
|
+
readonly imageBase64?: string;
|
|
1364
|
+
/** base64 audio payload `generateSpeech` decodes to bytes. */
|
|
1365
|
+
readonly audioBase64?: string;
|
|
1366
|
+
/** URL the mock video model yields (the default branch — most video
|
|
1367
|
+
* providers return a URL rather than inline bytes). */
|
|
1368
|
+
readonly videoUrl?: string;
|
|
1369
|
+
/** base64 the mock video model yields INSTEAD of a URL — exercises the
|
|
1370
|
+
* bytes branch of `generateVideo`. Takes precedence over `videoUrl`. */
|
|
1371
|
+
readonly videoBase64?: string;
|
|
1372
|
+
};
|
|
1373
|
+
} | {
|
|
1374
|
+
readonly name: 'anthropic';
|
|
1375
|
+
readonly model: AnthropicModel;
|
|
1376
|
+
readonly apiKey?: string;
|
|
1377
|
+
readonly baseURL?: string;
|
|
1378
|
+
} | {
|
|
1379
|
+
readonly name: 'openai';
|
|
1380
|
+
readonly model: OpenAIModel;
|
|
1381
|
+
readonly apiKey?: string;
|
|
1382
|
+
readonly baseURL?: string;
|
|
1383
|
+
} | {
|
|
1384
|
+
readonly name: 'gateway';
|
|
1385
|
+
/** A `creator/model` id, e.g. 'openai/gpt-5.5'. */
|
|
1386
|
+
readonly model: GatewayModel;
|
|
1387
|
+
readonly apiKey?: string;
|
|
1388
|
+
readonly baseURL?: string;
|
|
1389
|
+
};
|
|
1390
|
+
|
|
1391
|
+
/**
|
|
1392
|
+
* An ordered provider/model fallback chain. When a call FAILS on the primary
|
|
1393
|
+
* provider (`provider ?? providerFromEnv()`) — after that provider's OWN
|
|
1394
|
+
* SDK-level retries — the call re-runs against `fallbacks[0]`, then
|
|
1395
|
+
* `fallbacks[1]`, … until one succeeds. Exhausting every option surfaces the
|
|
1396
|
+
* LAST provider's typed error (an `AiError({ reason: 'generation' })`). Only a
|
|
1397
|
+
* `generation` failure (the provider call itself) falls through — a `decode`
|
|
1398
|
+
* failure (the model answered but the output didn't satisfy the schema) is a
|
|
1399
|
+
* prompt/schema problem another provider won't fix, so it surfaces immediately.
|
|
1400
|
+
* An empty / absent chain leaves behavior unchanged.
|
|
1401
|
+
*/
|
|
1402
|
+
export declare type ProviderFallbacks = ReadonlyArray<ProviderConfig>;
|
|
1403
|
+
|
|
1404
|
+
/**
|
|
1405
|
+
* Read the provider config. The process-global override (set by `useMockAi`)
|
|
1406
|
+
* wins over env. Otherwise honours:
|
|
1407
|
+
* AI_PROVIDER=mock|anthropic|openai|gateway (default: mock)
|
|
1408
|
+
* AI_MODEL=<model-id> (default per provider: 'claude-opus-4-8' for
|
|
1409
|
+
* anthropic, 'gpt-5.5' for openai, 'mock' for mock;
|
|
1410
|
+
* REQUIRED for gateway — a `creator/model` id)
|
|
1411
|
+
*
|
|
1412
|
+
* Provider keys are read by the underlying AI SDK packages from their standard
|
|
1413
|
+
* env vars: ANTHROPIC_API_KEY, OPENAI_API_KEY, AI_GATEWAY_API_KEY.
|
|
1414
|
+
*/
|
|
1415
|
+
export declare const providerFromEnv: (env?: Record<string, string | undefined>) => ProviderConfig;
|
|
1416
|
+
|
|
1417
|
+
export declare type ProviderName = 'mock' | 'anthropic' | 'openai' | 'gateway';
|
|
1418
|
+
|
|
1419
|
+
/**
|
|
1420
|
+
* Price a usage tally and write a ledger row. Returns the computed
|
|
1421
|
+
* `CostBreakdown` so the caller can surface the cost without a re-query.
|
|
1422
|
+
*/
|
|
1423
|
+
export declare const recordAiUsage: (store: DataStore, record: AiUsageRecordInput) => Effect.Effect<CostBreakdown>;
|
|
1424
|
+
|
|
1425
|
+
/**
|
|
1426
|
+
* Redis-backed resumable-stream store — the production "resumable via
|
|
1427
|
+
* Redis-Replay" path. The append-only log is a Redis LIST per `streamId`
|
|
1428
|
+
* (`RPUSH` to append, `LRANGE afterSeq -1` to read the tail past a cursor — the
|
|
1429
|
+
* list index lines up with the 1-based `seq` because exactly ONE producer
|
|
1430
|
+
* appends, elected by `claimProducer` = `SET … NX`). Multi-node correct + the
|
|
1431
|
+
* fastest backend; TTL (default 1h) evicts finished/abandoned streams without a
|
|
1432
|
+
* sweep. A drop-in for `dataStoreResumableStreamStore` against the same
|
|
1433
|
+
* `ResumableStreamStore` interface.
|
|
1434
|
+
*/
|
|
1435
|
+
export declare const redisResumableStreamStore: (redis: ResumableRedis, opts?: {
|
|
1436
|
+
readonly keyPrefix?: string;
|
|
1437
|
+
readonly ttlSeconds?: number;
|
|
1438
|
+
}) => ResumableStreamStore;
|
|
1439
|
+
|
|
1440
|
+
/**
|
|
1441
|
+
* Register (or reuse) the `AbortController` for `streamId`. `streamText` calls
|
|
1442
|
+
* this when given a `streamId`; it threads the returned controller's `signal`
|
|
1443
|
+
* into the SDK call. Re-registering an id that is still in flight returns the
|
|
1444
|
+
* SAME controller (so a pre-registered-then-aborted id is honoured) — keep
|
|
1445
|
+
* `streamId` unique per logical stream.
|
|
1446
|
+
*/
|
|
1447
|
+
export declare const registerStream: (streamId: string) => AbortController;
|
|
1448
|
+
|
|
1449
|
+
/**
|
|
1450
|
+
* Gate an AI call against a per-tenant USD budget with an ATOMIC reservation —
|
|
1451
|
+
* the cap precedent is billing's `requireEntitlement` / storage's
|
|
1452
|
+
* `consumeUsage`. Fails with `AiBudgetExceeded` (typed, client-marshalable)
|
|
1453
|
+
* when `reserved + addUsd` would exceed `limitUsd`. Call it BEFORE the provider
|
|
1454
|
+
* call, estimating `addUsd` from the prompt; record the actual cost with
|
|
1455
|
+
* `recordAiUsage` after (for the spend dashboard).
|
|
1456
|
+
*
|
|
1457
|
+
* Cross-replica-safe WITHOUT a `SELECT … FOR UPDATE` (the DataStore contract
|
|
1458
|
+
* exposes no row lock): a bounded compare-and-set loop over the single
|
|
1459
|
+
* `_voltro_ai_budget` counter row for the bucket. Read the counter, decide,
|
|
1460
|
+
* then EITHER conditionally `updateMany` the row only if the reservation we
|
|
1461
|
+
* read still holds (`affected === 1` ⇒ we won) OR `insertIgnore` the first row
|
|
1462
|
+
* (ON CONFLICT over the UNIQUE `bucket`). A lost CAS / ignored insert means a
|
|
1463
|
+
* concurrent reservation moved the counter → retry. So N concurrent calls
|
|
1464
|
+
* reserve exactly the budgeted amount — no overshoot — where the old
|
|
1465
|
+
* check-then-act SUM admitted all of them.
|
|
1466
|
+
*
|
|
1467
|
+
* NB the reservation is optimistic: it does NOT auto-release if the provider
|
|
1468
|
+
* call later fails (like billing's consume). For a rolling budget that's the
|
|
1469
|
+
* correct conservative bound; a rare failed-call over-reservation ages out with
|
|
1470
|
+
* the window. Pass `addUsd: 0` to check-only (no reservation) — useful for a UI
|
|
1471
|
+
* pre-flight where you don't want to consume headroom.
|
|
1472
|
+
*/
|
|
1473
|
+
export declare const requireAiBudget: (store: DataStore, opts: {
|
|
1474
|
+
readonly tenantId?: string | null;
|
|
1475
|
+
readonly limitUsd: number;
|
|
1476
|
+
readonly addUsd?: number;
|
|
1477
|
+
readonly since?: Date;
|
|
1478
|
+
}) => Effect.Effect<void, AiBudgetExceeded>;
|
|
1479
|
+
|
|
1480
|
+
/**
|
|
1481
|
+
* Rerank `documents` by relevance to `query`, highest score first. Returns
|
|
1482
|
+
* each surviving document with its relevance score; truncated to `topN`
|
|
1483
|
+
* when set.
|
|
1484
|
+
*
|
|
1485
|
+
* ```ts
|
|
1486
|
+
* const top = yield* rerank({ query: 'deploy', documents: rows, getText: (r) => r.body, topN: 5 })
|
|
1487
|
+
* ```
|
|
1488
|
+
*/
|
|
1489
|
+
export declare const rerank: <T>(options: RerankOptions<T>) => Effect.Effect<ReadonlyArray<RerankResult<T>>, AiError>;
|
|
1490
|
+
|
|
1491
|
+
export declare interface RerankOptions<T> {
|
|
1492
|
+
readonly query: string;
|
|
1493
|
+
readonly documents: ReadonlyArray<T>;
|
|
1494
|
+
/** Extract the text to score from each document. Default: identity for
|
|
1495
|
+
* `string[]`; required for non-string document shapes. */
|
|
1496
|
+
readonly getText?: (doc: T) => string;
|
|
1497
|
+
/** Rerank model id. e.g. 'rerank-english-v3.0' (cohere) / 'rerank-2' (voyage). */
|
|
1498
|
+
readonly model?: string;
|
|
1499
|
+
/** Provider. Default 'mock' (lexical overlap; deterministic). */
|
|
1500
|
+
readonly provider?: 'mock' | 'cohere' | 'voyage';
|
|
1501
|
+
/** Return only the top-N after reranking. Default: all. */
|
|
1502
|
+
readonly topN?: number;
|
|
1503
|
+
}
|
|
1504
|
+
|
|
1505
|
+
export declare interface RerankResult<T> {
|
|
1506
|
+
readonly document: T;
|
|
1507
|
+
readonly score: number;
|
|
1508
|
+
}
|
|
1509
|
+
|
|
1510
|
+
/** Resolve the provider config for a given decoded input. Supports a static
|
|
1511
|
+
* config OR a `(input) => ProviderConfig | undefined` selector (model picker /
|
|
1512
|
+
* cost routing). `undefined` → fall back to env (`providerFromEnv`). */
|
|
1513
|
+
export declare const resolveAgentModel: (exec: AnyAgentExecutor, input: unknown) => ProviderConfig | undefined;
|
|
1514
|
+
|
|
1515
|
+
/**
|
|
1516
|
+
* Resolve an {@link EmbeddingProviderConfig} to an AI-SDK
|
|
1517
|
+
* `EmbeddingModelV4`. The mock provider is synchronous; real providers are
|
|
1518
|
+
* resolved lazily (server-only dynamic import) so the provider package is
|
|
1519
|
+
* never bundled for the browser and is an optional, user-installed
|
|
1520
|
+
* dependency. A missing package surfaces a clear, actionable error.
|
|
1521
|
+
*/
|
|
1522
|
+
export declare const resolveEmbeddingModel: (config: EmbeddingProviderConfig) => Promise<EmbeddingModelV4>;
|
|
1523
|
+
|
|
1524
|
+
export declare const resolveImageModel: (config: ProviderConfig) => ImageModelV4;
|
|
1525
|
+
|
|
1526
|
+
export declare const resolveModel: (config: ProviderConfig, middleware?: AiMiddleware | ReadonlyArray<AiMiddleware>) => LanguageModelV4;
|
|
1527
|
+
|
|
1528
|
+
export declare const resolveSpeechModel: (config: ProviderConfig) => SpeechModelV4;
|
|
1529
|
+
|
|
1530
|
+
/** Resolve the system prompt for a given decoded input. */
|
|
1531
|
+
export declare const resolveSystem: (exec: AnyAgentExecutor, input: unknown) => string | undefined;
|
|
1532
|
+
|
|
1533
|
+
/**
|
|
1534
|
+
* Wire a `*.tool.tsx` module's DEFAULT-EXPORT handler onto its `defineTool`
|
|
1535
|
+
* export(s). The documented tool shape is a named descriptor with NO body plus
|
|
1536
|
+
* a separate `export default (input, ctx) => …` handler:
|
|
1537
|
+
*
|
|
1538
|
+
* export const searchDocs = defineTool({ name, description, input })
|
|
1539
|
+
* export default async ({ query }, ctx) => ctx.store.select('docs')…
|
|
1540
|
+
*
|
|
1541
|
+
* Nothing else connects the two halves — an agent's `tools: { searchDocs }`
|
|
1542
|
+
* imports the BODY-LESS descriptor, so without this pass every such tool fails
|
|
1543
|
+
* at call time ("neither execute nor handler"). This attaches the default
|
|
1544
|
+
* export as the descriptor's `handler` (mutated in place — the agent imports
|
|
1545
|
+
* the SAME module instance, so its tool reference is fixed too). No-op when the
|
|
1546
|
+
* tool already has an inline `execute`/`handler`, or the module has no default
|
|
1547
|
+
* function. Returns the number of tools wired. Idempotent.
|
|
1548
|
+
*/
|
|
1549
|
+
export declare const resolveToolModuleHandlers: (mod: Record<string, unknown>) => number;
|
|
1550
|
+
|
|
1551
|
+
export declare const resolveVideoModel: (config: ProviderConfig) => Experimental_VideoModelV4;
|
|
1552
|
+
|
|
1553
|
+
/**
|
|
1554
|
+
* The minimal Redis surface a resumable-stream store needs — a per-event LIST
|
|
1555
|
+
* (`RPUSH`/`LRANGE`) plus two flag keys (claim + done). Adapt your client
|
|
1556
|
+
* (ioredis / node-redis / a `@voltro/cache` RESP handle) to these five methods;
|
|
1557
|
+
* `@voltro/ai` takes NO Redis dependency, so the client is always injected.
|
|
1558
|
+
* `ttlSeconds`, when passed, should map to `SET … EX` / `EXPIRE` so finished
|
|
1559
|
+
* (or abandoned) streams self-evict — there is no GC sweep for the Redis path.
|
|
1560
|
+
*/
|
|
1561
|
+
export declare interface ResumableRedis {
|
|
1562
|
+
/** `SET key value NX [EX ttl]` — return true iff the key did NOT exist (we set it). */
|
|
1563
|
+
readonly setNx: (key: string, value: string, ttlSeconds?: number) => Promise<boolean>;
|
|
1564
|
+
/** `RPUSH key value`. */
|
|
1565
|
+
readonly rpush: (key: string, value: string) => Promise<void>;
|
|
1566
|
+
/** `LRANGE key start stop` (0-based, inclusive; negative = from the end, -1 = last). */
|
|
1567
|
+
readonly lrange: (key: string, start: number, stop: number) => Promise<ReadonlyArray<string>>;
|
|
1568
|
+
/** `SET key value [EX ttl]`. */
|
|
1569
|
+
readonly set: (key: string, value: string, ttlSeconds?: number) => Promise<void>;
|
|
1570
|
+
/** `EXISTS key` → boolean. */
|
|
1571
|
+
readonly exists: (key: string) => Promise<boolean>;
|
|
1572
|
+
/** `EXPIRE key seconds` — refresh a key's TTL. Optional (no-op if absent). */
|
|
1573
|
+
readonly expire?: (key: string, seconds: number) => Promise<void>;
|
|
1574
|
+
}
|
|
1575
|
+
|
|
1576
|
+
/**
|
|
1577
|
+
* The one call a streaming rpc makes per subscription. Elects a producer for
|
|
1578
|
+
* `streamId` (forking it as a daemon so it survives THIS consumer's lifetime),
|
|
1579
|
+
* then returns a consumer stream that replays past `fromSeq` and tails to the
|
|
1580
|
+
* end. First connection produces + consumes; a reconnect (producer already
|
|
1581
|
+
* elected) just consumes from its cursor.
|
|
1582
|
+
*/
|
|
1583
|
+
export declare const resumableStream: (opts: ResumableStreamOptions) => Stream.Stream<SeqEvent>;
|
|
1584
|
+
|
|
1585
|
+
export declare interface ResumableStreamOptions {
|
|
1586
|
+
readonly streamId: string;
|
|
1587
|
+
readonly store: ResumableStreamStore;
|
|
1588
|
+
/** The underlying event source — typically `() => streamText({...})`. Run at
|
|
1589
|
+
* most once per `streamId` (by the elected producer). */
|
|
1590
|
+
readonly source: () => Stream.Stream<AgentEvent>;
|
|
1591
|
+
/** Replay events past this `seq` before tailing (a reconnect cursor). */
|
|
1592
|
+
readonly fromSeq?: number;
|
|
1593
|
+
readonly pollMs?: number;
|
|
1594
|
+
readonly idleTimeoutMs?: number;
|
|
1595
|
+
}
|
|
1596
|
+
|
|
1597
|
+
/**
|
|
1598
|
+
* The persistence port a resumable stream drives. Append-only log + a
|
|
1599
|
+
* done-flag + a one-winner producer election, all keyed by `streamId`.
|
|
1600
|
+
* Implement it against any ordered store (a Redis list, a log table, …);
|
|
1601
|
+
* two implementations ship below.
|
|
1602
|
+
*/
|
|
1603
|
+
export declare interface ResumableStreamStore {
|
|
1604
|
+
/**
|
|
1605
|
+
* Atomically elect the producer for `streamId`. Returns `true` for the ONE
|
|
1606
|
+
* caller that should run the model, `false` for everyone else (they consume).
|
|
1607
|
+
* Must be race-free across processes — the durable backend uses an
|
|
1608
|
+
* insert-if-not-exists, exactly like `_voltro_idempotency`'s claim.
|
|
1609
|
+
*/
|
|
1610
|
+
readonly claimProducer: (streamId: string) => Effect.Effect<boolean>;
|
|
1611
|
+
/** Append one event at its `seq`. Producer-only. */
|
|
1612
|
+
readonly append: (streamId: string, ev: SeqEvent) => Effect.Effect<void>;
|
|
1613
|
+
/** Persisted events with `seq > afterSeq`, ascending. */
|
|
1614
|
+
readonly read: (streamId: string, afterSeq: number) => Effect.Effect<ReadonlyArray<SeqEvent>>;
|
|
1615
|
+
/** Mark the stream complete — the producer reached a terminal event. */
|
|
1616
|
+
readonly markDone: (streamId: string) => Effect.Effect<void>;
|
|
1617
|
+
/** Whether the producer has finished (all events are persisted). */
|
|
1618
|
+
readonly isDone: (streamId: string) => Effect.Effect<boolean>;
|
|
1619
|
+
}
|
|
1620
|
+
|
|
1621
|
+
/** Convenience: resume a `streamText` run by `streamId`. The producer runs
|
|
1622
|
+
* `streamText(options)`; reconnect by passing `fromSeq`. */
|
|
1623
|
+
export declare const resumableStreamText: (args: {
|
|
1624
|
+
readonly streamId: string;
|
|
1625
|
+
readonly store: ResumableStreamStore;
|
|
1626
|
+
readonly options: StreamTextOptions;
|
|
1627
|
+
readonly fromSeq?: number;
|
|
1628
|
+
readonly pollMs?: number;
|
|
1629
|
+
readonly idleTimeoutMs?: number;
|
|
1630
|
+
}) => Stream.Stream<SeqEvent>;
|
|
1631
|
+
|
|
1632
|
+
/**
|
|
1633
|
+
* The synthesized `<name>.send` body. Appends the user turn at `order`, then
|
|
1634
|
+
* drives a streaming assistant turn at `order + 1` via `runAssistant` (which
|
|
1635
|
+
* owns the loop + delta-persistence). Returns the assembled assistant text.
|
|
1636
|
+
*/
|
|
1637
|
+
export declare const runAgentTurn: (exec: AnyAgentExecutor, store: DataStore, args: {
|
|
1638
|
+
readonly threadId: string;
|
|
1639
|
+
readonly input: unknown;
|
|
1640
|
+
readonly tenantId?: string | null;
|
|
1641
|
+
readonly order: number;
|
|
1642
|
+
readonly toolContext?: ToolContext;
|
|
1643
|
+
readonly throttleMs?: number;
|
|
1644
|
+
readonly promptOf?: (input: unknown) => string;
|
|
1645
|
+
}) => Effect.Effect<string, Error>;
|
|
1646
|
+
|
|
1647
|
+
/**
|
|
1648
|
+
* Drive a streaming assistant turn end-to-end against the persisted row.
|
|
1649
|
+
*
|
|
1650
|
+
* Inserts the streaming row, consumes `streamText`, accumulates token deltas
|
|
1651
|
+
* into a single text part, records each tool call + result as a tool part,
|
|
1652
|
+
* throttle-patches the row as deltas arrive, and flips `streaming:false` on the
|
|
1653
|
+
* terminal `done`/`error` event. Returns the final assembled text.
|
|
1654
|
+
*/
|
|
1655
|
+
export declare const runAssistant: (store: DataStore, input: RunAssistantInput) => Effect.Effect<string, Error>;
|
|
1656
|
+
|
|
1657
|
+
export declare interface RunAssistantInput {
|
|
1658
|
+
readonly threadId: string;
|
|
1659
|
+
readonly tenantId?: string | null;
|
|
1660
|
+
readonly prompt: string;
|
|
1661
|
+
/** Full prior conversation for multi-turn memory. When present it is sent to
|
|
1662
|
+
* the model INSTEAD of `prompt` (must already end with the latest user
|
|
1663
|
+
* turn). The synthesized agent send builds this from the thread history. */
|
|
1664
|
+
readonly messages?: ReadonlyArray<ModelMessage>;
|
|
1665
|
+
readonly system?: string;
|
|
1666
|
+
readonly tools?: Record<string, AnyTool>;
|
|
1667
|
+
/** Provider override; defaults to `providerFromEnv()`. */
|
|
1668
|
+
readonly provider?: ProviderConfig;
|
|
1669
|
+
/** Max LLM↔tool round-trips before the loop stops. Forwarded to streamText. */
|
|
1670
|
+
readonly maxSteps?: number;
|
|
1671
|
+
/** Request context threaded into `*.tool.tsx` executor bodies. */
|
|
1672
|
+
readonly toolContext?: ToolContext;
|
|
1673
|
+
/** Provider-specific options forwarded to the SDK (e.g. reasoning effort).
|
|
1674
|
+
* See `AiProviderOptions`. */
|
|
1675
|
+
readonly providerOptions?: AiProviderOptions;
|
|
1676
|
+
/** The order the streaming assistant row was inserted at. */
|
|
1677
|
+
readonly order: number;
|
|
1678
|
+
/** Throttle window for the live `parts` patch. Default 100ms. */
|
|
1679
|
+
readonly throttleMs?: number;
|
|
1680
|
+
}
|
|
1681
|
+
|
|
1682
|
+
/**
|
|
1683
|
+
* End-to-end copilot step: build the grammar prompt, ask the model for a
|
|
1684
|
+
* constrained proposal, then VALIDATE it against the schema. Returns a
|
|
1685
|
+
* validated read-only descriptor or a typed {@link CopilotRejected}. Run the
|
|
1686
|
+
* descriptor AS the subject for tenant/scope isolation.
|
|
1687
|
+
*
|
|
1688
|
+
* Real wiring of `propose`:
|
|
1689
|
+
* ```ts
|
|
1690
|
+
* propose: ({ system, prompt }) =>
|
|
1691
|
+
* Effect.runPromise(
|
|
1692
|
+
* generateObject({ system, prompt, schema: CopilotProposalSchema })
|
|
1693
|
+
* .pipe(Effect.map((r) => r.object)),
|
|
1694
|
+
* )
|
|
1695
|
+
* ```
|
|
1696
|
+
*/
|
|
1697
|
+
export declare const runDataCopilot: (question: string, schema: CopilotSchema, deps: DataCopilotDeps) => Promise<CopilotValidation>;
|
|
1698
|
+
|
|
1699
|
+
/**
|
|
1700
|
+
* One persisted event of a RESUMABLE stream: an `AgentEvent` plus its monotonic
|
|
1701
|
+
* 1-based position. `seq` is the resume cursor — a client that has rendered up
|
|
1702
|
+
* to `seq=N` reconnects with `fromSeq: N` and gets `N+1…` replayed, then live.
|
|
1703
|
+
*
|
|
1704
|
+
* This is the WIRE element for a resumable `defineStream` (`element: SeqEvent`).
|
|
1705
|
+
* It lives here — next to `AgentEvent`, with ZERO server imports — so a
|
|
1706
|
+
* browser-loaded stream descriptor can reference the Schema without dragging
|
|
1707
|
+
* the server-only resumable-stream store graph (`@voltro/database`) into the
|
|
1708
|
+
* client bundle.
|
|
1709
|
+
*/
|
|
1710
|
+
export declare const SeqEvent: Schema.Struct<{
|
|
1711
|
+
seq: typeof Schema.Number;
|
|
1712
|
+
event: Schema.Union<[Schema.Struct<{
|
|
1713
|
+
_tag: Schema.Literal<["token"]>;
|
|
1714
|
+
text: typeof Schema.String;
|
|
1715
|
+
}>, Schema.Struct<{
|
|
1716
|
+
_tag: Schema.Literal<["reasoning"]>;
|
|
1717
|
+
text: typeof Schema.String;
|
|
1718
|
+
}>, Schema.Struct<{
|
|
1719
|
+
_tag: Schema.Literal<["toolCall"]>;
|
|
1720
|
+
id: typeof Schema.String;
|
|
1721
|
+
name: typeof Schema.String;
|
|
1722
|
+
input: typeof Schema.Unknown;
|
|
1723
|
+
}>, Schema.Struct<{
|
|
1724
|
+
_tag: Schema.Literal<["toolResult"]>;
|
|
1725
|
+
id: typeof Schema.String;
|
|
1726
|
+
name: typeof Schema.String;
|
|
1727
|
+
output: Schema.optional<typeof Schema.Unknown>;
|
|
1728
|
+
error: Schema.optional<typeof Schema.String>;
|
|
1729
|
+
}>, Schema.Struct<{
|
|
1730
|
+
_tag: Schema.Literal<["source"]>;
|
|
1731
|
+
sourceType: Schema.Literal<["url", "document"]>;
|
|
1732
|
+
id: typeof Schema.String;
|
|
1733
|
+
url: Schema.optional<typeof Schema.String>;
|
|
1734
|
+
title: Schema.optional<typeof Schema.String>;
|
|
1735
|
+
mediaType: Schema.optional<typeof Schema.String>;
|
|
1736
|
+
filename: Schema.optional<typeof Schema.String>;
|
|
1737
|
+
}>, Schema.Struct<{
|
|
1738
|
+
_tag: Schema.Literal<["file"]>;
|
|
1739
|
+
mediaType: typeof Schema.String;
|
|
1740
|
+
data: typeof Schema.String;
|
|
1741
|
+
}>, Schema.Struct<{
|
|
1742
|
+
_tag: Schema.Literal<["message"]>;
|
|
1743
|
+
role: typeof Schema.String;
|
|
1744
|
+
content: typeof Schema.String;
|
|
1745
|
+
}>, Schema.Struct<{
|
|
1746
|
+
_tag: Schema.Literal<["error"]>;
|
|
1747
|
+
code: Schema.Literal<["provider", "tool", "timeout", "cancelled", "decode", "internal"]>;
|
|
1748
|
+
message: typeof Schema.String;
|
|
1749
|
+
retryable: typeof Schema.Boolean;
|
|
1750
|
+
}>, Schema.Struct<{
|
|
1751
|
+
_tag: Schema.Literal<["done"]>;
|
|
1752
|
+
finishReason: typeof Schema.String;
|
|
1753
|
+
usage: Schema.Struct<{
|
|
1754
|
+
inputTokens: Schema.optional<typeof Schema.Number>;
|
|
1755
|
+
outputTokens: Schema.optional<typeof Schema.Number>;
|
|
1756
|
+
totalTokens: Schema.optional<typeof Schema.Number>;
|
|
1757
|
+
}>;
|
|
1758
|
+
}>]>;
|
|
1759
|
+
}>;
|
|
1760
|
+
|
|
1761
|
+
export declare type SeqEvent = typeof SeqEvent.Type;
|
|
1762
|
+
|
|
1763
|
+
/**
|
|
1764
|
+
* Install the global middleware stack — every subsequent `resolveModel` wraps
|
|
1765
|
+
* its model with it. Returns the PREVIOUS stack so a caller (a test, a scoped
|
|
1766
|
+
* boot) can restore it. Call with no args to clear.
|
|
1767
|
+
*/
|
|
1768
|
+
export declare const setAiMiddleware: (...middleware: ReadonlyArray<AiMiddleware>) => ReadonlyArray<AiMiddleware>;
|
|
1769
|
+
|
|
1770
|
+
/**
|
|
1771
|
+
* Install app-wide model-price overrides, merged OVER the built-in
|
|
1772
|
+
* point-in-time defaults (a user entry for a model id wins). Wire the ai
|
|
1773
|
+
* config's `pricing` map through this at boot to encode current/negotiated
|
|
1774
|
+
* rates without editing the framework. Returns the previous overrides so a
|
|
1775
|
+
* test can restore them. Pass `{}` to clear.
|
|
1776
|
+
*/
|
|
1777
|
+
export declare const setModelPricing: (pricing: Record<string, ModelPrice>) => Record<string, ModelPrice>;
|
|
1778
|
+
|
|
1779
|
+
/** Install a provider override that every `providerFromEnv()` call honours
|
|
1780
|
+
* regardless of env. `useMockAi` uses this; returns the previous override
|
|
1781
|
+
* so it can be restored. */
|
|
1782
|
+
export declare const setProviderOverride: (config: ProviderConfig | null) => ProviderConfig | null;
|
|
1783
|
+
|
|
1784
|
+
export { simulateStreamingMiddleware }
|
|
1785
|
+
|
|
1786
|
+
/**
|
|
1787
|
+
* Run the underlying stream ONCE and persist every event. Assigns a monotonic
|
|
1788
|
+
* `seq`, appends each event, and marks the stream done at the end — even if a
|
|
1789
|
+
* store append throws, so consumers never tail forever. Intended to run
|
|
1790
|
+
* detached (see `resumableStream`, which forks it as a daemon).
|
|
1791
|
+
*/
|
|
1792
|
+
export declare const startResumableProducer: (streamId: string, store: ResumableStreamStore, source: () => Stream.Stream<AgentEvent>) => Effect.Effect<void>;
|
|
1793
|
+
|
|
1794
|
+
export declare const STREAM_EVENTS_TABLE = "_voltro_stream_events";
|
|
1795
|
+
|
|
1796
|
+
export declare const STREAM_STATE_TABLE = "_voltro_stream_state";
|
|
1797
|
+
|
|
1798
|
+
/** The append-only event log. UNIQUE (streamId, seq) defends against a stray
|
|
1799
|
+
* double-producer writing duplicate positions. */
|
|
1800
|
+
export declare const streamEventsTable: Table<typeof STREAM_EVENTS_TABLE, Record<string, ColumnDefinition<unknown>>, false>;
|
|
1801
|
+
|
|
1802
|
+
export declare interface StreamRetryOptions {
|
|
1803
|
+
/** Total attempts, including the first (default 3 → 1 try + 2 retries). */
|
|
1804
|
+
readonly maxAttempts?: number;
|
|
1805
|
+
/** Base backoff between attempts in ms (default 250); grows exponentially. */
|
|
1806
|
+
readonly backoffMs?: number;
|
|
1807
|
+
/** Cap on a single backoff in ms (default 5000). */
|
|
1808
|
+
readonly maxBackoffMs?: number;
|
|
1809
|
+
}
|
|
1810
|
+
|
|
1811
|
+
/** The per-stream state + producer-election row. UNIQUE `streamId` is the
|
|
1812
|
+
* atomic claim arbiter (insert-if-not-exists). */
|
|
1813
|
+
export declare const streamStateTable: Table<typeof STREAM_STATE_TABLE, Record<string, ColumnDefinition<unknown>>, false>;
|
|
1814
|
+
|
|
1815
|
+
export declare const streamText: (options: StreamTextOptions) => Stream.Stream<AgentEvent>;
|
|
1816
|
+
|
|
1817
|
+
export declare interface StreamTextOptions {
|
|
1818
|
+
/** The latest user turn, sent as a single user message. Ignored when
|
|
1819
|
+
* `messages` is supplied (then the full conversation is sent instead). */
|
|
1820
|
+
readonly prompt: string;
|
|
1821
|
+
/** Full prior conversation (multi-turn memory). When present it is sent to
|
|
1822
|
+
* the model INSTEAD of `prompt` — it should already include the latest user
|
|
1823
|
+
* turn as its last entry. The agent send path builds this from the thread. */
|
|
1824
|
+
readonly messages?: ReadonlyArray<ModelMessage>;
|
|
1825
|
+
readonly system?: string;
|
|
1826
|
+
readonly provider?: ProviderConfig;
|
|
1827
|
+
/** Ordered provider/model fallback chain. When a stream ERRORS with a
|
|
1828
|
+
* retryable `provider` error BEFORE any content has streamed, the run
|
|
1829
|
+
* re-attempts against the next config in this list (then the next, …). Once
|
|
1830
|
+
* any content event (token / tool / …) has flowed the answer is committed and
|
|
1831
|
+
* a later error surfaces as-is — re-running elsewhere would duplicate tokens.
|
|
1832
|
+
* A `cancelled` error (deliberate abort) never falls through. Applies via
|
|
1833
|
+
* `streamTextWithFallback` / `streamTextWithRetry`; a plain `streamText` uses
|
|
1834
|
+
* only the primary provider. */
|
|
1835
|
+
readonly fallbacks?: ReadonlyArray<ProviderConfig>;
|
|
1836
|
+
readonly maxTokens?: number;
|
|
1837
|
+
/** Tools the model may call. The SDK dispatches them + loops. */
|
|
1838
|
+
readonly tools?: Record<string, AnyTool>;
|
|
1839
|
+
/** Max LLM↔tool round-trips before the loop stops. Default 8. */
|
|
1840
|
+
readonly maxSteps?: number;
|
|
1841
|
+
/** Request context threaded into `*.tool.tsx` executor bodies. */
|
|
1842
|
+
readonly toolContext?: ToolContext;
|
|
1843
|
+
/** Provider-specific options forwarded to the SDK (e.g. reasoning effort).
|
|
1844
|
+
* See `AiProviderOptions`. */
|
|
1845
|
+
readonly providerOptions?: AiProviderOptions;
|
|
1846
|
+
/** Cancel-by-streamId (#12). When set, the run registers an `AbortController`
|
|
1847
|
+
* addressable by `cancelStream(streamId)` — a "Stop" button calls that out of
|
|
1848
|
+
* band to abort the run. The id is dropped from the registry when the stream
|
|
1849
|
+
* ends. Keep it unique per logical stream. */
|
|
1850
|
+
readonly streamId?: string;
|
|
1851
|
+
/** An external abort signal (composed with `streamId`'s controller, if any).
|
|
1852
|
+
* Aborting it ends the run with a terminal `cancelled` error. */
|
|
1853
|
+
readonly signal?: AbortSignal;
|
|
1854
|
+
/** SDK-level transient retry (#12). Forwarded as the AI SDK's `maxRetries` —
|
|
1855
|
+
* retries the provider HTTP call on a transient failure BEFORE any bytes
|
|
1856
|
+
* stream, so it never duplicates emitted tokens. For re-attempting a whole
|
|
1857
|
+
* stream that errored before producing content, use `streamTextWithRetry`. */
|
|
1858
|
+
readonly maxRetries?: number;
|
|
1859
|
+
}
|
|
1860
|
+
|
|
1861
|
+
/**
|
|
1862
|
+
* `streamText` with an ordered provider/model FALLBACK chain (F10). When a run
|
|
1863
|
+
* ERRORS with a retryable `provider` error BEFORE any content has streamed, it
|
|
1864
|
+
* re-attempts against the next config in `options.fallbacks` (then the next, …).
|
|
1865
|
+
* The moment any content event (token / reasoning / tool / source / file) flows,
|
|
1866
|
+
* the answer is committed and a later error surfaces as-is — re-running against
|
|
1867
|
+
* another provider would duplicate tokens. A `cancelled` error (deliberate
|
|
1868
|
+
* abort) is never a fallback trigger. Exhausting every option surfaces the LAST
|
|
1869
|
+
* provider's terminal error.
|
|
1870
|
+
*
|
|
1871
|
+
* This complements `streamTextWithRetry` (which re-attempts the SAME provider on
|
|
1872
|
+
* a transient blip): each fallback config gets its own SDK-level `maxRetries`
|
|
1873
|
+
* BEFORE the chain moves on. Compose both by passing `fallbacks` AND wrapping in
|
|
1874
|
+
* `streamTextWithRetry` per config — or just use `streamTextWithRetry`, which
|
|
1875
|
+
* runs the fallback chain internally. With no `fallbacks` this is a single
|
|
1876
|
+
* `streamText`.
|
|
1877
|
+
*/
|
|
1878
|
+
export declare const streamTextWithFallback: (options: StreamTextOptions) => Stream.Stream<AgentEvent>;
|
|
1879
|
+
|
|
1880
|
+
/**
|
|
1881
|
+
* `streamText` with automatic re-attempts on a transient failure — but ONLY
|
|
1882
|
+
* while nothing has streamed yet. The moment any content event (token /
|
|
1883
|
+
* reasoning / tool / source / file) flows, the answer is committed and a later
|
|
1884
|
+
* error is surfaced as-is (re-running would duplicate tokens). A `cancelled`
|
|
1885
|
+
* error (deliberate abort) is never retryable, so a Stop is honoured.
|
|
1886
|
+
*
|
|
1887
|
+
* This complements `streamText({ maxRetries })` (the SDK's HTTP-level retry,
|
|
1888
|
+
* which handles a transient failure BEFORE the first byte): use `maxRetries`
|
|
1889
|
+
* for connection blips, this wrapper to recover from an immediate provider
|
|
1890
|
+
* error event (e.g. a 503 surfaced as the first stream chunk). Backoff is
|
|
1891
|
+
* exponential between attempts.
|
|
1892
|
+
*
|
|
1893
|
+
* FALLBACK-AWARE (F10): when `options.fallbacks` is set, each provider in the
|
|
1894
|
+
* chain (primary first) gets its OWN `maxAttempts` retries; when a provider
|
|
1895
|
+
* exhausts its retries WITHOUT streaming content, the chain moves to the next
|
|
1896
|
+
* provider before finally surfacing the last provider's terminal error. So this
|
|
1897
|
+
* one wrapper gives you "retry each provider N times, then fall to the next".
|
|
1898
|
+
*/
|
|
1899
|
+
export declare const streamTextWithRetry: (options: StreamTextOptions, retry?: StreamRetryOptions) => Stream.Stream<AgentEvent>;
|
|
1900
|
+
|
|
1901
|
+
/** A synthesized tool's metadata (the discovery/inventory view). */
|
|
1902
|
+
export declare interface SynthesizedTool {
|
|
1903
|
+
readonly name: string;
|
|
1904
|
+
readonly kind: string;
|
|
1905
|
+
readonly description: string;
|
|
1906
|
+
/** mutation/action → true. Drives the confirm default + the writes gate. */
|
|
1907
|
+
readonly write: boolean;
|
|
1908
|
+
/** Require human confirmation before this call executes. */
|
|
1909
|
+
readonly confirm: boolean;
|
|
1910
|
+
readonly maxPerRun?: number;
|
|
1911
|
+
}
|
|
1912
|
+
|
|
1913
|
+
/**
|
|
1914
|
+
* The discovery/inventory view: every descriptor that is exposable AND passes
|
|
1915
|
+
* the policy, as `SynthesizedTool` metadata (no executable body). Use for the
|
|
1916
|
+
* capability surface / a confirm-UI. Sorted by name.
|
|
1917
|
+
*/
|
|
1918
|
+
export declare const synthesizeToolSpecs: (entries: ReadonlyArray<AppToolEntry>, policy?: AppToolPolicy) => ReadonlyArray<SynthesizedTool>;
|
|
1919
|
+
|
|
1920
|
+
/** A `system` prompt: a static string OR a per-request function of the
|
|
1921
|
+
* decoded input (templates locale / plan / persona into the prompt). */
|
|
1922
|
+
export declare type SystemPrompt<Input> = string | ((input: Input) => string);
|
|
1923
|
+
|
|
1924
|
+
/** Project a raw gateway entry → our clean, UI-friendly + cost-aligned shape. */
|
|
1925
|
+
export declare const toModelInfo: (e: GatewayLanguageModelEntry) => GatewayModelInfo;
|
|
1926
|
+
|
|
1927
|
+
/** The slice of the request context a `*.tool.tsx` handler receives. Kept
|
|
1928
|
+
* structurally typed here (rather than importing `AppContext` from
|
|
1929
|
+
* `@voltro/runtime`) so `@voltro/ai` carries no runtime dependency — the
|
|
1930
|
+
* CLI discovery path passes the real `AppContext`, which is a superset.
|
|
1931
|
+
*
|
|
1932
|
+
* `runtime` is the caller's ambient Effect `Runtime`. When present, tool
|
|
1933
|
+
* bodies are run through it (`Runtime.runPromise(runtime)(eff)`) instead of
|
|
1934
|
+
* bare `Effect.runPromise`, so a body may `yield*` any service the caller's
|
|
1935
|
+
* runtime carries — `JiraService`, the data store layer, `HttpClient`, … —
|
|
1936
|
+
* rather than being limited to `R = never`. The agent loop
|
|
1937
|
+
* (`streamText`/`runAssistant`) captures `yield* Effect.runtime()` and threads
|
|
1938
|
+
* it here; without a runtime, behaviour is unchanged. */
|
|
1939
|
+
export declare interface ToolContext {
|
|
1940
|
+
readonly store: unknown;
|
|
1941
|
+
readonly request?: {
|
|
1942
|
+
readonly subject?: unknown;
|
|
1943
|
+
};
|
|
1944
|
+
readonly runtime?: Runtime.Runtime<unknown>;
|
|
1945
|
+
}
|
|
1946
|
+
|
|
1947
|
+
export declare interface ToolDef<A, I, O> {
|
|
1948
|
+
readonly name: string;
|
|
1949
|
+
readonly description: string;
|
|
1950
|
+
readonly input: Schema.Schema<A, I>;
|
|
1951
|
+
/** Optional result schema. When set, `buildSdkTools` decodes the body's
|
|
1952
|
+
* output through it before handing the value back to the model. */
|
|
1953
|
+
readonly output?: Schema.Schema.Any;
|
|
1954
|
+
/** Inline body: returns an Effect. May declare service deps on the R
|
|
1955
|
+
* channel — they are provided by the caller's runtime
|
|
1956
|
+
* (`ctx.runtime`) at execution time. */
|
|
1957
|
+
readonly execute?: (input: A) => Effect.Effect<O, never, unknown>;
|
|
1958
|
+
/** File-convention body: receives `ctx`, returns Effect or Promise. */
|
|
1959
|
+
readonly handler?: ToolHandler<A, O>;
|
|
1960
|
+
/** Per-call deadline; the tool fails with a timeout error past it. */
|
|
1961
|
+
readonly timeoutMs?: number;
|
|
1962
|
+
}
|
|
1963
|
+
|
|
1964
|
+
/** A context-receiving tool body — the `*.tool.tsx` default-export form.
|
|
1965
|
+
* May return an Effect (with service deps the caller's runtime provides) or
|
|
1966
|
+
* a Promise; `buildSdkTools` normalises both. */
|
|
1967
|
+
export declare type ToolHandler<A, O> = (input: A, ctx: ToolContext) => Effect.Effect<O, never, unknown> | Promise<O>;
|
|
1968
|
+
|
|
1969
|
+
/**
|
|
1970
|
+
* Drop a stream's controller. `streamText` calls this in its finaliser when the
|
|
1971
|
+
* stream ends — success, error, cancel, or consumer interrupt. Idempotent.
|
|
1972
|
+
*/
|
|
1973
|
+
export declare const unregisterStream: (streamId: string) => void;
|
|
1974
|
+
|
|
1975
|
+
/**
|
|
1976
|
+
* Validate a proposal against the schema and (on success) build a read-only
|
|
1977
|
+
* `QueryDescriptor`. EVERY table/column the proposal names must exist in the
|
|
1978
|
+
* manifest — otherwise a typed `CopilotRejected`. The descriptor is always a
|
|
1979
|
+
* SELECT (read-only); run it as the subject for tenant/scope isolation.
|
|
1980
|
+
*/
|
|
1981
|
+
export declare const validateCopilotProposal: (proposal: CopilotProposal, schema: CopilotSchema) => CopilotValidation;
|
|
1982
|
+
|
|
1983
|
+
/**
|
|
1984
|
+
* Wrap an `Effect`-returning generate call with a `voltro.ai.call` span +
|
|
1985
|
+
* per-call metrics (count, error count, duration histogram, token + cost
|
|
1986
|
+
* counters), labelled by provider/model/operation. A typed failure increments
|
|
1987
|
+
* the error counter + marks the span; success records the usage tally the
|
|
1988
|
+
* result exposes via `usageOf`. Pure metric updates + one span — no content or
|
|
1989
|
+
* secret ever touches a label.
|
|
1990
|
+
*/
|
|
1991
|
+
export declare const withAiCallSpan: <A, E, R>(attributes: AiCallAttributes, usageOf: (a: A) => {
|
|
1992
|
+
readonly inputTokens?: number | undefined;
|
|
1993
|
+
readonly outputTokens?: number | undefined;
|
|
1994
|
+
}, effect: Effect.Effect<A, E, R>) => Effect.Effect<A, E, R>;
|
|
1995
|
+
|
|
1996
|
+
export { }
|