@orkestrel/ollama 0.0.12 → 0.0.14
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 +6 -5
- package/dist/src/server/index.cjs +312 -142
- package/dist/src/server/index.cjs.map +1 -1
- package/dist/src/server/index.d.cts +261 -65
- package/dist/src/server/index.d.ts +261 -65
- package/dist/src/server/index.js +304 -143
- package/dist/src/server/index.js.map +1 -1
- package/package.json +24 -22
|
@@ -1,21 +1,44 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { ContextFormat } from '@orkestrel/agent';
|
|
2
|
+
import { Message } from '@orkestrel/agent';
|
|
3
3
|
import { ProviderDelta } from '@orkestrel/agent';
|
|
4
4
|
import { ProviderInterface } from '@orkestrel/agent';
|
|
5
5
|
import { ProviderResult } from '@orkestrel/agent';
|
|
6
6
|
import { ProviderStreamOptions } from '@orkestrel/agent';
|
|
7
|
+
import { ThinkSplitterInterface } from '@orkestrel/agent';
|
|
7
8
|
import { TimeoutInterface } from '@orkestrel/timeout';
|
|
9
|
+
import { TokenUsage } from '@orkestrel/budget';
|
|
10
|
+
import { ToolCall } from '@orkestrel/tool';
|
|
8
11
|
import { ToolDefinition } from '@orkestrel/tool';
|
|
9
12
|
|
|
10
13
|
/**
|
|
11
|
-
*
|
|
14
|
+
* Builds a provider result from a turn's content, reasoning, tool calls, and usage.
|
|
15
|
+
*
|
|
16
|
+
* @remarks
|
|
17
|
+
* Only the present optionals are set: no empty `thinking`, no empty `tools`, and no
|
|
18
|
+
* `usage` unless the wire reported one.
|
|
19
|
+
*
|
|
20
|
+
* @param content - The clean assistant content the splitter accumulated
|
|
21
|
+
* @param thinking - The joined reasoning, empty when the turn produced none
|
|
22
|
+
* @param tools - The tool calls collected across the turn
|
|
23
|
+
* @param usage - The token usage, or `undefined` when the wire reported none
|
|
24
|
+
* @returns The result carrying only its populated fields
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* ```ts
|
|
28
|
+
* buildResult('ok', '', [], undefined) // { content: 'ok' }
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
export declare function buildResult(content: string, thinking: string, tools: readonly ToolCall[], usage: TokenUsage | undefined): ProviderResult;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Creates a local Ollama inference provider — a {@link ProviderInterface} over the
|
|
12
35
|
* daemon's `POST /api/chat`, supporting non-streaming `generate` and streaming
|
|
13
36
|
* `stream`.
|
|
14
37
|
*
|
|
15
38
|
* @remarks
|
|
16
39
|
* Only `model` is required; `url` defaults to the local daemon, `keepAlive` to `'5m'`,
|
|
17
40
|
* `timeout` to `120_000`ms, and `options` is forwarded verbatim as sampling
|
|
18
|
-
* parameters (`temperature
|
|
41
|
+
* parameters (`temperature`, `seed`, and `num_predict`). Both calls take an
|
|
19
42
|
* `AbortSignal` to bound the request; a `stream` cancelled mid-flight throws a
|
|
20
43
|
* `ProviderAbortError` carrying the partial result.
|
|
21
44
|
*
|
|
@@ -23,12 +46,12 @@ import { ToolDefinition } from '@orkestrel/tool';
|
|
|
23
46
|
* point `url` at your own server, inject a custom `fetch`, and have `headers` attach a
|
|
24
47
|
* generated/obfuscated bearer token your server validates — so a browser runtime
|
|
25
48
|
* reaches the LLM through your middleware WITHOUT this library ever handling the real API
|
|
26
|
-
* key. Both omitted ⇒
|
|
49
|
+
* key. Both omitted ⇒ the global `fetch` and only a JSON content type.
|
|
27
50
|
*
|
|
28
51
|
* The optional `format` is the provider's context-framing default — the PROVIDER-DEFAULT
|
|
29
|
-
* level of `AgentContext`'s format cascade (
|
|
30
|
-
*
|
|
31
|
-
* provider's models prefer context sections framed (
|
|
52
|
+
* level of `AgentContext`'s format cascade (beaten by a manager-options or per-item
|
|
53
|
+
* override, beating the managers' built-in framing), declaring how this
|
|
54
|
+
* provider's models prefer context sections framed (for example XML group wrappers vs. Markdown
|
|
32
55
|
* headers). It is EXPOSED on the provider for the Agent's `build()` and is NOT Ollama's
|
|
33
56
|
* `/api/chat` `format` wire parameter (structured output) — the two are unrelated despite
|
|
34
57
|
* the shared word. Omitted ⇒ the provider is framing-agnostic (core's built-in defaults).
|
|
@@ -40,7 +63,7 @@ import { ToolDefinition } from '@orkestrel/tool';
|
|
|
40
63
|
* @example
|
|
41
64
|
* ```ts
|
|
42
65
|
* import { createAbort } from '@orkestrel/abort'
|
|
43
|
-
* import { createOllama } from '@
|
|
66
|
+
* import { createOllama } from '@orkestrel/ollama'
|
|
44
67
|
*
|
|
45
68
|
* const provider = createOllama({ model: 'qwen3.5:2b-q4_K_M' })
|
|
46
69
|
* const abort = createAbort()
|
|
@@ -48,7 +71,7 @@ import { ToolDefinition } from '@orkestrel/tool';
|
|
|
48
71
|
* ```
|
|
49
72
|
*
|
|
50
73
|
* @example
|
|
51
|
-
* Route through your own server with an obfuscated token
|
|
74
|
+
* Route through your own server with an obfuscated token:
|
|
52
75
|
* ```ts
|
|
53
76
|
* const provider = createOllama({
|
|
54
77
|
* model: 'qwen3.5:2b-q4_K_M',
|
|
@@ -77,50 +100,171 @@ import { ToolDefinition } from '@orkestrel/tool';
|
|
|
77
100
|
export declare function createOllama(options: OllamaOptions): ProviderInterface;
|
|
78
101
|
|
|
79
102
|
/**
|
|
80
|
-
*
|
|
103
|
+
* Names how long the model stays resident after a call when `OllamaOptions.keepAlive` is
|
|
81
104
|
* omitted — Ollama's own `keep_alive` default, expressed as a duration string.
|
|
105
|
+
*
|
|
106
|
+
* @remarks
|
|
107
|
+
* The name mirrors the Ollama `/api/chat` `keep_alive` field this value is sent as, so
|
|
108
|
+
* the constant, the `OllamaOptions.keepAlive` key, and the wire member read as one term.
|
|
82
109
|
*/
|
|
83
110
|
export declare const DEFAULT_KEEP_ALIVE = "5m";
|
|
84
111
|
|
|
85
|
-
/**
|
|
112
|
+
/** Names the local Ollama daemon base URL assumed when `OllamaOptions.url` is omitted. */
|
|
86
113
|
export declare const DEFAULT_OLLAMA_URL = "http://localhost:11434";
|
|
87
114
|
|
|
88
115
|
/**
|
|
89
|
-
*
|
|
116
|
+
* Names the per-call deadline in milliseconds when `OllamaOptions.timeout` is omitted —
|
|
90
117
|
* generous enough that a cold model load does not trip it.
|
|
91
118
|
*/
|
|
92
119
|
export declare const DEFAULT_PROVIDER_TIMEOUT = 120000;
|
|
93
120
|
|
|
94
121
|
/**
|
|
95
|
-
*
|
|
122
|
+
* Extracts a wire `arguments` value as a record.
|
|
123
|
+
*
|
|
124
|
+
* @remarks
|
|
125
|
+
* Total: an object passes through, a JSON string is parsed when it yields a record, and
|
|
126
|
+
* a malformed string yields `{}` rather than throwing.
|
|
127
|
+
*
|
|
128
|
+
* @param value - The wire's `function.arguments` value, of unknown shape
|
|
129
|
+
* @returns The argument record, or `{}` when the value carries none
|
|
130
|
+
*
|
|
131
|
+
* @example
|
|
132
|
+
* ```ts
|
|
133
|
+
* extractArguments('{"city":"Oslo"}') // { city: 'Oslo' }
|
|
134
|
+
* ```
|
|
135
|
+
*/
|
|
136
|
+
export declare function extractArguments(value: unknown): Readonly<Record<string, unknown>>;
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Extracts the assistant text of one wire record.
|
|
140
|
+
*
|
|
141
|
+
* @param record - One parsed `/api/chat` record — a non-stream body or an NDJSON line
|
|
142
|
+
* @returns The record's `message.content` when it is a string, else `''`
|
|
143
|
+
*
|
|
144
|
+
* @example
|
|
145
|
+
* ```ts
|
|
146
|
+
* extractContent({ message: { content: 'ok' } }) // 'ok'
|
|
147
|
+
* ```
|
|
148
|
+
*/
|
|
149
|
+
export declare function extractContent(record: Readonly<Record<string, unknown>>): string;
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Extracts the daemon-side reasoning of one wire record.
|
|
153
|
+
*
|
|
154
|
+
* @remarks
|
|
155
|
+
* `message.thinking` is the `think: true` wire shape. It is read whatever the configured
|
|
156
|
+
* flag says, because a daemon may separate reasoning on its own.
|
|
157
|
+
*
|
|
158
|
+
* @param record - One parsed `/api/chat` record — a non-stream body or an NDJSON line
|
|
159
|
+
* @returns The record's `message.thinking` when it is a string, else `''`
|
|
160
|
+
*
|
|
161
|
+
* @example
|
|
162
|
+
* ```ts
|
|
163
|
+
* extractThinking({ message: { thinking: 'weighing it' } }) // 'weighing it'
|
|
164
|
+
* ```
|
|
165
|
+
*/
|
|
166
|
+
export declare function extractThinking(record: Readonly<Record<string, unknown>>): string;
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Extracts the tool calls of one wire record's `message.tool_calls`.
|
|
170
|
+
*
|
|
171
|
+
* @remarks
|
|
172
|
+
* Each entry narrows to `{ id, name, arguments }`: the entry and its `function` must be
|
|
173
|
+
* records and `name` a string, else the entry is dropped. An id is minted when the wire
|
|
174
|
+
* omits one.
|
|
175
|
+
*
|
|
176
|
+
* @param record - One parsed `/api/chat` record — a non-stream body or an NDJSON line
|
|
177
|
+
* @returns The narrowed tool calls, empty when the record carries none
|
|
178
|
+
*
|
|
179
|
+
* @example
|
|
180
|
+
* ```ts
|
|
181
|
+
* extractTools({ message: { tool_calls: [{ function: { name: 'weather' } }] } })
|
|
182
|
+
* // [{ id: '…', name: 'weather', arguments: {} }]
|
|
183
|
+
* ```
|
|
184
|
+
*/
|
|
185
|
+
export declare function extractTools(record: Readonly<Record<string, unknown>>): readonly ToolCall[];
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Extracts the token usage of one wire record.
|
|
189
|
+
*
|
|
190
|
+
* @remarks
|
|
191
|
+
* Both counts must be numbers, which is true of the non-stream body and the stream's
|
|
192
|
+
* `done: true` line. A delta line carries neither, so it yields `undefined`.
|
|
193
|
+
*
|
|
194
|
+
* @param record - One parsed `/api/chat` record — a non-stream body or an NDJSON line
|
|
195
|
+
* @returns The `TokenUsage` shape, or `undefined` when either count is absent
|
|
196
|
+
*
|
|
197
|
+
* @example
|
|
198
|
+
* ```ts
|
|
199
|
+
* extractUsage({ prompt_eval_count: 3, eval_count: 4 })
|
|
200
|
+
* // { prompt: 3, completion: 4, total: 7 }
|
|
201
|
+
* ```
|
|
202
|
+
*/
|
|
203
|
+
export declare function extractUsage(record: Readonly<Record<string, unknown>>): TokenUsage | undefined;
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Checks whether a value is an {@link OllamaHTTPError}.
|
|
96
207
|
*
|
|
97
208
|
* @param value - The value to test
|
|
98
|
-
* @returns
|
|
209
|
+
* @returns True if `value` is an `OllamaHTTPError`; false otherwise
|
|
99
210
|
*/
|
|
100
211
|
export declare function isOllamaHTTPError(value: unknown): value is OllamaHTTPError;
|
|
101
212
|
|
|
102
213
|
/**
|
|
103
|
-
*
|
|
214
|
+
* Joins a call's two reasoning carriers into the result's `thinking`.
|
|
215
|
+
*
|
|
216
|
+
* @param splitter - The per-call splitter holding the separated in-content spans
|
|
217
|
+
* @param wired - The accumulated wire-side `message.thinking` text
|
|
218
|
+
* @returns The two carriers separated by a blank line, or whichever one is non-empty
|
|
219
|
+
*
|
|
220
|
+
* @example
|
|
221
|
+
* ```ts
|
|
222
|
+
* joinThinking(createThinkSplitter(), 'from the wire') // 'from the wire'
|
|
223
|
+
* ```
|
|
224
|
+
*/
|
|
225
|
+
export declare function joinThinking(splitter: ThinkSplitterInterface, wired: string): string;
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Maps conversation turns onto the `/api/chat` wire's minimal message shape.
|
|
229
|
+
*
|
|
230
|
+
* @remarks
|
|
231
|
+
* `tool_calls` is emitted only on a turn that replays them and `images` only on a
|
|
232
|
+
* multimodal turn, so an empty optional never reaches the wire.
|
|
233
|
+
*
|
|
234
|
+
* @param messages - The conversation turns to send
|
|
235
|
+
* @returns The wire `messages` array, one entry per turn, in order
|
|
236
|
+
*
|
|
237
|
+
* @example
|
|
238
|
+
* ```ts
|
|
239
|
+
* mapMessages([{ id: '1', role: 'user', content: 'Say hello.' }])
|
|
240
|
+
* // [{ role: 'user', content: 'Say hello.' }]
|
|
241
|
+
* ```
|
|
242
|
+
*/
|
|
243
|
+
export declare function mapMessages(messages: readonly Message[]): WireChatRequest['messages'];
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Names the cap, in characters, on how much of a non-OK response body is
|
|
104
247
|
* incorporated into a thrown {@link OllamaHTTPError}'s message.
|
|
105
248
|
*
|
|
106
249
|
* @remarks
|
|
107
250
|
* Bounds the excerpt so a defensive proxy or a misbehaving daemon handing
|
|
108
251
|
* back an unbounded response body cannot inflate the thrown error's message
|
|
109
|
-
* without limit
|
|
252
|
+
* without limit. `2048` characters is generous enough to carry a
|
|
110
253
|
* useful diagnostic snippet while staying well short of any practical size
|
|
111
254
|
* concern.
|
|
112
255
|
*/
|
|
113
256
|
export declare const MAX_ERROR_BODY_LENGTH = 2048;
|
|
114
257
|
|
|
115
258
|
/**
|
|
116
|
-
*
|
|
259
|
+
* Represents an error thrown when the Ollama `/api/chat` HTTP transport fails.
|
|
117
260
|
*
|
|
118
261
|
* @remarks
|
|
119
|
-
* Carries the response `status` (0 when no
|
|
120
|
-
*
|
|
121
|
-
* failure sites — the non-OK status branch and the
|
|
122
|
-
* caller can branch on `error.
|
|
123
|
-
* a caught value with
|
|
262
|
+
* Carries the machine-readable `code` `'HTTP'` and the response `status` (0 when no
|
|
263
|
+
* HTTP response was received at all, for example a `null` body). Thrown by
|
|
264
|
+
* {@link OllamaProvider} at its HTTP failure sites — the non-OK status branch and the
|
|
265
|
+
* null-body branch — so a caller can branch on `error.code` and read `error.status`
|
|
266
|
+
* for the HTTP number instead of parsing the message. Narrow a caught value with
|
|
267
|
+
* {@link isOllamaHTTPError}.
|
|
124
268
|
*
|
|
125
269
|
* @example
|
|
126
270
|
* ```ts
|
|
@@ -134,100 +278,124 @@ export declare const MAX_ERROR_BODY_LENGTH = 2048;
|
|
|
134
278
|
* ```
|
|
135
279
|
*/
|
|
136
280
|
export declare class OllamaHTTPError extends Error {
|
|
281
|
+
/**
|
|
282
|
+
* Names the machine-readable condition this error reports — `'HTTP'`: an `/api/chat`
|
|
283
|
+
* transport, status, or body failure.
|
|
284
|
+
*/
|
|
285
|
+
readonly code: "HTTP";
|
|
137
286
|
readonly status: number;
|
|
138
|
-
constructor(message: string, status: number, options?:
|
|
139
|
-
readonly cause?: unknown;
|
|
140
|
-
});
|
|
287
|
+
constructor(message: string, status: number, options?: OllamaHTTPErrorOptions);
|
|
141
288
|
}
|
|
142
289
|
|
|
143
290
|
/**
|
|
144
|
-
*
|
|
291
|
+
* Represents the options a thrown {@link OllamaHTTPError} accepts beside its message and status —
|
|
292
|
+
* the standard error `cause` link, named so a consumer can reference the shape.
|
|
293
|
+
*
|
|
294
|
+
* @remarks
|
|
295
|
+
* `cause` is the underlying value that produced the HTTP failure: the transport or
|
|
296
|
+
* body-read rejection the provider caught before rethrowing. It is `unknown` because a
|
|
297
|
+
* thrown value is unconstrained. Omitted ⇒ the error carries no cause.
|
|
298
|
+
*/
|
|
299
|
+
export declare interface OllamaHTTPErrorOptions {
|
|
300
|
+
readonly cause?: unknown;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* Represents the configuration `createOllama` accepts for the local Ollama backend.
|
|
145
305
|
*
|
|
146
306
|
* @remarks
|
|
147
307
|
* Only `model` is required. `url` defaults to the local daemon, `keepAlive` controls
|
|
148
308
|
* how long the model stays resident after a call, `timeout` is the per-call deadline
|
|
149
309
|
* in milliseconds, and `options` is a passthrough bag of sampling parameters
|
|
150
|
-
* (`temperature
|
|
310
|
+
* (`temperature`, `seed`, and `num_predict`) forwarded verbatim to the wire.
|
|
151
311
|
*
|
|
152
312
|
* The optional `fetch` + `headers` form a **transport seam**: by default the provider
|
|
153
313
|
* talks straight to a local daemon over `globalThis.fetch` with only a JSON content
|
|
154
314
|
* type, but a browser-side runtime can inject a custom transport AND a dynamic header
|
|
155
|
-
* (
|
|
315
|
+
* (for example an obfuscated bearer token) so requests route through the developer's OWN
|
|
156
316
|
* server, which validates that header and forwards to the real LLM. Your app never
|
|
157
317
|
* holds a real API key — the real key lives only on the developer's server; the
|
|
158
318
|
* `headers` hook supplies whatever short-lived/obfuscated token that server expects.
|
|
159
319
|
*/
|
|
160
320
|
export declare interface OllamaOptions {
|
|
161
321
|
readonly model: string;
|
|
162
|
-
/**
|
|
322
|
+
/** Sets the daemon base URL; defaults to `'http://localhost:11434'`. */
|
|
163
323
|
readonly url?: string;
|
|
164
|
-
/**
|
|
324
|
+
/**
|
|
325
|
+
* Sets how long the model stays resident after a call; defaults to `'5m'`. Mirrors the
|
|
326
|
+
* Ollama `/api/chat` `keep_alive` field, whose value this key carries verbatim onto
|
|
327
|
+
* {@link WireChatRequest.keep_alive}.
|
|
328
|
+
*/
|
|
165
329
|
readonly keepAlive?: string | number;
|
|
166
|
-
/**
|
|
330
|
+
/** Sets the per-call deadline in milliseconds; defaults to `120_000`. */
|
|
167
331
|
readonly timeout?: number;
|
|
168
|
-
/**
|
|
332
|
+
/**
|
|
333
|
+
* Carries passthrough sampling parameters (`temperature`, `seed`, and `num_predict`).
|
|
334
|
+
* Mirrors the Ollama `/api/chat` `options` field, whose value this key carries verbatim
|
|
335
|
+
* onto {@link WireChatRequest.options}.
|
|
336
|
+
*/
|
|
169
337
|
readonly options?: Readonly<Record<string, unknown>>;
|
|
170
338
|
/**
|
|
171
|
-
*
|
|
172
|
-
* model (
|
|
339
|
+
* Sets the `/api/chat` `think` wire flag; defaults to `false`. When `true`, a thinking-capable
|
|
340
|
+
* model (for example `qwen3`) separates its reasoning NATIVELY at the wire — the daemon returns it
|
|
173
341
|
* on the distinct `message.thinking` channel (surfaced on `ProviderResult.thinking`) rather
|
|
174
|
-
* than inline in `message.content`. The default
|
|
175
|
-
*
|
|
342
|
+
* than inline in `message.content`. The default is `false`, so a non-thinking model needs no
|
|
343
|
+
* configuration and answers immediately; the per-call ThinkSplitter
|
|
176
344
|
* remains the defensive fallback for daemons/models that still inline `<think>` tags either
|
|
177
345
|
* way. Set it `true` for a thinking model whose reasoning you intend to DISPLAY separately.
|
|
178
346
|
*/
|
|
179
347
|
readonly think?: boolean;
|
|
180
348
|
/**
|
|
181
|
-
*
|
|
349
|
+
* Sets a custom `fetch` implementation for every request; defaults to
|
|
182
350
|
* `globalThis.fetch`. Lets a runtime inject its own transport (a browser fetch
|
|
183
|
-
* pointed at the developer's server, an instrumented wrapper
|
|
351
|
+
* pointed at the developer's server, an instrumented wrapper) without changing
|
|
184
352
|
* the wire protocol. Omitted ⇒ the global `fetch`.
|
|
185
353
|
*/
|
|
186
354
|
readonly fetch?: typeof globalThis.fetch;
|
|
187
355
|
/**
|
|
188
|
-
*
|
|
356
|
+
* Sets a dynamic, possibly-async header injector called once per request; its returned
|
|
189
357
|
* headers are merged into the request on top of the base `Content-Type`. Use it to
|
|
190
|
-
* attach an authorization header —
|
|
358
|
+
* attach an authorization header — for example an obfuscated/generated bearer token the
|
|
191
359
|
* developer's server validates before relaying to the real LLM — so a browser
|
|
192
360
|
* runtime can authenticate WITHOUT your app ever handling a real API key. Async so a
|
|
193
361
|
* token can be refreshed/fetched per call. A returned `Content-Type` overrides the
|
|
194
362
|
* default; other headers add to it. Omitted ⇒ only `Content-Type: application/json`.
|
|
195
363
|
*/
|
|
196
|
-
readonly headers?: () => Record<string, string
|
|
364
|
+
readonly headers?: () => Readonly<Record<string, string>> | Promise<Readonly<Record<string, string>>>;
|
|
197
365
|
/**
|
|
198
|
-
*
|
|
366
|
+
* Sets the provider's OPTIONAL context-framing default — the PROVIDER-DEFAULT level of
|
|
199
367
|
* `AgentContext`'s format cascade (beaten by a manager-options or per-item override,
|
|
200
368
|
* beating the managers' built-in framing). Declares how this provider's models prefer
|
|
201
|
-
* context sections framed (
|
|
369
|
+
* context sections framed (for example XML group wrappers vs. Markdown headers). Omitted ⇒
|
|
202
370
|
* the provider is framing-agnostic and core's built-in defaults apply unchanged. NOTE:
|
|
203
371
|
* this is the prompt-CONTEXT framing consumed by `AgentContext.build()` — it is NOT
|
|
204
372
|
* Ollama's `/api/chat` `format` wire parameter (structured-output / JSON schema),
|
|
205
|
-
* which this provider
|
|
206
|
-
* shared word.
|
|
373
|
+
* which this provider sends only when a call supplies a `schema`; the two are unrelated
|
|
374
|
+
* despite the shared word.
|
|
207
375
|
*/
|
|
208
|
-
readonly format?:
|
|
376
|
+
readonly format?: ContextFormat;
|
|
209
377
|
}
|
|
210
378
|
|
|
211
379
|
/**
|
|
212
|
-
*
|
|
380
|
+
* Implements the local Ollama inference boundary — a {@link ProviderInterface} over Ollama's
|
|
213
381
|
* `POST /api/chat`, both non-streaming (`generate`) and streaming NDJSON (`stream`).
|
|
214
382
|
*
|
|
215
383
|
* @remarks
|
|
216
384
|
* - **Wire protocol.** Posts `{ model, messages, stream, keep_alive, think }` plus
|
|
217
385
|
* passthrough sampling `options` and mapped function `tools`. The `think` flag is
|
|
218
|
-
* CONFIGURABLE
|
|
386
|
+
* CONFIGURABLE through {@link OllamaOptions.think} (default `false`). Non-stream parses
|
|
219
387
|
* one JSON body; stream consumes NDJSON (one JSON object per `\n`-terminated line) —
|
|
220
388
|
* deltas carry `message.content`, the final `done: true` line carries the token usage.
|
|
221
|
-
* - **Think separation
|
|
389
|
+
* - **Think separation.** The wire `think` flag is configurable
|
|
222
390
|
* ({@link OllamaOptions.think}, default `false`). With `think: true` a thinking model's
|
|
223
391
|
* daemon separates reasoning NATIVELY — returning it on the distinct `message.thinking`
|
|
224
|
-
* channel (read here
|
|
392
|
+
* channel (read here through `extractThinking`) instead of inline in `message.content`. EITHER
|
|
225
393
|
* way the per-call {@link ThinkSplitterInterface} is the defensive guarantee: a daemon
|
|
226
394
|
* may ignore `think: false` for a thinking model and inline `<think>` tags, so every
|
|
227
395
|
* content delta routes through the splitter, only CLEAN content is yielded / assembled,
|
|
228
396
|
* and the separated reasoning (plus any daemon-side `message.thinking` deltas) lands on
|
|
229
397
|
* `ProviderResult.thinking`, never in the conversation.
|
|
230
|
-
* - **Boundary narrowing
|
|
398
|
+
* - **Boundary narrowing.** Every wire value arrives as `unknown` and is
|
|
231
399
|
* narrowed through guards (`isRecord` / `isString` / `isNumber`) — never `as`. A
|
|
232
400
|
* missing / malformed field degrades to a sensible default (empty content, no
|
|
233
401
|
* usage, `{}` arguments), never a throw.
|
|
@@ -244,7 +412,8 @@ export declare interface OllamaOptions {
|
|
|
244
412
|
* `globalThis.fetch`) and {@link OllamaOptions.headers} is a per-request, possibly
|
|
245
413
|
* async header injector merged over the base `Content-Type` — so a browser runtime
|
|
246
414
|
* can route through the developer's own server with an obfuscated bearer token,
|
|
247
|
-
* without this library ever handling a real API key. Both omitted ⇒
|
|
415
|
+
* without this library ever handling a real API key. Both omitted ⇒ the global `fetch`
|
|
416
|
+
* and only a JSON content type.
|
|
248
417
|
* Orthogonal to the deadline: the hook is awaited inside `#fetch`'s try, so a hook
|
|
249
418
|
* rejection clears the armed timer like any other request failure.
|
|
250
419
|
*
|
|
@@ -256,11 +425,18 @@ export declare interface OllamaOptions {
|
|
|
256
425
|
*/
|
|
257
426
|
export declare class OllamaProvider implements ProviderInterface {
|
|
258
427
|
#private;
|
|
259
|
-
readonly id: `${string}-${string}-${string}-${string}-${string}`;
|
|
260
428
|
readonly name = "ollama";
|
|
261
429
|
constructor(options: OllamaOptions);
|
|
262
430
|
/**
|
|
263
|
-
*
|
|
431
|
+
* Exposes this instance's identity — a fresh `crypto.randomUUID()` minted at
|
|
432
|
+
* construction, satisfying the {@link ProviderInterface.id} contract member. A second
|
|
433
|
+
* provider built from identical options carries a distinct id.
|
|
434
|
+
*
|
|
435
|
+
* @returns The instance's minted identifier
|
|
436
|
+
*/
|
|
437
|
+
get id(): string;
|
|
438
|
+
/**
|
|
439
|
+
* Exposes the provider's context-framing default — the PROVIDER-DEFAULT level of
|
|
264
440
|
* {@link import('@orkestrel/agent').AgentContextInterface.build}'s format cascade (it BEATS
|
|
265
441
|
* the managers' built-in framing, is BEATEN by a manager-options or per-item override).
|
|
266
442
|
* Satisfies the OPTIONAL {@link ProviderInterface.format} contract member: `undefined`
|
|
@@ -274,21 +450,21 @@ export declare class OllamaProvider implements ProviderInterface {
|
|
|
274
450
|
* structured-output `format` wire parameter — that one IS sent in `#body`, but only when
|
|
275
451
|
* a per-call `ProviderStreamOptions.schema` is supplied; only the word collides.
|
|
276
452
|
*
|
|
277
|
-
* @returns The configured {@link
|
|
453
|
+
* @returns The configured {@link ContextFormat}, or `undefined` when none
|
|
278
454
|
*/
|
|
279
|
-
get format():
|
|
280
|
-
generate(messages: readonly
|
|
281
|
-
stream(messages: readonly
|
|
455
|
+
get format(): ContextFormat | undefined;
|
|
456
|
+
generate(messages: readonly Message[], signal: AbortSignal, tools?: readonly ToolDefinition[], options?: ProviderStreamOptions): Promise<ProviderResult>;
|
|
457
|
+
stream(messages: readonly Message[], signal: AbortSignal, tools?: readonly ToolDefinition[], options?: ProviderStreamOptions): AsyncGenerator<ProviderDelta, ProviderResult>;
|
|
282
458
|
}
|
|
283
459
|
|
|
284
460
|
/**
|
|
285
|
-
*
|
|
286
|
-
*
|
|
461
|
+
* Represents an open `POST /api/chat` response together with the deadline and the
|
|
462
|
+
* combined signal that bound the request.
|
|
287
463
|
*
|
|
288
464
|
* @remarks
|
|
289
465
|
* The `response` is the open `POST /api/chat` `Response`; `timeout` is the armed
|
|
290
466
|
* {@link TimeoutInterface} the consuming call clears once it finishes reading the body
|
|
291
|
-
* (or that
|
|
467
|
+
* (or that the provider clears on a failed or aborted request); `combined` is the
|
|
292
468
|
* `AbortSignal.any([timeout.signal, callerSignal])` the request was issued under, which
|
|
293
469
|
* the streaming path checks to tell a mid-stream cancel apart from any other error.
|
|
294
470
|
*/
|
|
@@ -299,13 +475,33 @@ export declare interface OllamaResponse {
|
|
|
299
475
|
}
|
|
300
476
|
|
|
301
477
|
/**
|
|
302
|
-
*
|
|
478
|
+
* Parses a non-stream `/api/chat` response body into a wire record.
|
|
479
|
+
*
|
|
480
|
+
* @remarks
|
|
481
|
+
* Total by construction: an empty body, a body that is not JSON, and a body whose JSON is
|
|
482
|
+
* not an object all yield `undefined`, so a malformed daemon response never escapes as a
|
|
483
|
+
* `SyntaxError`. The call site supplies the empty-record default that reads as empty
|
|
484
|
+
* content and no usage.
|
|
485
|
+
*
|
|
486
|
+
* @param response - The 200-OK `/api/chat` response whose body is read as text
|
|
487
|
+
* @returns The parsed record, or `undefined` when the body is empty or malformed
|
|
488
|
+
*
|
|
489
|
+
* @example
|
|
490
|
+
* ```ts
|
|
491
|
+
* await parseBody(new Response('{"message":{"content":"ok"}}'))
|
|
492
|
+
* // { message: { content: 'ok' } }
|
|
493
|
+
* ```
|
|
494
|
+
*/
|
|
495
|
+
export declare function parseBody(response: Response): Promise<Readonly<Record<string, unknown>> | undefined>;
|
|
496
|
+
|
|
497
|
+
/**
|
|
498
|
+
* Represents the exact `POST /api/chat` request body `OllamaProvider` sends — the internal typed
|
|
303
499
|
* wire contract.
|
|
304
500
|
*
|
|
305
501
|
* @remarks
|
|
306
502
|
* This is the typed wire shape asserted against the official `ollama` client's
|
|
307
503
|
* `ChatRequest` by the compile-time parity test; `src/` never imports `ollama` itself.
|
|
308
|
-
* `messages` mirrors the minimal turn shape
|
|
504
|
+
* `messages` mirrors the minimal turn shape `mapMessages` builds (`role` / `content`, plus
|
|
309
505
|
* `tool_calls` only on a turn that replays them and `images` only on a multimodal
|
|
310
506
|
* turn); `options` and `tools` are only present when configured.
|
|
311
507
|
*/
|
|
@@ -335,7 +531,7 @@ export declare interface WireChatRequest {
|
|
|
335
531
|
};
|
|
336
532
|
}>;
|
|
337
533
|
/**
|
|
338
|
-
*
|
|
534
|
+
* Holds the `/api/chat` structured-output constraint — a JSON-Schema object forwarded
|
|
339
535
|
* verbatim from the per-call `ProviderStreamOptions.schema`. This is NOT
|
|
340
536
|
* `OllamaOptions.format` (the unrelated prompt-context framing); only present
|
|
341
537
|
* when a call supplies a `schema`.
|