@miraiclip/assistant 0.1.2 → 0.3.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/LICENSE +21 -21
- package/README.md +23 -21
- package/dist/index.cjs +1447 -90
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +486 -25
- package/dist/index.d.ts +486 -25
- package/dist/index.js +1450 -90
- package/dist/index.js.map +1 -1
- package/package.json +13 -6
package/dist/index.d.ts
CHANGED
|
@@ -10,6 +10,18 @@ interface ToolCall {
|
|
|
10
10
|
id: string;
|
|
11
11
|
name: string;
|
|
12
12
|
arguments: Record<string, unknown>;
|
|
13
|
+
/**
|
|
14
|
+
* Opaque data the adapter that produced the call needs back when the
|
|
15
|
+
* conversation is replayed (Gemini thought signatures, Anthropic thinking
|
|
16
|
+
* blocks). Keep it with the call; other adapters ignore it.
|
|
17
|
+
*/
|
|
18
|
+
meta?: Record<string, unknown>;
|
|
19
|
+
}
|
|
20
|
+
/** An image the model looks at (a rendered preview a tool returns): base64 bytes and their type. */
|
|
21
|
+
interface ChatImage {
|
|
22
|
+
mimeType: string;
|
|
23
|
+
/** Base64, no `data:` prefix. */
|
|
24
|
+
data: string;
|
|
13
25
|
}
|
|
14
26
|
type ChatMessage = {
|
|
15
27
|
role: "system";
|
|
@@ -21,11 +33,14 @@ type ChatMessage = {
|
|
|
21
33
|
role: "assistant";
|
|
22
34
|
content: string;
|
|
23
35
|
toolCalls?: ToolCall[];
|
|
24
|
-
}
|
|
36
|
+
}
|
|
37
|
+
/** `images`: pictures that come with the result (e.g. rendered frames), for models that can see. */
|
|
38
|
+
| {
|
|
25
39
|
role: "tool";
|
|
26
40
|
toolCallId: string;
|
|
27
41
|
name: string;
|
|
28
42
|
content: string;
|
|
43
|
+
images?: ChatImage[];
|
|
29
44
|
};
|
|
30
45
|
/** A tool as the model sees it: name, description and a JSON Schema for its input. */
|
|
31
46
|
interface ToolSpec {
|
|
@@ -59,18 +74,28 @@ interface ChatOptions {
|
|
|
59
74
|
/** Streaming text, as it arrives (adapters that can't stream call it once). */
|
|
60
75
|
onText?: (delta: string) => void;
|
|
61
76
|
}
|
|
77
|
+
interface ModelEntry {
|
|
78
|
+
id: string;
|
|
79
|
+
label?: string;
|
|
80
|
+
}
|
|
62
81
|
interface ChatModelInfo {
|
|
63
82
|
/** Adapter id, e.g. "openai". Alphanumeric with - or _. */
|
|
64
83
|
id: string;
|
|
65
84
|
label: string;
|
|
66
85
|
/** Models this adapter serves; the first is the default. */
|
|
67
|
-
models?:
|
|
86
|
+
models?: ModelEntry[];
|
|
87
|
+
/** For a model that routes to several providers (`routedChatModel`): which ones. */
|
|
88
|
+
providers?: {
|
|
68
89
|
id: string;
|
|
69
|
-
label
|
|
90
|
+
label: string;
|
|
70
91
|
}[];
|
|
71
92
|
}
|
|
72
93
|
interface ChatModel extends ChatModelInfo {
|
|
73
94
|
complete(request: ChatRequest, options?: ChatOptions): Promise<ChatResponse>;
|
|
95
|
+
/** Every model the account behind this adapter can use, live from the vendor (for model pickers). */
|
|
96
|
+
listModels?(options?: {
|
|
97
|
+
signal?: AbortSignal;
|
|
98
|
+
}): Promise<ModelEntry[]>;
|
|
74
99
|
}
|
|
75
100
|
/** An error from a model vendor, carrying its HTTP status when there was one. */
|
|
76
101
|
declare class ChatModelError extends Error {
|
|
@@ -82,6 +107,16 @@ type FetchLike = (url: string, init?: RequestInit) => Promise<Response>;
|
|
|
82
107
|
/** Check an adapter's shape once, at registration. */
|
|
83
108
|
declare function defineChatModel<M extends ChatModel>(model: M): M;
|
|
84
109
|
|
|
110
|
+
/**
|
|
111
|
+
* Plumbing the vendor adapters share: POST with retries on rate limits and
|
|
112
|
+
* server errors, server-sent-event reading, and tool-argument parsing.
|
|
113
|
+
*/
|
|
114
|
+
|
|
115
|
+
/** Parse a tool call's JSON arguments; unparseable JSON is reported to the model as a tool error. */
|
|
116
|
+
declare function parseToolArguments(raw: string): Record<string, unknown>;
|
|
117
|
+
/** How long the server asked us to wait (Retry-After header, or "try again in 1.2s / 350ms"), else exponential backoff. */
|
|
118
|
+
declare function retryDelayMs(res: Pick<Response, "headers">, message: string, attempt: number): number;
|
|
119
|
+
|
|
85
120
|
/**
|
|
86
121
|
* OpenAI adapter, over the Chat Completions API with function calling.
|
|
87
122
|
*
|
|
@@ -99,7 +134,7 @@ interface OpenAIChatModelOptions {
|
|
|
99
134
|
label?: string;
|
|
100
135
|
}[];
|
|
101
136
|
/** API key. Omit for local servers that don't need one. Keep it server-side (see `createChatHandler`). */
|
|
102
|
-
apiKey?: string;
|
|
137
|
+
apiKey?: string | undefined;
|
|
103
138
|
/** Default https://api.openai.com/v1. Any OpenAI-compatible base URL works. */
|
|
104
139
|
baseUrl?: string;
|
|
105
140
|
organization?: string;
|
|
@@ -115,14 +150,33 @@ interface OpenAIChatModelOptions {
|
|
|
115
150
|
* backing off 1 s, 2 s, 4 s… (capped at 30 s). Default 4; 0 disables.
|
|
116
151
|
*/
|
|
117
152
|
maxRetries?: number;
|
|
153
|
+
/**
|
|
154
|
+
* The request field for the output cap: "max_completion_tokens" (default;
|
|
155
|
+
* OpenAI) or "max_tokens" (what most other compatible servers read).
|
|
156
|
+
*/
|
|
157
|
+
maxTokensField?: "max_completion_tokens" | "max_tokens";
|
|
158
|
+
/** Ask for token usage at the end of a stream (default true; turn off for servers that reject `stream_options`). */
|
|
159
|
+
streamUsage?: boolean;
|
|
118
160
|
/** Adapter id / label (default "openai" / "OpenAI"), e.g. "ollama" when pointing elsewhere. */
|
|
119
161
|
id?: string;
|
|
120
162
|
label?: string;
|
|
121
163
|
fetch?: FetchLike;
|
|
122
164
|
}
|
|
165
|
+
type OpenAIPart = {
|
|
166
|
+
type: "text";
|
|
167
|
+
text: string;
|
|
168
|
+
} | {
|
|
169
|
+
type: "image_url";
|
|
170
|
+
image_url: {
|
|
171
|
+
url: string;
|
|
172
|
+
};
|
|
173
|
+
};
|
|
123
174
|
type OpenAIMessage = {
|
|
124
175
|
role: "system" | "user";
|
|
125
176
|
content: string;
|
|
177
|
+
} | {
|
|
178
|
+
role: "user";
|
|
179
|
+
content: OpenAIPart[];
|
|
126
180
|
} | {
|
|
127
181
|
role: "assistant";
|
|
128
182
|
content: string | null;
|
|
@@ -141,56 +195,364 @@ type OpenAIMessage = {
|
|
|
141
195
|
};
|
|
142
196
|
/** Neutral messages → Chat Completions messages. */
|
|
143
197
|
declare function toOpenAIMessages(messages: readonly ChatMessage[]): OpenAIMessage[];
|
|
144
|
-
/** Parse a tool call's JSON arguments; unparseable JSON is reported to the model as a tool error. */
|
|
145
|
-
declare function parseToolArguments(raw: string): Record<string, unknown>;
|
|
146
|
-
/** How long the server asked us to wait (Retry-After header, or "try again in 1.2s / 350ms"), else exponential backoff. */
|
|
147
|
-
declare function retryDelayMs(res: Pick<Response, "headers">, message: string, attempt: number): number;
|
|
148
198
|
declare function openAIChatModel(options: OpenAIChatModelOptions): ChatModel;
|
|
149
199
|
|
|
200
|
+
/**
|
|
201
|
+
* Anthropic adapter, over the Messages API with tool use.
|
|
202
|
+
*
|
|
203
|
+
* System messages become the top-level `system` prompt; tool results are
|
|
204
|
+
* grouped into one user turn, as the API expects. When extended thinking is
|
|
205
|
+
* on (`params: { thinking: … }`), the thinking blocks of a tool-using turn
|
|
206
|
+
* ride on the first tool call's `meta` and are sent back on the next request,
|
|
207
|
+
* which the API requires.
|
|
208
|
+
*/
|
|
209
|
+
|
|
210
|
+
interface AnthropicChatModelOptions {
|
|
211
|
+
/** Model id, e.g. "claude-sonnet-5-5". Required: model names change faster than this package. */
|
|
212
|
+
model: string;
|
|
213
|
+
/** Other models the app may pick per request (`ChatRequest.model`). */
|
|
214
|
+
models?: {
|
|
215
|
+
id: string;
|
|
216
|
+
label?: string;
|
|
217
|
+
}[];
|
|
218
|
+
/** API key. Keep it server-side (see `createChatHandler`) unless it's the user's own key in their own browser (`browser`). */
|
|
219
|
+
apiKey?: string | undefined;
|
|
220
|
+
/** Default https://api.anthropic.com (requests go to `{baseUrl}/v1/messages`). */
|
|
221
|
+
baseUrl?: string;
|
|
222
|
+
/** `anthropic-version` header (default "2023-06-01"). */
|
|
223
|
+
version?: string;
|
|
224
|
+
/** `anthropic-beta` flags, joined with commas. */
|
|
225
|
+
betas?: string[];
|
|
226
|
+
headers?: Record<string, string>;
|
|
227
|
+
/** Default request fields merged into every call (e.g. `{ thinking: { type: "enabled", budget_tokens: 4000 } }`). */
|
|
228
|
+
params?: Record<string, unknown>;
|
|
229
|
+
/** The API requires an output cap; used when a request sets none (default 8192). */
|
|
230
|
+
maxOutputTokens?: number;
|
|
231
|
+
/** Stream responses (default true). */
|
|
232
|
+
stream?: boolean;
|
|
233
|
+
/** Retries on 429, 5xx and 529 (overloaded), as the server asks. Default 4; 0 disables. */
|
|
234
|
+
maxRetries?: number;
|
|
235
|
+
/**
|
|
236
|
+
* Calling from a browser with the user's own key: sends the header the API
|
|
237
|
+
* needs to allow cross-origin requests. Never ship your own key this way.
|
|
238
|
+
*/
|
|
239
|
+
browser?: boolean;
|
|
240
|
+
/** Adapter id / label (default "anthropic" / "Anthropic"). */
|
|
241
|
+
id?: string;
|
|
242
|
+
label?: string;
|
|
243
|
+
fetch?: FetchLike;
|
|
244
|
+
}
|
|
245
|
+
type ImageBlock = {
|
|
246
|
+
type: "image";
|
|
247
|
+
source: {
|
|
248
|
+
type: "base64";
|
|
249
|
+
media_type: string;
|
|
250
|
+
data: string;
|
|
251
|
+
};
|
|
252
|
+
};
|
|
253
|
+
type Block = {
|
|
254
|
+
type: "text";
|
|
255
|
+
text: string;
|
|
256
|
+
} | {
|
|
257
|
+
type: "tool_use";
|
|
258
|
+
id: string;
|
|
259
|
+
name: string;
|
|
260
|
+
input: Record<string, unknown>;
|
|
261
|
+
} | {
|
|
262
|
+
type: "tool_result";
|
|
263
|
+
tool_use_id: string;
|
|
264
|
+
content: string | ({
|
|
265
|
+
type: "text";
|
|
266
|
+
text: string;
|
|
267
|
+
} | ImageBlock)[];
|
|
268
|
+
} | {
|
|
269
|
+
type: "thinking";
|
|
270
|
+
thinking: string;
|
|
271
|
+
signature: string;
|
|
272
|
+
} | {
|
|
273
|
+
type: "redacted_thinking";
|
|
274
|
+
data: string;
|
|
275
|
+
};
|
|
276
|
+
type AnthropicMessage = {
|
|
277
|
+
role: "user" | "assistant";
|
|
278
|
+
content: Block[];
|
|
279
|
+
};
|
|
280
|
+
/** Neutral messages → `{ system, messages }` for the Messages API. */
|
|
281
|
+
declare function toAnthropicMessages(messages: readonly ChatMessage[]): {
|
|
282
|
+
system: string;
|
|
283
|
+
messages: AnthropicMessage[];
|
|
284
|
+
};
|
|
285
|
+
declare function anthropicChatModel(options: AnthropicChatModelOptions): ChatModel;
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* Google Gemini adapter, over the Gemini API's `generateContent` with
|
|
289
|
+
* function calling.
|
|
290
|
+
*
|
|
291
|
+
* Tools are sent as full JSON Schema (`parametersJsonSchema`), so the same
|
|
292
|
+
* schemas the other adapters use work unchanged. Gemini's thought signatures
|
|
293
|
+
* (required when thinking models call tools) ride on each tool call's `meta`
|
|
294
|
+
* and are sent back exactly as received.
|
|
295
|
+
*/
|
|
296
|
+
|
|
297
|
+
interface GeminiChatModelOptions {
|
|
298
|
+
/** Model id, e.g. "gemini-3.8-flash". Required: model names change faster than this package. */
|
|
299
|
+
model: string;
|
|
300
|
+
/** Other models the app may pick per request (`ChatRequest.model`). */
|
|
301
|
+
models?: {
|
|
302
|
+
id: string;
|
|
303
|
+
label?: string;
|
|
304
|
+
}[];
|
|
305
|
+
/** Gemini API key. Keep it server-side (see `createChatHandler`) unless it's the user's own key in their own browser. */
|
|
306
|
+
apiKey?: string | undefined;
|
|
307
|
+
/** Default https://generativelanguage.googleapis.com/v1beta. */
|
|
308
|
+
baseUrl?: string;
|
|
309
|
+
headers?: Record<string, string>;
|
|
310
|
+
/** Default request fields merged into every call; `generationConfig` is merged key by key (e.g. `{ generationConfig: { temperature: 0.2 } }`). */
|
|
311
|
+
params?: Record<string, unknown>;
|
|
312
|
+
/** Stream responses (default true). */
|
|
313
|
+
stream?: boolean;
|
|
314
|
+
/** Retries on 429 and 5xx, as the server asks. Default 4; 0 disables. */
|
|
315
|
+
maxRetries?: number;
|
|
316
|
+
/** Adapter id / label (default "gemini" / "Google Gemini"). */
|
|
317
|
+
id?: string;
|
|
318
|
+
label?: string;
|
|
319
|
+
fetch?: FetchLike;
|
|
320
|
+
}
|
|
321
|
+
type Part = {
|
|
322
|
+
text?: string;
|
|
323
|
+
thought?: boolean;
|
|
324
|
+
thoughtSignature?: string;
|
|
325
|
+
functionCall?: {
|
|
326
|
+
id?: string;
|
|
327
|
+
name: string;
|
|
328
|
+
args?: Record<string, unknown>;
|
|
329
|
+
};
|
|
330
|
+
functionResponse?: {
|
|
331
|
+
id?: string;
|
|
332
|
+
name: string;
|
|
333
|
+
response: Record<string, unknown>;
|
|
334
|
+
};
|
|
335
|
+
inlineData?: {
|
|
336
|
+
mimeType: string;
|
|
337
|
+
data: string;
|
|
338
|
+
};
|
|
339
|
+
};
|
|
340
|
+
type GeminiContent = {
|
|
341
|
+
role: "user" | "model";
|
|
342
|
+
parts: Part[];
|
|
343
|
+
};
|
|
344
|
+
/** Neutral messages → `{ systemInstruction, contents }` for `generateContent`. */
|
|
345
|
+
declare function toGeminiContents(messages: readonly ChatMessage[]): {
|
|
346
|
+
system: string;
|
|
347
|
+
contents: GeminiContent[];
|
|
348
|
+
};
|
|
349
|
+
declare function geminiChatModel(options: GeminiChatModelOptions): ChatModel;
|
|
350
|
+
|
|
351
|
+
type ProviderApi = "openai" | "anthropic" | "gemini";
|
|
352
|
+
interface AiProviderInfo {
|
|
353
|
+
/** Id used in configs, env names and "provider/model" references. */
|
|
354
|
+
id: string;
|
|
355
|
+
label: string;
|
|
356
|
+
/** Which wire format it speaks. */
|
|
357
|
+
api: ProviderApi;
|
|
358
|
+
/** API root; `openai-compatible` has none (you give it). */
|
|
359
|
+
baseUrl?: string;
|
|
360
|
+
/** Whether calls need an API key (local servers don't). */
|
|
361
|
+
needsKey: boolean;
|
|
362
|
+
/** Runs on the user's machine (Ollama, LM Studio): reachable from that machine only. */
|
|
363
|
+
local?: boolean;
|
|
364
|
+
/** Where a user creates a key. */
|
|
365
|
+
keyUrl?: string;
|
|
366
|
+
/** Env var prefix: `{PREFIX}_API_KEY`, `{PREFIX}_MODEL`, `{PREFIX}_BASE_URL`, `{PREFIX}_MODELS`. */
|
|
367
|
+
env: string;
|
|
368
|
+
/** Used when no model is named. Providers without one need a model picked (env or UI). */
|
|
369
|
+
defaultModel?: string;
|
|
370
|
+
/** A few good picks to show before the live list loads. */
|
|
371
|
+
models?: ModelEntry[];
|
|
372
|
+
/** Accepts calls straight from a browser (CORS) with the user's own key. */
|
|
373
|
+
browser?: boolean;
|
|
374
|
+
/** OpenAI-format servers: the output-cap field and whether `stream_options` is accepted. */
|
|
375
|
+
maxTokensField?: "max_completion_tokens" | "max_tokens";
|
|
376
|
+
streamUsage?: boolean;
|
|
377
|
+
}
|
|
378
|
+
/**
|
|
379
|
+
* The built-in provider catalog. Model ids are suggestions as of release; the
|
|
380
|
+
* live list comes from each provider (`ChatModel.listModels`).
|
|
381
|
+
*/
|
|
382
|
+
declare const AI_PROVIDERS: readonly AiProviderInfo[];
|
|
383
|
+
/** A provider from the catalog, or undefined. */
|
|
384
|
+
declare function aiProvider(id: string): AiProviderInfo | undefined;
|
|
385
|
+
interface CreateChatModelOptions {
|
|
386
|
+
/** A catalog id (`AI_PROVIDERS`): "openai", "anthropic", "gemini", "openrouter", "ollama", "openai-compatible", … */
|
|
387
|
+
provider: string;
|
|
388
|
+
apiKey?: string | undefined;
|
|
389
|
+
/** Model id (default: the provider's `defaultModel`; required for providers without one). */
|
|
390
|
+
model?: string | undefined;
|
|
391
|
+
/** Other models to offer (default: the catalog's suggestions). */
|
|
392
|
+
models?: ModelEntry[];
|
|
393
|
+
/** Override the API root (required for "openai-compatible"). */
|
|
394
|
+
baseUrl?: string | undefined;
|
|
395
|
+
headers?: Record<string, string>;
|
|
396
|
+
/** Default request fields for every call (vendor-specific). */
|
|
397
|
+
params?: Record<string, unknown>;
|
|
398
|
+
stream?: boolean;
|
|
399
|
+
maxRetries?: number;
|
|
400
|
+
/** Calling from the browser with the user's own key (sets what the vendor needs for CORS). */
|
|
401
|
+
browser?: boolean;
|
|
402
|
+
/** Adapter id / label (default: the provider's). */
|
|
403
|
+
id?: string;
|
|
404
|
+
label?: string;
|
|
405
|
+
fetch?: FetchLike;
|
|
406
|
+
}
|
|
407
|
+
/** One `ChatModel` for any provider in the catalog. */
|
|
408
|
+
declare function createChatModel(options: CreateChatModelOptions): ChatModel;
|
|
409
|
+
interface ChatModelsFromEnvOptions {
|
|
410
|
+
/** Only these providers (default: all). */
|
|
411
|
+
providers?: string[];
|
|
412
|
+
/** Told about providers that have a key but couldn't be set up (e.g. no model). */
|
|
413
|
+
onSkip?: (provider: string, reason: string) => void;
|
|
414
|
+
fetch?: FetchLike;
|
|
415
|
+
}
|
|
416
|
+
/**
|
|
417
|
+
* A model for every provider configured in `env` (`process.env`, Vite's
|
|
418
|
+
* `loadEnv`, a Worker's bindings):
|
|
419
|
+
*
|
|
420
|
+
* {PREFIX}_API_KEY enables a hosted provider (GEMINI also reads GOOGLE_API_KEY)
|
|
421
|
+
* {PREFIX}_MODEL its default model (required where the catalog has none)
|
|
422
|
+
* {PREFIX}_MODELS more models to offer, comma-separated
|
|
423
|
+
* {PREFIX}_BASE_URL another API root (a proxy, a gateway, a regional endpoint)
|
|
424
|
+
*
|
|
425
|
+
* Local servers (OLLAMA, LMSTUDIO) are enabled by their _MODEL or _BASE_URL.
|
|
426
|
+
* OPENAI_BASE_URL alone keeps its old meaning: an OpenAI-compatible server
|
|
427
|
+
* (label OPENAI_LABEL), key optional. Order follows the catalog, so the
|
|
428
|
+
* first entry makes a sensible default.
|
|
429
|
+
*/
|
|
430
|
+
declare function chatModelsFromEnv(env: Record<string, string | undefined>, options?: ChatModelsFromEnvOptions): ChatModel[];
|
|
431
|
+
/** "anthropic/claude-sonnet-5-5" → { provider: "anthropic", model: "claude-sonnet-5-5" } when the prefix is a known id. */
|
|
432
|
+
declare function splitModelRef(ref: string, providerIds: Iterable<string>): {
|
|
433
|
+
provider?: string;
|
|
434
|
+
model: string;
|
|
435
|
+
};
|
|
436
|
+
interface RoutedChatModelOptions {
|
|
437
|
+
id?: string;
|
|
438
|
+
label?: string;
|
|
439
|
+
/** Adapter id used when a request names no provider (default: the first). */
|
|
440
|
+
default?: string;
|
|
441
|
+
}
|
|
442
|
+
/**
|
|
443
|
+
* Several providers as one `ChatModel`. Its models are "provider/model"
|
|
444
|
+
* (`anthropic/claude-sonnet-5-5`, `openrouter/meta-llama/…`); a request's
|
|
445
|
+
* `model` picks the adapter and the model. A bare model id goes to the
|
|
446
|
+
* adapter that lists it, else to the default adapter; no model → the default
|
|
447
|
+
* adapter's default model.
|
|
448
|
+
*/
|
|
449
|
+
declare function routedChatModel(models: readonly ChatModel[], options?: RoutedChatModelOptions): ChatModel;
|
|
450
|
+
|
|
150
451
|
/**
|
|
151
452
|
* Keys stay on the server: run the real model adapter in your backend behind
|
|
152
453
|
* `createChatHandler`, and give the browser `remoteChatModel(endpoint)` — a
|
|
153
454
|
* proxy with the same `ChatModel` shape. The agent loop and the tools run in
|
|
154
455
|
* the browser, next to the project; only model calls cross the network.
|
|
155
456
|
*
|
|
156
|
-
* GET {base}/ → { id, label, models }
|
|
457
|
+
* GET {base}/ → { id, label, models, providers?, userKeys? }
|
|
458
|
+
* GET {base}/models → { models } (live from the vendor when it can list them)
|
|
157
459
|
* POST {base}/chat { request: ChatRequest } →
|
|
158
460
|
* application/x-ndjson, one JSON object per line:
|
|
159
461
|
* { "type": "text", "delta": "…" } (as it streams)
|
|
160
462
|
* { "type": "done", "response": ChatResponse }
|
|
161
463
|
* { "type": "error", "error": "…", "status": 401 }
|
|
162
|
-
* errors before streaming → { error } with 400 / 404 / 413, or the vendor's status
|
|
464
|
+
* errors before streaming → { error } with 400 / 401 / 403 / 404 / 413, or the vendor's status
|
|
465
|
+
*
|
|
466
|
+
* Bring your own key (`userKeys`): a request may carry the user's own
|
|
467
|
+
* provider and key in headers (`x-ai-provider`, `x-ai-key`, optionally
|
|
468
|
+
* `x-ai-base-url`). The handler builds a model for that one request and
|
|
469
|
+
* forgets the key — it's never stored or logged. Use it for plans where
|
|
470
|
+
* customers pay their AI vendor directly.
|
|
163
471
|
*
|
|
164
472
|
* Fetch-API handler (`Request` → `Response`): mounts in Node 18+, edge
|
|
165
473
|
* runtimes, Next, Hono, Express (via an adapter).
|
|
166
474
|
*/
|
|
167
475
|
|
|
476
|
+
/** Header names a request uses to bring the user's own key. */
|
|
477
|
+
declare const USER_KEY_HEADERS: {
|
|
478
|
+
readonly provider: "x-ai-provider";
|
|
479
|
+
readonly key: "x-ai-key";
|
|
480
|
+
readonly baseUrl: "x-ai-base-url";
|
|
481
|
+
};
|
|
482
|
+
/** The user's own provider settings, as the browser sends them. */
|
|
483
|
+
interface UserCredentials {
|
|
484
|
+
provider: string;
|
|
485
|
+
apiKey?: string;
|
|
486
|
+
/** Only honored when the server allows it (`userKeys.allowBaseUrl`). */
|
|
487
|
+
baseUrl?: string;
|
|
488
|
+
}
|
|
489
|
+
interface UserKeyOptions {
|
|
490
|
+
/** Providers users may bring a key for (default: every hosted provider in the catalog). */
|
|
491
|
+
providers?: string[];
|
|
492
|
+
/**
|
|
493
|
+
* Let users send their own API root (OpenAI-compatible servers, gateways).
|
|
494
|
+
* Off by default: your server would call whatever URL a user sends.
|
|
495
|
+
*/
|
|
496
|
+
allowBaseUrl?: boolean;
|
|
497
|
+
/** Refuse requests without the user's own key, even when the server has a model. */
|
|
498
|
+
required?: boolean;
|
|
499
|
+
/** Build the model for a user's key (default `createChatModel`): add headers, pin params, wrap with limits. */
|
|
500
|
+
create?: (options: CreateChatModelOptions, request: Request) => ChatModel;
|
|
501
|
+
fetch?: FetchLike;
|
|
502
|
+
}
|
|
168
503
|
interface ChatHandlerOptions {
|
|
169
504
|
/** Path prefix the handler is mounted at, e.g. "/api/assistant". */
|
|
170
505
|
basePath?: string;
|
|
171
506
|
/** Reject request bodies larger than this (default 2 MB). */
|
|
172
507
|
maxBodyBytes?: number;
|
|
173
|
-
/** Return a Response to refuse a request (auth, rate limits); undefined to allow. */
|
|
508
|
+
/** Return a Response to refuse a request (auth, rate limits, plan checks); undefined to allow. */
|
|
174
509
|
authorize?: (request: Request) => Promise<Response | undefined> | Response | undefined;
|
|
175
510
|
/** Adjust each request server-side (pin a model, cap tokens, add instructions). */
|
|
176
511
|
prepare?: (request: ChatRequest) => ChatRequest;
|
|
512
|
+
/** Accept the user's own provider key per request (see the module note). Off by default. */
|
|
513
|
+
userKeys?: boolean | UserKeyOptions;
|
|
514
|
+
}
|
|
515
|
+
/** What `GET {base}/` tells the browser about bring-your-own-key. */
|
|
516
|
+
interface UserKeyInfo {
|
|
517
|
+
providers: Pick<AiProviderInfo, "id" | "label" | "needsKey" | "keyUrl" | "defaultModel" | "models">[];
|
|
518
|
+
allowBaseUrl: boolean;
|
|
519
|
+
required: boolean;
|
|
520
|
+
}
|
|
521
|
+
/**
|
|
522
|
+
* @param model The server's own model (its keys, from env or config), or
|
|
523
|
+
* `null` when every user brings their own key (`userKeys` must be on).
|
|
524
|
+
*/
|
|
525
|
+
declare function createChatHandler(model: ChatModel | null, options?: ChatHandlerOptions): (request: Request) => Promise<Response | null>;
|
|
526
|
+
/** What the server said about itself (`GET {base}/`). */
|
|
527
|
+
interface RemoteModelInfo {
|
|
528
|
+
id: string;
|
|
529
|
+
label: string;
|
|
530
|
+
models?: ModelEntry[];
|
|
531
|
+
providers?: {
|
|
532
|
+
id: string;
|
|
533
|
+
label: string;
|
|
534
|
+
}[];
|
|
535
|
+
userKeys?: UserKeyInfo;
|
|
536
|
+
/** The server has no model of its own (or requires user keys): calls need the user's key. */
|
|
537
|
+
needsKey?: boolean;
|
|
177
538
|
}
|
|
178
|
-
declare function createChatHandler(model: ChatModel, options?: ChatHandlerOptions): (request: Request) => Promise<Response | null>;
|
|
179
539
|
interface RemoteChatModelOptions {
|
|
180
540
|
fetch?: FetchLike;
|
|
181
541
|
headers?: Record<string, string>;
|
|
182
542
|
/** Skip the GET for model info (id/label/models default to "remote"). */
|
|
183
|
-
info?:
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
}[];
|
|
190
|
-
};
|
|
543
|
+
info?: RemoteModelInfo;
|
|
544
|
+
/**
|
|
545
|
+
* The user's own provider key, read on every call (so a settings change
|
|
546
|
+
* applies at once). Return nothing to use the server's model.
|
|
547
|
+
*/
|
|
548
|
+
credentials?: () => UserCredentials | null | undefined;
|
|
191
549
|
}
|
|
192
|
-
/**
|
|
193
|
-
declare function
|
|
550
|
+
/** Headers carrying the user's own key, for any request to a `createChatHandler` server. */
|
|
551
|
+
declare function userKeyHeaders(credentials: UserCredentials | null | undefined): Record<string, string>;
|
|
552
|
+
/** The model a backend serves, as a browser-side `ChatModel` (plus what the server said about itself in `info`). */
|
|
553
|
+
declare function remoteChatModel(endpoint: string, options?: RemoteChatModelOptions): Promise<ChatModel & {
|
|
554
|
+
info: RemoteModelInfo;
|
|
555
|
+
}>;
|
|
194
556
|
|
|
195
557
|
interface RateLimitOptions {
|
|
196
558
|
/** Tokens per rolling minute across every caller of this model (input + output). */
|
|
@@ -224,16 +586,32 @@ interface UnknownFields {
|
|
|
224
586
|
/** Unknown payload fields for a command, judged against the best-matching schema branch. */
|
|
225
587
|
declare function unknownFields(schema: unknown, payload: unknown): UnknownFields | null;
|
|
226
588
|
|
|
589
|
+
/**
|
|
590
|
+
* Checks an html clip's template the way the renderer will read it: as XML
|
|
591
|
+
* (it rasterizes through an SVG `foreignObject`). Markup that isn't
|
|
592
|
+
* well-formed renders nothing, silently, so the assistant rejects it up front
|
|
593
|
+
* with an error the model can fix.
|
|
594
|
+
*/
|
|
595
|
+
/** Why the markup won't parse as XML, or undefined when it's well-formed. */
|
|
596
|
+
declare function markupProblem(markup: string): string | undefined;
|
|
597
|
+
/** `asset:<id>` references in a template. */
|
|
598
|
+
declare function assetRefs(markup: string): string[];
|
|
599
|
+
|
|
227
600
|
/** What a tool's `run` gets. `project` is the turn's working copy. */
|
|
228
601
|
interface ToolContext {
|
|
229
602
|
project: Project;
|
|
230
603
|
signal?: AbortSignal;
|
|
231
604
|
}
|
|
232
|
-
/**
|
|
605
|
+
/**
|
|
606
|
+
* A tool's answer. `changes` are human-readable lines for the app's "what
|
|
607
|
+
* changed" list. `images` go to the model with the result, so it can look at
|
|
608
|
+
* what it made (rendered frames); models that can't see get the text only.
|
|
609
|
+
*/
|
|
233
610
|
type ToolOutput = {
|
|
234
611
|
ok: true;
|
|
235
612
|
result?: unknown;
|
|
236
613
|
changes?: string[];
|
|
614
|
+
images?: ChatImage[];
|
|
237
615
|
} | {
|
|
238
616
|
ok: false;
|
|
239
617
|
error: unknown;
|
|
@@ -245,7 +623,8 @@ declare function defineTool(tool: AssistantTool): AssistantTool;
|
|
|
245
623
|
/**
|
|
246
624
|
* Adopt tools that come as definitions plus one runner — e.g.
|
|
247
625
|
* `@miraiclip/audio-sources`' `audioToolDefinitions` + `runAudioTool`.
|
|
248
|
-
* Accepts `{ name, description, input_schema }
|
|
626
|
+
* Accepts `{ name, description, input_schema }`, OpenAI function tools or
|
|
627
|
+
* Gemini function declarations.
|
|
249
628
|
*/
|
|
250
629
|
declare function toolsFromDefinitions(definitions: readonly unknown[], run: (name: string, input: Record<string, unknown>, ctx: ToolContext) => Promise<{
|
|
251
630
|
ok: boolean;
|
|
@@ -263,6 +642,13 @@ interface EditorToolsOptions {
|
|
|
263
642
|
transitionKinds?: string[];
|
|
264
643
|
/** The command catalog to offer in apply_commands (default: the built-ins). Pass `project.commandCatalog()` when you register custom commands. */
|
|
265
644
|
commandCatalog?: Record<string, unknown>;
|
|
645
|
+
/**
|
|
646
|
+
* Runs the continuous-shot check over the document (e.g. `checkShot` from `@miraiclip/server-export`, or the app's
|
|
647
|
+
* in-browser equivalent). The `check_sequence` tool is offered only when this is given, because the check needs a browser.
|
|
648
|
+
*/
|
|
649
|
+
checkSequence?: (doc: ProjectDocument, options: {
|
|
650
|
+
clipId?: string;
|
|
651
|
+
}) => Promise<unknown>;
|
|
266
652
|
}
|
|
267
653
|
/**
|
|
268
654
|
* The tools that edit a Miraiclip project: read state, look up a command's
|
|
@@ -315,12 +701,75 @@ type CommitResult = {
|
|
|
315
701
|
/** Replay a fork's commands onto the real project as one undoable step. */
|
|
316
702
|
declare function commitFork(project: Project, fork: RecordingProject, label?: string): CommitResult;
|
|
317
703
|
|
|
704
|
+
/**
|
|
705
|
+
* Skills: playbooks for bigger jobs (a launch video, captions, a reformat),
|
|
706
|
+
* loaded by the model only when a request needs one.
|
|
707
|
+
*
|
|
708
|
+
* The system prompt lists each skill's name and description. The model calls
|
|
709
|
+
* `load_skill` to read one, and `read_skill_file` for the reference files it
|
|
710
|
+
* points to, so the prompt stays small however many skills there are. An app
|
|
711
|
+
* can also start a request with a skill already loaded (`TurnOptions.skills`),
|
|
712
|
+
* e.g. from a button.
|
|
713
|
+
*
|
|
714
|
+
* Skills are plain Markdown with a front matter (`name`, `description`), the
|
|
715
|
+
* same shape as Claude Code skills. `MIRAICLIP_SKILLS` are the Miraiclip
|
|
716
|
+
* plugin's skills, in their editor versions.
|
|
717
|
+
*/
|
|
718
|
+
interface AssistantSkill {
|
|
719
|
+
/** Lowercase letters, digits and dashes, e.g. "captions". */
|
|
720
|
+
name: string;
|
|
721
|
+
/** When to use it, in one or two sentences. The model picks skills by this. */
|
|
722
|
+
description: string;
|
|
723
|
+
/** The playbook (Markdown, without the front matter). */
|
|
724
|
+
instructions: string;
|
|
725
|
+
/** Reference files the playbook points to, by relative path ("reference/craft.md"). */
|
|
726
|
+
files?: Readonly<Record<string, string>>;
|
|
727
|
+
}
|
|
728
|
+
declare function defineSkill(skill: AssistantSkill): AssistantSkill;
|
|
729
|
+
/** Split `---\nkey: value\n---\nbody` into its fields and body. Values may be quoted. */
|
|
730
|
+
declare function parseFrontMatter(markdown: string): {
|
|
731
|
+
data: Record<string, string>;
|
|
732
|
+
body: string;
|
|
733
|
+
};
|
|
734
|
+
/** One skill from its Markdown (front matter `name` and `description`). */
|
|
735
|
+
declare function parseSkill(markdown: string, options?: {
|
|
736
|
+
name?: string;
|
|
737
|
+
description?: string;
|
|
738
|
+
files?: Record<string, string>;
|
|
739
|
+
}): AssistantSkill;
|
|
740
|
+
interface SkillsFromFilesOptions {
|
|
741
|
+
/**
|
|
742
|
+
* Which file in a skill folder is the playbook, in order of preference.
|
|
743
|
+
* Default `["SKILL.md"]`. The Miraiclip plugin keeps the Claude Code
|
|
744
|
+
* version in SKILL.md and the in-editor version in EDITOR.md, so an editor
|
|
745
|
+
* passes `["EDITOR.md"]` (folders without one are skipped). The other
|
|
746
|
+
* entry file's front matter fills in a missing name or description.
|
|
747
|
+
*/
|
|
748
|
+
entry?: string[];
|
|
749
|
+
}
|
|
750
|
+
/**
|
|
751
|
+
* Skills from a folder of skill folders, given as `{ path: text }` — e.g.
|
|
752
|
+
* Vite's `import.meta.glob("./skills/**\/*.md", { query: "?raw", import: "default", eager: true })`.
|
|
753
|
+
* Every other `.md` in a skill's folder becomes one of its files.
|
|
754
|
+
*/
|
|
755
|
+
declare function skillsFromFiles(files: Record<string, string>, options?: SkillsFromFilesOptions): AssistantSkill[];
|
|
756
|
+
/** The skill list for the system prompt. */
|
|
757
|
+
declare function describeSkills(skills: readonly AssistantSkill[]): string;
|
|
758
|
+
/** `load_skill` and `read_skill_file` for these skills. */
|
|
759
|
+
declare function skillTools(skills: readonly AssistantSkill[]): AssistantTool[];
|
|
760
|
+
|
|
318
761
|
interface AssistantOptions {
|
|
319
762
|
model: ChatModel;
|
|
320
763
|
/** Default `editorTools()`. Add your own (audio, captions, stock media…). */
|
|
321
764
|
tools?: AssistantTool[];
|
|
322
765
|
/** Extra system instructions (brand voice, house rules). */
|
|
323
766
|
instructions?: string;
|
|
767
|
+
/**
|
|
768
|
+
* Playbooks the model can load when a request needs one (e.g.
|
|
769
|
+
* `MIRAICLIP_SKILLS`). Adds `load_skill` and `read_skill_file` to the tools
|
|
770
|
+
* and lists each skill's name and description in the system prompt.
|
|
771
|
+
*/
|
|
772
|
+
skills?: readonly AssistantSkill[];
|
|
324
773
|
/** Model round-trips per request (default 12). */
|
|
325
774
|
maxSteps?: number;
|
|
326
775
|
/** Model id when the adapter serves several. */
|
|
@@ -342,6 +791,8 @@ type AssistantEvent = {
|
|
|
342
791
|
ok: boolean;
|
|
343
792
|
changes?: string[];
|
|
344
793
|
error?: string;
|
|
794
|
+
/** Images the tool returned (previews, storyboards) — the model sees them too; apps may show them. */
|
|
795
|
+
images?: ChatImage[];
|
|
345
796
|
} | {
|
|
346
797
|
type: "step";
|
|
347
798
|
index: number;
|
|
@@ -357,6 +808,13 @@ interface TurnOptions {
|
|
|
357
808
|
onEvent?: (event: AssistantEvent) => void;
|
|
358
809
|
/** History label for the undo step (default "Assistant: <request>"). */
|
|
359
810
|
label?: string;
|
|
811
|
+
/**
|
|
812
|
+
* Skills to load up front for this request, by name (e.g. the user picked
|
|
813
|
+
* "Captions" in the app). Their playbooks go into the system prompt, so the
|
|
814
|
+
* model starts on them without a `load_skill` call. Unknown names are
|
|
815
|
+
* ignored.
|
|
816
|
+
*/
|
|
817
|
+
skills?: readonly string[];
|
|
360
818
|
}
|
|
361
819
|
interface AssistantTurn {
|
|
362
820
|
/** The model's final answer to the user. */
|
|
@@ -387,6 +845,9 @@ interface Assistant {
|
|
|
387
845
|
declare const DEFAULT_INSTRUCTIONS = "You are the editing assistant inside a video editor. You change the user's project by calling tools; every change you make lands as ONE undo step when you finish, so act confidently.\n\nHow to work:\n- The current project is summarized below. Clip, track and asset ids there are what tools expect. Call get_state to re-read it after big changes.\n- Prefer the high-level tools when they fit (add_transition, animate_clip, add_effect, set_effects_enabled, set_background, trim_clip, set_keyframes, close_gaps, and any audio or media tools). Call tools directly; apply_commands takes only editing commands like \"clip/add\", never tool names. Use apply_commands for everything else, batching related commands; look up a command's payload with get_command_schema the first time you use it, and use only the fields it lists.\n- Fading a picture (text, image, logo, video) in or out is animate_clip (in / out: fade). fadeInUs / fadeOutUs are audio fades. \"Fade the video in at the start / out at the end\" means the first clip's entrance and the last clip's exit.\n- Constant values are set directly with clip/set-property: rotation (\"rotate 45\u00B0\", \"tilt\"), volume (\"lower to 30%\", \"louder\"), position, size, opacity. Only values that CHANGE over a time range (\"duck the music between 10 and 15 s\", \"fade to silence between 18 and 20 s\", \"spin one full turn\", \"pan across\") are keyframes: set_keyframes, with times in timeline seconds. Remove an animation with keyframe/clear.\n- \"Cuts\" are the ones listed in the project summary; use their times (e.g. one sound per cut).\n- \"Add N more seconds of footage\" adds a new clip (clip/add with trimStartUs for the source point) after the last one; it does not stretch an existing clip.\n- Caption looks (box behind the text, outline, shadow, colors, word preset) are the caption clip's style (clip/set-property { style }).\n- \"The whole video\" means from 0 to the composition length (trim_clip startSeconds / endSeconds).\n- A solid color background behind everything is set_background (add, recolor, remove). Other html clips are recolored through the params their template uses (shown in the clip details). A new track goes on top unless track/add gets index 0 (the bottom).\n- \"Replace X with Y\" removes X and adds Y in the same turn, whichever tool adds Y (audio tools take replaceClipId, which does both).\n- Solo / mute / lock / hide a track with track/set-property (solo, muted, locked, hidden). \"Solo X\" is solo: true on X, not muting the others.\n- Relative requests (\"bigger\", \"louder\", \"a bit brighter\", \"twice as big\") start from the current values in the clip details below: make a visible change in the asked direction (about 25% for \"bigger\"/\"smaller\", \u00D72 for \"twice\").\n- When something is selected, \"this\", \"it\", \"the clip\" and any request that names no target mean ONLY the selection, even with words like \"at the end\" (the end of that clip). Edit nothing else. \"Here\" / \"now\" / \"this point\" mean the playhead. \"Clips\" without a kind mean the video clips on the main video track.\n- \"Again\" / \"also\" / \"another\" mean keep the original and add a copy (clip/duplicate, or clip/add with the same asset); never move the original. \"Instead\" means move or replace.\n- Removing a clip leaves a gap; when asked to close it, also run close_gaps (or trim_clip with ripple).\n- Commands use integer microseconds (1 second = 1000000). High-level tools take seconds.\n- Positions (transform x, y) are fractions of the frame: 0.5, 0.5 is the center.\n- Only reference media that exists in the project or that a tool returned. Never invent file URLs.\n- If a tool fails, read the error, fix the input and retry. A failed apply_commands batch applied NONE of its commands: resend all of them, corrected. If something can't be done, say why.\n- If the request is ambiguous in a way that matters, ask one short question instead of guessing.\n\nWhen you're done, reply in one to three short sentences: what you changed, in plain words (times in seconds, names over ids). Describe only edits your tools reported as applied; if a tool says something changed nothing or failed, fix it or say so. No markdown headings.";
|
|
388
846
|
declare function createAssistant(options: AssistantOptions): Assistant;
|
|
389
847
|
|
|
848
|
+
/** The Miraiclip plugin's skills, in their in-editor versions: pass to `createAssistant({ skills })`. */
|
|
849
|
+
declare const MIRAICLIP_SKILLS: readonly AssistantSkill[];
|
|
850
|
+
|
|
390
851
|
type ScriptStep = string | {
|
|
391
852
|
text?: string;
|
|
392
853
|
toolCalls?: (Omit<ToolCall, "id"> & {
|
|
@@ -404,4 +865,4 @@ declare function scriptedChatModel(steps: ScriptStep[], options?: {
|
|
|
404
865
|
requests: ChatRequest[];
|
|
405
866
|
};
|
|
406
867
|
|
|
407
|
-
export { type Assistant, type AssistantEvent, type AssistantOptions, type AssistantTool, type AssistantTurn, BACKGROUND_TEMPLATE, type ChatHandlerOptions, type ChatMessage, type ChatModel, ChatModelError, type ChatModelInfo, type ChatOptions, type ChatRequest, type ChatResponse, type ChatUsage, type CommitResult, DEFAULT_INSTRUCTIONS, type EditorToolsOptions, type FetchLike, type ForkOptions, type OpenAIChatModelOptions, type RateLimitOptions, type RecordingProject, type RemoteChatModelOptions, type ScriptStep, type ToolCall, type ToolContext, type ToolOutput, type ToolSpec, type TurnOptions, type UnknownFields, applyChecked, applyCommandsSchema, commitFork, createAssistant, createChatHandler, defineChatModel, defineTool, describeCommands, describeForAssistant, editorTools, forkProject, openAIChatModel, parseToolArguments, rateLimitedChatModel, remoteChatModel, retryDelayMs, scriptedChatModel, toOpenAIMessages, toolsFromDefinitions, unknownFields, withExplicitIds };
|
|
868
|
+
export { AI_PROVIDERS, type AiProviderInfo, type AnthropicChatModelOptions, type Assistant, type AssistantEvent, type AssistantOptions, type AssistantSkill, type AssistantTool, type AssistantTurn, BACKGROUND_TEMPLATE, type ChatHandlerOptions, type ChatImage, type ChatMessage, type ChatModel, ChatModelError, type ChatModelInfo, type ChatModelsFromEnvOptions, type ChatOptions, type ChatRequest, type ChatResponse, type ChatUsage, type CommitResult, type CreateChatModelOptions, DEFAULT_INSTRUCTIONS, type EditorToolsOptions, type FetchLike, type ForkOptions, type GeminiChatModelOptions, MIRAICLIP_SKILLS, type ModelEntry, type OpenAIChatModelOptions, type ProviderApi, type RateLimitOptions, type RecordingProject, type RemoteChatModelOptions, type RemoteModelInfo, type RoutedChatModelOptions, type ScriptStep, type SkillsFromFilesOptions, type ToolCall, type ToolContext, type ToolOutput, type ToolSpec, type TurnOptions, USER_KEY_HEADERS, type UnknownFields, type UserCredentials, type UserKeyInfo, type UserKeyOptions, aiProvider, anthropicChatModel, applyChecked, applyCommandsSchema, assetRefs, chatModelsFromEnv, commitFork, createAssistant, createChatHandler, createChatModel, defineChatModel, defineSkill, defineTool, describeCommands, describeForAssistant, describeSkills, editorTools, forkProject, geminiChatModel, markupProblem, openAIChatModel, parseFrontMatter, parseSkill, parseToolArguments, rateLimitedChatModel, remoteChatModel, retryDelayMs, routedChatModel, scriptedChatModel, skillTools, skillsFromFiles, splitModelRef, toAnthropicMessages, toGeminiContents, toOpenAIMessages, toolsFromDefinitions, unknownFields, userKeyHeaders, withExplicitIds };
|