@zerotal/ai 1.5.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/src/types.ts ADDED
@@ -0,0 +1,317 @@
1
+ /**
2
+ * The vocabulary every driver speaks.
3
+ *
4
+ * These types are deliberately provider-shaped only where every provider agrees.
5
+ * Where they don't — thinking budgets, reasoning effort, cache breakpoints — the
6
+ * difference goes through {@link AiRequest.providerOptions} rather than being
7
+ * flattened into the intersection of what everyone supports.
8
+ */
9
+
10
+ /** How hard the model should work before answering. Mapped per-driver. */
11
+ export type AiEffort = "low" | "medium" | "high" | "xhigh" | "max";
12
+
13
+ /** Who said it. Tool results ride inside a `user` turn, as the providers expect. */
14
+ export type AiRole = "user" | "assistant";
15
+
16
+ /** A tool call the model asked for, lifted out of whatever block shape the provider used. */
17
+ export interface AiToolCall {
18
+ /** Provider-assigned id — echo it back with the result so the pairing survives. */
19
+ id: string;
20
+ name: string;
21
+ input: Record<string, unknown>;
22
+ }
23
+
24
+ /** The answer to one {@link AiToolCall}. */
25
+ export interface AiToolResult {
26
+ id: string;
27
+ /** Serialized output. Objects are JSON-stringified before they reach here. */
28
+ content: string;
29
+ isError?: boolean;
30
+ }
31
+
32
+ /** One turn of a conversation. */
33
+ export interface AiMessage {
34
+ role: AiRole;
35
+ content: string;
36
+ /** Tool calls this assistant turn asked for. */
37
+ toolCalls?: AiToolCall[];
38
+ /** Tool results this user turn carries back. */
39
+ toolResults?: AiToolResult[];
40
+ /**
41
+ * The provider's own content blocks for this turn, kept verbatim.
42
+ *
43
+ * Replaying a turn that contained thinking, cache markers, or server-tool blocks
44
+ * requires handing the provider back exactly what it produced — a reconstruction
45
+ * from `content` alone loses the signature and the request is rejected. Drivers
46
+ * write this; callers should not.
47
+ */
48
+ raw?: unknown;
49
+ }
50
+
51
+ /** Token accounting for one request. Fields a provider does not report stay 0. */
52
+ export interface AiUsage {
53
+ inputTokens: number;
54
+ outputTokens: number;
55
+ /** Tokens served from the prompt cache (billed at a fraction of input). */
56
+ cacheReadTokens: number;
57
+ /** Tokens written to the prompt cache (billed at a premium over input). */
58
+ cacheWriteTokens: number;
59
+ }
60
+
61
+ /** Why generation stopped. `refusal` is a successful HTTP response, not an error. */
62
+ export type AiStopReason =
63
+ "end_turn" | "max_tokens" | "tool_use" | "pause_turn" | "refusal" | "stop_sequence" | "unknown";
64
+
65
+ /**
66
+ * Per-driver escape hatch, keyed by driver name and passed through untouched.
67
+ *
68
+ * @example
69
+ * providerOptions: {
70
+ * anthropic: { thinking: { type: "adaptive", display: "summarized" } },
71
+ * openai: { reasoning: { effort: "high" } },
72
+ * }
73
+ */
74
+ export type AiProviderOptions = Record<string, Record<string, unknown>>;
75
+
76
+ /** What every generation call takes. `prompt` and `messages` are interchangeable. */
77
+ export interface AiRequest {
78
+ /** Shorthand for a single user turn. Ignored when `messages` is present. */
79
+ prompt?: string;
80
+ messages?: AiMessage[];
81
+ /** The system prompt. Cached by default on drivers that support it. */
82
+ system?: string;
83
+ /** Override the driver's configured model for this call. */
84
+ model?: string;
85
+ maxTokens?: number;
86
+ effort?: AiEffort;
87
+ /** Tools the model may call. Only `agent()` runs them; `text()` reports them. */
88
+ tools?: AiTool[];
89
+ /** Cancels the HTTP request and, for `agent()`, ends the loop between steps. */
90
+ signal?: AbortSignal;
91
+ providerOptions?: AiProviderOptions;
92
+ /** Turn off prompt caching of the system prompt for this call. */
93
+ cache?: boolean;
94
+ /**
95
+ * Sampling temperature — **best-effort, not honoured everywhere.**
96
+ *
97
+ * Current Claude models reject `temperature` with a 400, so the Anthropic
98
+ * driver drops it rather than failing every request. Reach for
99
+ * {@link AiRequest.effort} instead: `low` for terse and deterministic-ish,
100
+ * `max` when correctness matters more than cost.
101
+ */
102
+ temperature?: number;
103
+ /** Which configured driver to use. Defaults to `ai.default`. */
104
+ driver?: string;
105
+ }
106
+
107
+ /** A finished, non-streaming generation. */
108
+ export interface AiResponse {
109
+ text: string;
110
+ /** The model that actually served the request — a fallback may have swapped it. */
111
+ model: string;
112
+ usage: AiUsage;
113
+ stopReason: AiStopReason;
114
+ /** Tool calls the model asked for. Empty unless `tools` were supplied. */
115
+ toolCalls: AiToolCall[];
116
+ /**
117
+ * This turn, in the form to append when continuing the conversation.
118
+ *
119
+ * The driver builds it because only the driver knows what has to survive
120
+ * verbatim: an assistant turn carrying thinking or cache blocks is rejected
121
+ * when it is rebuilt from `text`, since the signature does not round-trip.
122
+ * The agent loop appends this rather than reconstructing one.
123
+ */
124
+ assistantTurn: AiMessage;
125
+ /** The provider's untouched response object, for anything this shape omits. */
126
+ raw?: unknown;
127
+ }
128
+
129
+ /** One event from a streaming generation. */
130
+ export type AiStreamChunk =
131
+ | { type: "text"; text: string }
132
+ /** Summarized reasoning, on drivers configured to return it. */
133
+ | { type: "thinking"; text: string }
134
+ | { type: "tool_call"; call: AiToolCall }
135
+ | { type: "done"; response: AiResponse };
136
+
137
+ /** A structured-output generation: the parsed value plus the usual accounting. */
138
+ export interface AiObjectResponse<T> {
139
+ object: T;
140
+ model: string;
141
+ usage: AiUsage;
142
+ raw?: unknown;
143
+ }
144
+
145
+ /** A tool the model can call, with a handler this package will run. */
146
+ export interface AiTool<I = Record<string, unknown>> {
147
+ name: string;
148
+ description: string;
149
+ /** JSON Schema for the input, already translated from a validator schema. */
150
+ inputSchema: JsonSchema;
151
+ /**
152
+ * Runs when the model calls the tool. Return anything JSON-serializable; a
153
+ * string is passed through, everything else is stringified.
154
+ */
155
+ handler: (input: I, ctx: AiToolContext) => Promise<unknown> | unknown;
156
+ }
157
+
158
+ /** What a tool handler is told about the turn that invoked it. */
159
+ export interface AiToolContext {
160
+ /** Aborted when the caller cancels, or when the agent loop's lock is lost. */
161
+ signal: AbortSignal;
162
+ /** 1-based index of the agent step that made this call. */
163
+ step: number;
164
+ }
165
+
166
+ /** The result of running the agent loop to completion. */
167
+ export interface AiAgentResult {
168
+ text: string;
169
+ model: string;
170
+ /** Summed across every step of the loop. */
171
+ usage: AiUsage;
172
+ /** Every tool call made, in order, with what the handler returned. */
173
+ steps: AiAgentStep[];
174
+ stopReason: AiStopReason;
175
+ }
176
+
177
+ /** One tool call and its result within an agent run. */
178
+ export interface AiAgentStep {
179
+ step: number;
180
+ call: AiToolCall;
181
+ result: string;
182
+ isError: boolean;
183
+ durationMs: number;
184
+ }
185
+
186
+ /** A vector embedding request. */
187
+ export interface AiEmbedRequest {
188
+ input: string | string[];
189
+ model?: string;
190
+ driver?: string;
191
+ signal?: AbortSignal;
192
+ }
193
+
194
+ /** Embeddings, one vector per input, in input order. */
195
+ export interface AiEmbedResponse {
196
+ embeddings: number[][];
197
+ model: string;
198
+ usage: Pick<AiUsage, "inputTokens">;
199
+ }
200
+
201
+ // ── JSON Schema ────────────────────────────────────────────────────────────
202
+
203
+ /**
204
+ * The narrow JSON Schema subset the structured-output APIs accept.
205
+ *
206
+ * Deliberately not the full spec: `minLength`, `maximum`, and friends are
207
+ * rejected at request time by the providers, so {@link translateSchema} either
208
+ * strips them (re-checking client-side) or refuses to emit them.
209
+ */
210
+ export interface JsonSchema {
211
+ type?: "object" | "array" | "string" | "number" | "integer" | "boolean" | "null";
212
+ description?: string;
213
+ properties?: Record<string, JsonSchema>;
214
+ required?: string[];
215
+ additionalProperties?: false;
216
+ items?: JsonSchema;
217
+ enum?: Array<string | number>;
218
+ format?: string;
219
+ /** Present only on nullable fields: `[{...}, {"type":"null"}]`. */
220
+ anyOf?: JsonSchema[];
221
+ }
222
+
223
+ // ── Configuration ──────────────────────────────────────────────────────────
224
+
225
+ /** Anthropic driver settings. */
226
+ export interface AnthropicConfigShape {
227
+ apiKey: string;
228
+ /** Exact model id, no date suffix. */
229
+ model: string;
230
+ /** Cap for non-streaming calls. Covers thinking *and* response text. */
231
+ maxTokens: number;
232
+ /** Cap for streaming calls, where HTTP timeouts are not a concern. */
233
+ streamMaxTokens: number;
234
+ effort: AiEffort;
235
+ /** Route a safety refusal to Anthropic's recommended fallback model. */
236
+ fallbacks: boolean;
237
+ /** Mark the system prompt cacheable. The cheapest win available. */
238
+ cacheSystem: boolean;
239
+ /** Override the API base URL (proxies, gateways). */
240
+ baseUrl?: string;
241
+ /** Request timeout in milliseconds. */
242
+ timeout: number;
243
+ /**
244
+ * Best-effort default sampling temperature. Dropped by this driver on models
245
+ * that reject it (which is every current one) — `validateAiConfig` warns.
246
+ */
247
+ temperature?: number;
248
+ }
249
+
250
+ /** OpenAI driver settings. */
251
+ export interface OpenAiConfigShape {
252
+ apiKey: string;
253
+ model: string;
254
+ maxTokens: number;
255
+ baseUrl: string;
256
+ timeout: number;
257
+ }
258
+
259
+ /** Ollama driver settings — a local server, so no key. */
260
+ export interface OllamaConfigShape {
261
+ model: string;
262
+ baseUrl: string;
263
+ timeout: number;
264
+ }
265
+
266
+ /**
267
+ * Embeddings are their own block with their own driver.
268
+ *
269
+ * Anthropic has no embeddings endpoint, so tying embeddings to the generation
270
+ * driver would make "Claude for generation, something cheaper for vectors" —
271
+ * the normal pairing — impossible to express.
272
+ */
273
+ export interface EmbeddingsConfigShape {
274
+ default: string;
275
+ drivers: {
276
+ openai?: { apiKey: string; model: string; baseUrl: string; timeout: number };
277
+ ollama?: { model: string; baseUrl: string; timeout: number };
278
+ };
279
+ }
280
+
281
+ /** Spend ceilings, enforced before the request leaves. */
282
+ export interface AiLimitsConfigShape {
283
+ /** Reject a single request whose estimated cost exceeds this, in USD. 0 = off. */
284
+ perRequestUsd: number;
285
+ /** Reject once the process has spent this much today, in USD. 0 = off. */
286
+ perDayUsd: number;
287
+ }
288
+
289
+ /** How the agent loop behaves. */
290
+ export interface AiAgentConfigShape {
291
+ /** Hold a refreshable lock for the duration of a named agent run. */
292
+ lock: boolean;
293
+ /** Lock TTL in seconds — how long after a crash before another run may start. */
294
+ lockTtl: number;
295
+ /** Maximum tool-calling round trips before the loop gives up. */
296
+ maxSteps: number;
297
+ /** Maximum `pause_turn` resumes before the loop gives up. */
298
+ maxResumes: number;
299
+ }
300
+
301
+ /** The full `config/ai.ts` shape. */
302
+ export interface AiConfigShape {
303
+ default: string;
304
+ drivers: {
305
+ anthropic?: AnthropicConfigShape;
306
+ openai?: OpenAiConfigShape;
307
+ ollama?: OllamaConfigShape;
308
+ };
309
+ embeddings: EmbeddingsConfigShape;
310
+ limits: AiLimitsConfigShape;
311
+ /**
312
+ * Redact prompts before they reach logs and the monitor. A prompt is user data
313
+ * and the observability path is the one place it would otherwise be kept.
314
+ */
315
+ redact: boolean;
316
+ agent: AiAgentConfigShape;
317
+ }