octaflow 0.34.0 → 0.35.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/README.md +23 -10
- package/bin/octaflow-runner.mjs +209 -0
- package/dist/ai/providers/index.d.ts +1 -1
- package/dist/ai/providers/index.js +1 -1
- package/dist/ai/runner/index.d.ts +29 -5
- package/dist/ai/runner/index.js +224 -67
- package/dist/drizzle/index.d.ts +18 -0
- package/dist/drizzle/index.js +19 -1
- package/dist/{hub-SXfvoqH3.d.ts → hub-CTf95SkP.d.ts} +30 -3
- package/dist/{model-DY-oJny8.js → model-DztyiOGz.js} +45 -4
- package/dist/{model-runner-wSk3p4m5.d.ts → model-runner-2ONkybAq.d.ts} +33 -4
- package/dist/postgres/index.d.ts +9 -5
- package/dist/postgres/index.js +62 -20
- package/package.json +12 -8
- package/skills/serve-a-model-from-a-runner/SKILL.md +154 -0
package/README.md
CHANGED
|
@@ -218,7 +218,7 @@ next to what your steps actually do.
|
|
|
218
218
|
| 🔭 | **Observability** — lifecycle events (run history), per-step spans, and `createMeterObserver` turning the events into OpenTelemetry-style metrics — all pluggable, all structural |
|
|
219
219
|
| 🤖 | **AI add-on** — instrumented models, token/cost capture, quota, daily rollups, a `usage` sink that books each step into `@octabits-io/metering` (the paid APIs around the model in the same table, priced at read time), refusal-aware models, lenient JSON from local models (`withJsonRepair`), a vendor's refusal explained by key and hint (`explainProviderError`), a per-run circuit breaker, a connection test (`testProvider`), Ollama's native API as language and embedding models (`createOllama` in `octaflow/ai/providers`: `num_ctx`, thinking and `keep_alive` per model or per call, no dependency) |
|
|
220
220
|
| 🗂️ | **Model providers an administrator manages** — `createModelProviders` (`octaflow/ai`): providers as rows, each with its kind, server, region, a key that crosses a cipher seam, and its models with declared capabilities (temperature, JSON mode, vision, video, tools, embedding); `createPgModelProviderStore` + `modelProviderDdl` (`octaflow/postgres`), `modelProviderColumns` (`octaflow/drizzle`); `createModelResolver` (`octaflow/ai/providers`) builds the AI SDK model from a row, shapes each call to what the model declares, hands the host its `destination` for the disclosure guard, and rebuilds after a write |
|
|
221
|
-
| 🏃 | **Model runners** — a model the app cannot reach, served by a process that connects *out*: a provider row of kind `runner` carries tags instead of a URL, a runner registered with a token (`octaflow/ai/runner`: `createModelRunnerHub`, shown once, kept as a hash) subscribes with its tags, claims the calls by long-poll through `@octabits-io/server/fetch/octaflow`'s `createModelRunnerHandler` and posts the answers back; the mailbox is
|
|
221
|
+
| 🏃 | **Model runners** — a model the app cannot reach, served by a process that connects *out*: a provider row of kind `runner` carries tags instead of a URL, a runner registered with a token (`octaflow/ai/runner`: `createModelRunnerHub`, shown once, kept as a hash) subscribes with its tags, claims the calls by long-poll through `@octabits-io/server/fetch/octaflow`'s `createModelRunnerHandler` and posts the answers back; the mailbox is three tables (`createPgModelRunnerStore` + `modelRunnerDdl`; `modelRunnerColumns`, `modelRunnerRequestColumns`, `modelRunnerChunkColumns`) whose rows live only while a call is in flight, a streamed answer's parts chunk by chunk among them; nobody online fails the call at once, so the breaker trips and the probe answers in milliseconds; `octaflow-runner` is the runner as a process (Ollama or any OpenAI-compatible server) |
|
|
222
222
|
| 🧱 | **Pluggable everything** — `WorkflowStore`, `Dispatcher`, `StepGate`, `FlowObserver`, hooks |
|
|
223
223
|
|
|
224
224
|
---
|
|
@@ -251,10 +251,10 @@ pnpm add ai @ai-sdk/provider
|
|
|
251
251
|
|
|
252
252
|
### The Postgres tables
|
|
253
253
|
|
|
254
|
-
`octaflow/postgres` owns
|
|
254
|
+
`octaflow/postgres` owns nine tables and ships their DDL: `flowStoreDdl()` (`flow_workflow`,
|
|
255
255
|
`flow_workflow_step`), `flowEventDdl()` (`flow_step_event`), `flowGateDdl()`
|
|
256
256
|
(`flow_rate_bucket`, `flow_step_lease`), `modelProviderDdl()` (`model_provider`) and
|
|
257
|
-
`modelRunnerDdl()` (`model_runner`, `model_runner_request`). Each is idempotent `CREATE TABLE IF NOT EXISTS`, takes
|
|
257
|
+
`modelRunnerDdl()` (`model_runner`, `model_runner_request`, `model_runner_chunk`). Each is idempotent `CREATE TABLE IF NOT EXISTS`, takes
|
|
258
258
|
a `schema`, and is meant to be pasted into your own migration system; `applySchema(pool, ddl)`
|
|
259
259
|
is the dev and test shortcut.
|
|
260
260
|
|
|
@@ -273,8 +273,8 @@ migration. Where the store's queries are one SQL statement by nature — every o
|
|
|
273
273
|
is — `./drizzle` ships the columns only, and the store stays the SQL one. So
|
|
274
274
|
`octaflow/drizzle` exports `flowWorkflowColumns`, `flowWorkflowStepColumns`,
|
|
275
275
|
`flowStepEventColumns`, `flowRateBucketColumns`, `flowStepLeaseColumns`, `modelProviderColumns`,
|
|
276
|
-
`modelRunnerColumns` and `
|
|
277
|
-
nothing that runs a query. The foreign keys, the composite keys, the unique
|
|
276
|
+
`modelRunnerColumns`, `modelRunnerRequestColumns` and `modelRunnerChunkColumns` (`drizzle-orm` an
|
|
277
|
+
optional peer) and nothing that runs a query. The foreign keys, the composite keys, the unique
|
|
278
278
|
constraint and the indexes are yours to declare on the `pgTable`; each column set's doc lists
|
|
279
279
|
the ones its DDL creates, and two of them the store relies on:
|
|
280
280
|
|
|
@@ -343,7 +343,8 @@ const { token } = (await runners.registerRunner({ id: 'dani-mac', label: "Dani's
|
|
|
343
343
|
import { createModelRunnerHandler } from '@octabits-io/server/fetch/octaflow';
|
|
344
344
|
export const POST = compose(withErrorMapping())(createModelRunnerHandler({ prefix: '/api/model-runner', hub: runners }));
|
|
345
345
|
|
|
346
|
-
// the runner, on the machine with the model
|
|
346
|
+
// the runner, on the machine with the model — the bin, or the loop in your own process
|
|
347
|
+
// npx octaflow-runner --url https://app.example/api/model-runner --token omr_… --ollama http://localhost:11434
|
|
347
348
|
import { createHttpRunnerClient, runModelRunner } from 'octaflow/ai/runner';
|
|
348
349
|
import { createOllama } from 'octaflow/ai/providers';
|
|
349
350
|
await runModelRunner({
|
|
@@ -352,6 +353,15 @@ await runModelRunner({
|
|
|
352
353
|
});
|
|
353
354
|
```
|
|
354
355
|
|
|
356
|
+
**`octaflow-runner`**, the bin, is the runner as a process: `--url` and `--token` (or
|
|
357
|
+
`OCTAFLOW_RUNNER_URL` / `OCTAFLOW_RUNNER_TOKEN`), one backend — `--ollama <url>` for Ollama's
|
|
358
|
+
native API, `--openai-compatible <url>` (with `--api-key`) for mlx_lm.server, vLLM or llama.cpp
|
|
359
|
+
through the optional `@ai-sdk/openai-compatible` peer — and `--concurrency`. At start it says
|
|
360
|
+
who the token belongs to, which models the app's provider rows declare for its tags (the door
|
|
361
|
+
tells it) and which of them the local server has, with the `ollama pull` for a missing one;
|
|
362
|
+
`--check` does that and exits. A door it cannot reach is retried with a backoff, forever.
|
|
363
|
+
The skill `serve-a-model-from-a-runner` walks through the four pieces.
|
|
364
|
+
|
|
355
365
|
A runner may claim a request when it has **every tag** of the request — the provider row's
|
|
356
366
|
tags — as a CI job's labels must all be on the runner. A call with nobody online for its
|
|
357
367
|
tags fails at once with `provider_unreachable`, so `withCircuitBreaker` trips and
|
|
@@ -363,10 +373,13 @@ every provider, and the disclosure gate reads it the same way.
|
|
|
363
373
|
|
|
364
374
|
The wire is the AI SDK's own call and answer as JSON — binary file data as base64, a URL
|
|
365
375
|
restored on the other side — so the runner's backend is any `LanguageModelV4`: `createOllama`
|
|
366
|
-
from this package, or `@ai-sdk/openai-compatible` for mlx_lm.server, vLLM and llama.cpp.
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
376
|
+
from this package, or `@ai-sdk/openai-compatible` for mlx_lm.server, vLLM and llama.cpp.
|
|
377
|
+
`streamText` streams: the runner runs `doStream` and posts the parts in chunks as they come
|
|
378
|
+
(every 100 ms, or 64 parts), a third table carries them, and the app emits each chunk as it
|
|
379
|
+
lands; a chunk the door refuses tells the runner the caller gave up, and it cancels the
|
|
380
|
+
stream. A runner on the older wire answers a stream whole, and the app emits that as parts
|
|
381
|
+
too. Files reach the runner as bytes, since the runner model declares no URL support and the
|
|
382
|
+
SDK downloads on the app's side. Embeddings go the same way.
|
|
370
383
|
|
|
371
384
|
---
|
|
372
385
|
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* octaflow-runner — serve a model to an app from a machine the app cannot reach.
|
|
4
|
+
*
|
|
5
|
+
* The runner connects *out* to the app's door (`@octabits-io/server/fetch/octaflow`'s
|
|
6
|
+
* `createModelRunnerHandler`) with the token an administrator minted for it, asks for the model
|
|
7
|
+
* calls that carry its tags and runs them on the server beside it — Ollama, or anything with
|
|
8
|
+
* OpenAI's chat API (mlx_lm.server, vLLM, llama.cpp) — and posts the answers back. No port is
|
|
9
|
+
* opened; the machine needs a way out and nothing else.
|
|
10
|
+
*
|
|
11
|
+
* octaflow-runner --url https://app.example/api/model-runner --token omr_… --ollama http://localhost:11434
|
|
12
|
+
* octaflow-runner --url … --token … --openai-compatible http://localhost:8081/v1 [--api-key …]
|
|
13
|
+
*
|
|
14
|
+
* --url <url> the door; or OCTAFLOW_RUNNER_URL
|
|
15
|
+
* --token <token> the runner's token, shown once when it was registered; or OCTAFLOW_RUNNER_TOKEN
|
|
16
|
+
* --ollama <url> serve from Ollama's native API at this URL (num_ctx, thinking, keep_alive per model honoured)
|
|
17
|
+
* --openai-compatible <url> serve from an OpenAI-compatible server at this base URL (its `/v1`)
|
|
18
|
+
* --api-key <key> the key that server wants, if any; or OCTAFLOW_RUNNER_API_KEY
|
|
19
|
+
* --concurrency <n> requests in flight at once (default 1 — mlx_lm.server answers one at a time); the door caps it at what was registered
|
|
20
|
+
* --check connect, say who the token belongs to and whether each model the app declares is installed, then exit
|
|
21
|
+
* --quiet log failures only
|
|
22
|
+
* --help
|
|
23
|
+
*
|
|
24
|
+
* At start the runner says who it is and which models the app's provider rows declare for its
|
|
25
|
+
* tags, and asks the local server which of them it has: a missing one is a warning with the
|
|
26
|
+
* command that installs it, not a refusal — the app may declare a model it will ask for later.
|
|
27
|
+
*/
|
|
28
|
+
import { parseArgs } from 'node:util';
|
|
29
|
+
import { fileURLToPath } from 'node:url';
|
|
30
|
+
|
|
31
|
+
const USAGE = `octaflow-runner — serve a model to an app from a machine the app cannot reach
|
|
32
|
+
|
|
33
|
+
octaflow-runner --url <door> --token <token> (--ollama <url> | --openai-compatible <url> [--api-key <key>]) [--concurrency <n>] [--check] [--quiet]
|
|
34
|
+
|
|
35
|
+
--url the app's door (OCTAFLOW_RUNNER_URL)
|
|
36
|
+
--token the runner's token (OCTAFLOW_RUNNER_TOKEN)
|
|
37
|
+
--ollama Ollama's native API, e.g. http://localhost:11434
|
|
38
|
+
--openai-compatible an OpenAI-compatible base URL, e.g. http://localhost:8081/v1 (mlx_lm.server, vLLM, llama.cpp)
|
|
39
|
+
--api-key that server's key, if it wants one (OCTAFLOW_RUNNER_API_KEY)
|
|
40
|
+
--concurrency requests at once (default 1)
|
|
41
|
+
--check connect and report, then exit
|
|
42
|
+
--quiet log failures only
|
|
43
|
+
`;
|
|
44
|
+
|
|
45
|
+
/** The command line and the environment as one configuration, or the reason it is not one. */
|
|
46
|
+
export function readArgs(argv, env = process.env) {
|
|
47
|
+
let values;
|
|
48
|
+
try {
|
|
49
|
+
({ values } = parseArgs({
|
|
50
|
+
args: argv,
|
|
51
|
+
options: {
|
|
52
|
+
url: { type: 'string' },
|
|
53
|
+
token: { type: 'string' },
|
|
54
|
+
ollama: { type: 'string' },
|
|
55
|
+
'openai-compatible': { type: 'string' },
|
|
56
|
+
'api-key': { type: 'string' },
|
|
57
|
+
concurrency: { type: 'string' },
|
|
58
|
+
check: { type: 'boolean', default: false },
|
|
59
|
+
quiet: { type: 'boolean', default: false },
|
|
60
|
+
help: { type: 'boolean', default: false },
|
|
61
|
+
},
|
|
62
|
+
strict: true,
|
|
63
|
+
allowPositionals: false,
|
|
64
|
+
}));
|
|
65
|
+
} catch (error) {
|
|
66
|
+
return { error: error instanceof Error ? error.message : String(error) };
|
|
67
|
+
}
|
|
68
|
+
if (values.help) return { help: true };
|
|
69
|
+
const url = values.url ?? env.OCTAFLOW_RUNNER_URL;
|
|
70
|
+
const token = values.token ?? env.OCTAFLOW_RUNNER_TOKEN;
|
|
71
|
+
if (!url) return { error: 'the door is missing: --url or OCTAFLOW_RUNNER_URL' };
|
|
72
|
+
if (!token) return { error: 'the token is missing: --token or OCTAFLOW_RUNNER_TOKEN' };
|
|
73
|
+
if (values.ollama && values['openai-compatible']) return { error: 'one backend: --ollama or --openai-compatible, not both' };
|
|
74
|
+
if (!values.ollama && !values['openai-compatible']) return { error: 'a backend is missing: --ollama <url> or --openai-compatible <url>' };
|
|
75
|
+
const concurrency = values.concurrency === undefined ? 1 : Number(values.concurrency);
|
|
76
|
+
if (!Number.isInteger(concurrency) || concurrency < 1) return { error: `--concurrency wants a whole number of 1 or more, not '${values.concurrency}'` };
|
|
77
|
+
return {
|
|
78
|
+
url,
|
|
79
|
+
token,
|
|
80
|
+
backend: values.ollama ? { kind: 'ollama', url: values.ollama } : { kind: 'openai-compatible', url: values['openai-compatible'], apiKey: values['api-key'] ?? env.OCTAFLOW_RUNNER_API_KEY },
|
|
81
|
+
concurrency,
|
|
82
|
+
check: values.check,
|
|
83
|
+
quiet: values.quiet,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/** The model ids the local server has, or `null` when it could not be asked. */
|
|
88
|
+
export async function installedModels(backend, fetchImpl = fetch) {
|
|
89
|
+
try {
|
|
90
|
+
if (backend.kind === 'ollama') {
|
|
91
|
+
const res = await fetchImpl(`${backend.url.replace(/\/+$/, '')}/api/tags`, { signal: AbortSignal.timeout(5000) });
|
|
92
|
+
if (!res.ok) return null;
|
|
93
|
+
const body = await res.json();
|
|
94
|
+
return (body.models ?? []).map((m) => m.name ?? m.model).filter(Boolean);
|
|
95
|
+
}
|
|
96
|
+
const res = await fetchImpl(`${backend.url.replace(/\/+$/, '')}/models`, {
|
|
97
|
+
headers: backend.apiKey ? { authorization: `Bearer ${backend.apiKey}` } : {},
|
|
98
|
+
signal: AbortSignal.timeout(5000),
|
|
99
|
+
});
|
|
100
|
+
if (!res.ok) return null;
|
|
101
|
+
const body = await res.json();
|
|
102
|
+
return (body.data ?? []).map((m) => m.id).filter(Boolean);
|
|
103
|
+
} catch {
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/** Ollama lists `name:tag`; an app may declare `name` alone and mean `name:latest`. */
|
|
109
|
+
const sameModel = (declared, installed) => declared === installed || `${declared}:latest` === installed || declared === `${installed}:latest`;
|
|
110
|
+
|
|
111
|
+
/** The lines the start-up report prints — what the token is, what the app declares, what is there. */
|
|
112
|
+
export function startupReport(self, installed, backend) {
|
|
113
|
+
const lines = [`connected as ${self.id} (${self.label}) — tags ${self.tags.join(', ')}, up to ${self.concurrency} at once`];
|
|
114
|
+
if (self.models.length === 0) lines.push('the app declares no models for these tags yet — a provider row of kind `runner` with these tags names them');
|
|
115
|
+
for (const model of self.models) {
|
|
116
|
+
if (installed === null) lines.push(` ${model} — the local server could not be asked whether it is installed`);
|
|
117
|
+
else if (installed.some((i) => sameModel(model, i))) lines.push(` ${model} — installed`);
|
|
118
|
+
else lines.push(` ${model} — NOT installed${backend.kind === 'ollama' ? ` (ollama pull ${model})` : ''}`);
|
|
119
|
+
}
|
|
120
|
+
return lines;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
async function makeBackend(backend) {
|
|
124
|
+
if (backend.kind === 'ollama') {
|
|
125
|
+
const { createOllama } = await import('../dist/ai/providers/index.js');
|
|
126
|
+
return createOllama({ baseUrl: backend.url });
|
|
127
|
+
}
|
|
128
|
+
let mod;
|
|
129
|
+
try {
|
|
130
|
+
mod = await import('@ai-sdk/openai-compatible');
|
|
131
|
+
} catch {
|
|
132
|
+
throw new Error("an OpenAI-compatible backend needs the optional peer '@ai-sdk/openai-compatible' installed beside octaflow");
|
|
133
|
+
}
|
|
134
|
+
const sdk = mod.createOpenAICompatible({ name: 'runner', baseURL: backend.url, ...(backend.apiKey ? { apiKey: backend.apiKey } : {}), supportsStructuredOutputs: true });
|
|
135
|
+
return {
|
|
136
|
+
languageModel: (id) => sdk.languageModel(id),
|
|
137
|
+
embeddingModel: (id) => sdk.embeddingModel(id),
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export async function main(argv, env = process.env, io = { out: console.log, err: console.error }) {
|
|
142
|
+
const args = readArgs(argv, env);
|
|
143
|
+
if (args.help) {
|
|
144
|
+
io.out(USAGE);
|
|
145
|
+
return 0;
|
|
146
|
+
}
|
|
147
|
+
if (args.error) {
|
|
148
|
+
io.err(`octaflow-runner: ${args.error}\n\n${USAGE}`);
|
|
149
|
+
return 2;
|
|
150
|
+
}
|
|
151
|
+
const { createHttpRunnerClient, runModelRunner } = await import('../dist/ai/runner/index.js');
|
|
152
|
+
const client = createHttpRunnerClient({ url: args.url, token: args.token });
|
|
153
|
+
|
|
154
|
+
let self;
|
|
155
|
+
try {
|
|
156
|
+
self = await client.self();
|
|
157
|
+
} catch (error) {
|
|
158
|
+
io.err(`octaflow-runner: cannot reach the door at ${args.url}: ${error instanceof Error ? error.message : String(error)}`);
|
|
159
|
+
return 1;
|
|
160
|
+
}
|
|
161
|
+
const installed = await installedModels(args.backend);
|
|
162
|
+
for (const line of startupReport(self, installed, args.backend)) io.out(line);
|
|
163
|
+
if (installed === null) io.err(`octaflow-runner: no model server answered at ${args.backend.url} — requests will fail until it is up`);
|
|
164
|
+
if (args.check) return installed === null ? 1 : 0;
|
|
165
|
+
|
|
166
|
+
const backend = await makeBackend(args.backend);
|
|
167
|
+
const stop = new AbortController();
|
|
168
|
+
const onSignal = () => {
|
|
169
|
+
io.out('stopping — what is in flight is aborted');
|
|
170
|
+
stop.abort();
|
|
171
|
+
};
|
|
172
|
+
process.once('SIGINT', onSignal);
|
|
173
|
+
process.once('SIGTERM', onSignal);
|
|
174
|
+
|
|
175
|
+
io.out(`waiting for work from ${args.url}`);
|
|
176
|
+
await runModelRunner({
|
|
177
|
+
client,
|
|
178
|
+
backend,
|
|
179
|
+
concurrency: Math.min(args.concurrency, self.concurrency),
|
|
180
|
+
signal: stop.signal,
|
|
181
|
+
log: (event) => {
|
|
182
|
+
switch (event.type) {
|
|
183
|
+
case 'claimed':
|
|
184
|
+
if (!args.quiet) io.out(`→ ${event.request.id.slice(0, 8)} ${event.request.kind} ${event.request.model}`);
|
|
185
|
+
break;
|
|
186
|
+
case 'delivered':
|
|
187
|
+
if (!args.quiet || !event.ok) io.out(`${event.ok ? '✓' : '✗'} ${event.requestId.slice(0, 8)} in ${event.elapsedMs} ms`);
|
|
188
|
+
break;
|
|
189
|
+
case 'lost':
|
|
190
|
+
io.err(`✗ ${event.requestId.slice(0, 8)} no longer ours — cancelled by the caller, or the lease ran out`);
|
|
191
|
+
break;
|
|
192
|
+
case 'door_unreachable':
|
|
193
|
+
io.err(`door unreachable (${event.error instanceof Error ? event.error.message : String(event.error)}) — retrying in ${Math.round(event.retryInMs / 1000)} s`);
|
|
194
|
+
break;
|
|
195
|
+
}
|
|
196
|
+
},
|
|
197
|
+
});
|
|
198
|
+
return 0;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
if (process.argv[1] && fileURLToPath(import.meta.url) === process.argv[1]) {
|
|
202
|
+
main(process.argv.slice(2)).then(
|
|
203
|
+
(code) => process.exit(code),
|
|
204
|
+
(error) => {
|
|
205
|
+
console.error(`octaflow-runner: ${error instanceof Error ? error.message : String(error)}`);
|
|
206
|
+
process.exit(1);
|
|
207
|
+
},
|
|
208
|
+
);
|
|
209
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { b as OpenAiCompatibleProviderId, g as EmbeddingProviderId, i as AiEndpointResidency, o as AiProviderId } from "../../provider-registry-C_mOfdHH.js";
|
|
2
2
|
import { A as maskKeys, C as ProviderErrorExplanation, D as explainProviderError, E as ProviderErrorPreset, O as isFatalProviderError, S as ProviderError, T as ProviderErrorPhrase, _ as ExplainProviderErrorOptions, a as OllamaProbeReport, c as ProviderProbeOptions, d as testProvider, f as CircuitBreaker, h as withCircuitBreaker, i as OllamaProbeOptions, k as isProviderError, l as ProviderProbeReport, m as createCircuitBreaker, o as ProbeableProvider, p as CircuitBreakerOptions, s as ProviderProbeFailure, u as testOllamaModel, v as FATAL_PROVIDER_ERROR_KEYS, w as ProviderErrorKey, x as PROVIDER_ERROR_STATUS, y as PROVIDER_ERROR_PHRASES } from "../../provider-probe-BDg4c1Rk.js";
|
|
3
3
|
import { a as ModelCapabilities, c as ModelProvider, g as ModelProviders, m as ModelProviderRegion, o as ModelEntry } from "../../model-providers-DQU57vAf.js";
|
|
4
|
-
import {
|
|
4
|
+
import { d as ModelRunnerHub } from "../../hub-CTf95SkP.js";
|
|
5
5
|
import { EmbeddingModelV4, LanguageModelV4, LanguageModelV4CallOptions } from "@ai-sdk/provider";
|
|
6
6
|
//#region src/ai/providers/ollama.d.ts
|
|
7
7
|
/** `false` switches thinking off; a level asks a model that has levels for that much. */
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { a as testProvider, i as testOllamaModel, o as createCircuitBreaker, s as withCircuitBreaker, u as createRefusalAwareModel } from "../../provider-probe-CwZWwFlo.js";
|
|
2
2
|
import { a as EMBEDDING_PROVIDERS, c as OPENAI_COMPATIBLE_PROVIDER_ID, d as resolveEndpointResidency, t as AI_PROVIDERS } from "../../provider-registry-B91lgOmu.js";
|
|
3
3
|
import { a as PROVIDER_ERROR_STATUS, c as isFatalProviderError, l as isProviderError, n as FATAL_PROVIDER_ERROR_KEYS, o as ProviderError, r as PROVIDER_ERROR_PHRASES, s as explainProviderError, u as maskKeys } from "../../provider-errors-DZWjl8di.js";
|
|
4
|
-
import { t as createRunnerModels } from "../../model-
|
|
4
|
+
import { t as createRunnerModels } from "../../model-DztyiOGz.js";
|
|
5
5
|
import { wrapLanguageModel } from "ai";
|
|
6
6
|
import { APICallError } from "@ai-sdk/provider";
|
|
7
7
|
//#region src/ai/providers/ollama.ts
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { C as modelRunnerSelfSchema, S as modelRunnerInputSchema, _ as
|
|
2
|
-
import { a as
|
|
1
|
+
import { C as modelRunnerDeliverBodySchema, D as modelRunnerTagsSchema, E as modelRunnerSelfSchema, S as modelRunnerClaimBodySchema, T as modelRunnerInputSchema, _ as StoredModelRunner, a as ModelRunner, b as createInMemoryModelRunnerStore, c as ModelRunnerDeliverBody, d as ModelRunnerOutcome, f as ModelRunnerRequestError, g as ModelRunnerStore, h as ModelRunnerSelf, i as MODEL_RUNNER_TAG, l as ModelRunnerHeartbeatBody, m as ModelRunnerRequestStatus, n as MODEL_RUNNER_REQUEST_KINDS, o as ModelRunnerChunkBody, p as ModelRunnerRequestKind, r as MODEL_RUNNER_REQUEST_STATUSES, s as ModelRunnerClaimBody, t as ClaimedModelRunnerRequest, u as ModelRunnerInput, v as StoredModelRunnerChunk, w as modelRunnerHeartbeatBodySchema, x as modelRunnerChunkBodySchema, y as StoredModelRunnerRequest } from "../../model-runner-2ONkybAq.js";
|
|
2
|
+
import { _ as generateModelRunnerToken, a as DEFAULT_MODEL_RUNNER_ONLINE_WINDOW_MS, c as DEFAULT_MODEL_RUNNER_SWEEP_AFTER_MS, d as ModelRunnerHub, f as ModelRunnerHubError, g as createModelRunnerHub, h as StartedModelRunnerCall, i as DEFAULT_MODEL_RUNNER_MAX_ATTEMPTS, l as ModelRunnerCall, m as ModelRunnerStart, n as DEFAULT_MODEL_RUNNER_CLAIM_WAIT_MS, o as DEFAULT_MODEL_RUNNER_POLL_MS, p as ModelRunnerHubOptions, r as DEFAULT_MODEL_RUNNER_LEASE_MS, s as DEFAULT_MODEL_RUNNER_REQUEST_TIMEOUT_MS, t as DEFAULT_MODEL_RUNNER_CHUNK_BATCH, u as ModelRunnerCallResult, v as hashModelRunnerToken } from "../../hub-CTf95SkP.js";
|
|
3
3
|
import { EmbeddingModelV4, EmbeddingModelV4CallOptions, EmbeddingModelV4Result, LanguageModelV4, LanguageModelV4CallOptions, LanguageModelV4GenerateResult, LanguageModelV4StreamPart } from "@ai-sdk/provider";
|
|
4
4
|
//#region src/ai/runner/model.d.ts
|
|
5
5
|
export interface RunnerModelsOptions {
|
|
@@ -33,6 +33,10 @@ export interface ModelRunnerClient {
|
|
|
33
33
|
held: string[];
|
|
34
34
|
}>;
|
|
35
35
|
deliver(requestId: string, outcome: ModelRunnerOutcome): Promise<boolean>;
|
|
36
|
+
/** One batch of a `stream` request's parts; `accepted: false` says to stop. */
|
|
37
|
+
chunk(requestId: string, seq: number, parts: unknown[]): Promise<{
|
|
38
|
+
accepted: boolean;
|
|
39
|
+
}>;
|
|
36
40
|
}
|
|
37
41
|
/** What runs the requests: a model per id, settings as the request's entry carries them. */
|
|
38
42
|
export interface ModelRunnerBackend {
|
|
@@ -53,6 +57,10 @@ export interface RunModelRunnerOptions {
|
|
|
53
57
|
/** The first wait after the door could not be reached; doubles up to `maxBackoffMs`. Default 1 s / 30 s. */
|
|
54
58
|
backoffMs?: number;
|
|
55
59
|
maxBackoffMs?: number;
|
|
60
|
+
/** A stream's parts are posted at least this often while some are waiting. Default 100 ms. */
|
|
61
|
+
chunkEveryMs?: number;
|
|
62
|
+
/** … or as soon as this many are waiting. Default 64. */
|
|
63
|
+
chunkParts?: number;
|
|
56
64
|
/** Told about each request and each failure to reach the door. */
|
|
57
65
|
log?: (event: ModelRunnerLogEvent) => void;
|
|
58
66
|
}
|
|
@@ -72,8 +80,21 @@ export type ModelRunnerLogEvent = {
|
|
|
72
80
|
error: unknown;
|
|
73
81
|
retryInMs: number;
|
|
74
82
|
};
|
|
83
|
+
/** Where a stream's chunks go: the door, or a test. `false` back means stop. */
|
|
84
|
+
export type ChunkSink = (seq: number, parts: unknown[]) => Promise<boolean>;
|
|
85
|
+
export interface RunRequestOptions {
|
|
86
|
+
signal?: AbortSignal;
|
|
87
|
+
/** For a `stream` request: where its chunks go. Without one, the stream is collected and answered whole. */
|
|
88
|
+
chunks?: ChunkSink;
|
|
89
|
+
chunkEveryMs?: number;
|
|
90
|
+
chunkParts?: number;
|
|
91
|
+
}
|
|
92
|
+
/** The answer a runner delivers for a stream it posted chunk by chunk: how many chunks there were. */
|
|
93
|
+
export interface StreamedResult {
|
|
94
|
+
streamed: number;
|
|
95
|
+
}
|
|
75
96
|
/** Run one claimed request on the backend; never throws — a failure is the outcome. */
|
|
76
|
-
export declare function runModelRunnerRequest(backend: ModelRunnerBackend, request: ClaimedModelRunnerRequest,
|
|
97
|
+
export declare function runModelRunnerRequest(backend: ModelRunnerBackend, request: ClaimedModelRunnerRequest, options?: RunRequestOptions | AbortSignal): Promise<ModelRunnerOutcome>;
|
|
77
98
|
/** The loop. Returns when `signal` aborts. */
|
|
78
99
|
export declare function runModelRunner(options: RunModelRunnerOptions): Promise<void>;
|
|
79
100
|
/** The hub in the same process as the client — a host that runs its runner inside, and tests. */
|
|
@@ -84,7 +105,7 @@ export interface HttpRunnerClientOptions {
|
|
|
84
105
|
token: string;
|
|
85
106
|
fetch?: typeof fetch;
|
|
86
107
|
}
|
|
87
|
-
/** The door over HTTP: `createModelRunnerHandler`'s
|
|
108
|
+
/** The door over HTTP: `createModelRunnerHandler`'s five routes. Throws on a door that cannot be reached or answers an error. */
|
|
88
109
|
export declare function createHttpRunnerClient(options: HttpRunnerClientOptions): ModelRunnerClient;
|
|
89
110
|
//#endregion
|
|
90
111
|
//#region src/ai/runner/wire.d.ts
|
|
@@ -106,9 +127,12 @@ export declare function encodeCallOptions(options: LanguageModelV4CallOptions):
|
|
|
106
127
|
export declare function decodeCallOptions(wire: unknown): LanguageModelV4CallOptions;
|
|
107
128
|
export declare function encodeGenerateResult(result: LanguageModelV4GenerateResult): unknown;
|
|
108
129
|
export declare function decodeGenerateResult(wire: unknown): LanguageModelV4GenerateResult;
|
|
130
|
+
/** Stream parts as JSON: bytes as base64, URLs as strings; an `error` part carries a sentence, since an `Error` does not cross. */
|
|
131
|
+
export declare function encodeStreamParts(parts: LanguageModelV4StreamPart[]): unknown[];
|
|
132
|
+
export declare function decodeStreamParts(wire: unknown[]): LanguageModelV4StreamPart[];
|
|
109
133
|
export declare function encodeEmbedOptions(options: EmbeddingModelV4CallOptions): EmbedPayload['options'];
|
|
110
134
|
export declare function decodeEmbedOptions(wire: unknown): EmbeddingModelV4CallOptions;
|
|
111
135
|
export declare function encodeEmbedResult(result: EmbeddingModelV4Result): unknown;
|
|
112
136
|
export declare function decodeEmbedResult(wire: unknown): EmbeddingModelV4Result;
|
|
113
137
|
//#endregion
|
|
114
|
-
export { ClaimedModelRunnerRequest, DEFAULT_MODEL_RUNNER_CLAIM_WAIT_MS, DEFAULT_MODEL_RUNNER_LEASE_MS, DEFAULT_MODEL_RUNNER_MAX_ATTEMPTS, DEFAULT_MODEL_RUNNER_ONLINE_WINDOW_MS, DEFAULT_MODEL_RUNNER_POLL_MS, DEFAULT_MODEL_RUNNER_REQUEST_TIMEOUT_MS, DEFAULT_MODEL_RUNNER_SWEEP_AFTER_MS, MODEL_RUNNER_REQUEST_KINDS, MODEL_RUNNER_REQUEST_STATUSES, MODEL_RUNNER_TAG, ModelRunner, ModelRunnerCall, ModelRunnerCallResult, ModelRunnerClaimBody, ModelRunnerDeliverBody, ModelRunnerHeartbeatBody, ModelRunnerHub, ModelRunnerHubError, ModelRunnerHubOptions, ModelRunnerInput, ModelRunnerOutcome, ModelRunnerRequestError, ModelRunnerRequestKind, ModelRunnerRequestStatus, ModelRunnerSelf, ModelRunnerStore, StoredModelRunner, StoredModelRunnerRequest, createInMemoryModelRunnerStore, createModelRunnerHub, generateModelRunnerToken, hashModelRunnerToken, modelRunnerClaimBodySchema, modelRunnerDeliverBodySchema, modelRunnerHeartbeatBodySchema, modelRunnerInputSchema, modelRunnerSelfSchema, modelRunnerTagsSchema };
|
|
138
|
+
export { ClaimedModelRunnerRequest, DEFAULT_MODEL_RUNNER_CHUNK_BATCH, DEFAULT_MODEL_RUNNER_CLAIM_WAIT_MS, DEFAULT_MODEL_RUNNER_LEASE_MS, DEFAULT_MODEL_RUNNER_MAX_ATTEMPTS, DEFAULT_MODEL_RUNNER_ONLINE_WINDOW_MS, DEFAULT_MODEL_RUNNER_POLL_MS, DEFAULT_MODEL_RUNNER_REQUEST_TIMEOUT_MS, DEFAULT_MODEL_RUNNER_SWEEP_AFTER_MS, MODEL_RUNNER_REQUEST_KINDS, MODEL_RUNNER_REQUEST_STATUSES, MODEL_RUNNER_TAG, ModelRunner, ModelRunnerCall, ModelRunnerCallResult, ModelRunnerChunkBody, ModelRunnerClaimBody, ModelRunnerDeliverBody, ModelRunnerHeartbeatBody, ModelRunnerHub, ModelRunnerHubError, ModelRunnerHubOptions, ModelRunnerInput, ModelRunnerOutcome, ModelRunnerRequestError, ModelRunnerRequestKind, ModelRunnerRequestStatus, ModelRunnerSelf, ModelRunnerStart, ModelRunnerStore, StartedModelRunnerCall, StoredModelRunner, StoredModelRunnerChunk, StoredModelRunnerRequest, createInMemoryModelRunnerStore, createModelRunnerHub, generateModelRunnerToken, hashModelRunnerToken, modelRunnerChunkBodySchema, modelRunnerClaimBodySchema, modelRunnerDeliverBodySchema, modelRunnerHeartbeatBodySchema, modelRunnerInputSchema, modelRunnerSelfSchema, modelRunnerTagsSchema };
|