@nemesis-oss/ollama-sdk 1.0.0 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +34 -0
- package/README.md +36 -10
- package/dist/{compose-DC11aDJn.d.cts → compose-DJjVpDrk.d.cts} +29 -2
- package/dist/{compose-DC11aDJn.d.ts → compose-DJjVpDrk.d.ts} +29 -2
- package/dist/index.cjs +54 -12
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +107 -10
- package/dist/index.d.ts +107 -10
- package/dist/index.js +54 -13
- package/dist/index.js.map +1 -1
- package/dist/skills.d.cts +2 -2
- package/dist/skills.d.ts +2 -2
- package/package.json +5 -4
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,40 @@ All notable changes to `@nemesis-oss/ollama-sdk` will be documented in this file
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [1.1.0] - 2026-08-23
|
|
9
|
+
|
|
10
|
+
Brings the SDK's typed surface up to date with Ollama v0.13.3+.
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- **`/api/create` field parity:** `CreateRequestOptions` now exposes the documented
|
|
15
|
+
`template`, `renderer`, `parser`, `license`, `system`, `parameters`, and `messages`
|
|
16
|
+
fields alongside the existing `from`/`files`/`adapters`/`quantize`. The old
|
|
17
|
+
`modelfile` string field is kept for backward compatibility but marked `@deprecated`
|
|
18
|
+
in favor of the discrete fields.
|
|
19
|
+
- **OpenAI Responses API bridge:** `client.openai.responses()` / `createResponses()`
|
|
20
|
+
POST to `/v1/responses`. Ollama implements this non-statefully, so
|
|
21
|
+
`previous_response_id`/`conversation` are typed but documented as ignored.
|
|
22
|
+
- **`reasoning_effort` / `reasoning.effort`:** Added to `OpenAIChatCompletionRequest`
|
|
23
|
+
for thinking models (`deepseek-r1`, `qwen3`, etc.), matching Ollama's OpenAI
|
|
24
|
+
compatibility docs.
|
|
25
|
+
- **`tool_choice` / `parallel_tool_calls`:** Now typed as accepted-but-ignored on
|
|
26
|
+
`OpenAIChatCompletionRequest` (previously omitted entirely), so passing a standard
|
|
27
|
+
OpenAI request object type-checks without modification.
|
|
28
|
+
- **Raw image bytes:** `Message.images` and `GenerateRequestOptions.images` accept
|
|
29
|
+
`Uint8Array` alongside base64 strings. `OllamaClient.chat()`/`generate()` encode any
|
|
30
|
+
`Uint8Array` entries to base64 automatically via the new `encodeImage` utility
|
|
31
|
+
(exported from the package root), using `Buffer` on Node and `btoa` elsewhere so it
|
|
32
|
+
stays Edge Runtime-safe.
|
|
33
|
+
- **`doneReason` in stream results:** `ChatStreamResult`/`GenerateStreamResult` now
|
|
34
|
+
carry `doneReason`, mapped from the final chunk's `done_reason` (e.g. `"stop"`,
|
|
35
|
+
`"load"`, `"unload"`).
|
|
36
|
+
|
|
37
|
+
### Fixed
|
|
38
|
+
|
|
39
|
+
- `package.json`'s `homepage`, `repository`, and `bugs` URLs now point at
|
|
40
|
+
`ollama-sdk` instead of the pre-rename `ollama-client-ts`.
|
|
41
|
+
|
|
8
42
|
## [1.0.0] - 2026-08-17
|
|
9
43
|
|
|
10
44
|
First public release. The version was `0.1.0` throughout development but was never
|
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @nemesis-oss/ollama-sdk
|
|
2
2
|
|
|
3
|
-
[](https://github.com/shubhamtaywade82/ollama-sdk/actions/workflows/ci.yml)
|
|
4
4
|
[](https://www.npmjs.com/package/@nemesis-oss/ollama-sdk)
|
|
5
5
|
[](https://opensource.org/licenses/MIT)
|
|
6
6
|
|
|
@@ -11,12 +11,12 @@
|
|
|
11
11
|
## Key Features
|
|
12
12
|
|
|
13
13
|
- 🚀 **Native Web Standards**: Built on native `fetch` and Web Streams. Zero external HTTP dependencies.
|
|
14
|
-
- 🧠 **Reasoning & Thinking Tokens**: First-class support for reasoning models (`qwen3
|
|
14
|
+
- 🧠 **Reasoning & Thinking Tokens**: First-class support for reasoning models (`qwen3:8b`, `deepseek-r1:8b`) with discrete `thinking` and `token` streaming events.
|
|
15
15
|
- 🎯 **Zod-Powered Structured Outputs**: Strictly typed schema enforcement via `chatWithSchema` and `generateWithSchema` with resilient markdown JSON parsing.
|
|
16
16
|
- 🛠️ **Autonomous Agent & Tool Calling**: Multi-turn agent loop (`Agent`) with automated tool execution, parameter validation, and self-correcting error recovery.
|
|
17
17
|
- 🌐 **High Availability & Failover**: Multi-endpoint registry with priority routing, circuit breaker failover, and active health checks.
|
|
18
18
|
- 🔌 **Model Context Protocol (MCP)**: Native adapters to convert MCP tools into Ollama-compatible function schemas.
|
|
19
|
-
- 🌉 **OpenAI & Anthropic Compatibility Bridges**: Built-in clients for `/v1/chat/completions`, `/v1/models`, and `/v1/messages
|
|
19
|
+
- 🌉 **OpenAI & Anthropic Compatibility Bridges**: Built-in clients for `/v1/chat/completions`, `/v1/responses`, `/v1/models`, and `/v1/messages`, including `reasoning_effort`/`reasoning.effort` for thinking models.
|
|
20
20
|
- 🌊 **Web Stream Adapters**: Drop-in adapters (`toTextStream`, `toDataStream`, `toResponse`) for Next.js Route Handlers and Vercel AI SDK.
|
|
21
21
|
- 📈 **OpenTelemetry Instrumentation**: Automatic spans for HTTP requests, endpoint failover, chat/generate calls, and agent runs — zero-cost when OpenTelemetry isn't installed.
|
|
22
22
|
- ⚡ **Edge Runtime Verified**: CI bundles and runs the client in a real Edge Runtime sandbox (Cloudflare Workers/Vercel Edge-compatible) with zero Node.js APIs.
|
|
@@ -45,7 +45,7 @@ const client = new OllamaClient();
|
|
|
45
45
|
|
|
46
46
|
// Text helper
|
|
47
47
|
const answer = await client.chatText({
|
|
48
|
-
model: 'qwen3
|
|
48
|
+
model: 'qwen3:8b',
|
|
49
49
|
messages: [{ role: 'user', content: 'Explain quantum computing in one sentence.' }],
|
|
50
50
|
});
|
|
51
51
|
console.log(answer);
|
|
@@ -55,7 +55,7 @@ console.log(answer);
|
|
|
55
55
|
|
|
56
56
|
```typescript
|
|
57
57
|
const stream = await client.chatStream({
|
|
58
|
-
model: 'qwen3
|
|
58
|
+
model: 'qwen3:8b',
|
|
59
59
|
messages: [{ role: 'user', content: 'What is 18 * 4?' }],
|
|
60
60
|
options: { temperature: 0 },
|
|
61
61
|
});
|
|
@@ -86,7 +86,7 @@ const ProductSchema = z.object({
|
|
|
86
86
|
|
|
87
87
|
const product = await client.chatWithSchema(
|
|
88
88
|
{
|
|
89
|
-
model: 'qwen3
|
|
89
|
+
model: 'qwen3:8b',
|
|
90
90
|
messages: [{ role: 'user', content: 'Generate a gaming keyboard item.' }],
|
|
91
91
|
},
|
|
92
92
|
ProductSchema,
|
|
@@ -131,7 +131,7 @@ const registry = new ToolRegistry([weatherTool]);
|
|
|
131
131
|
const agent = new Agent(client, { tools: registry, maxIterations: 5 });
|
|
132
132
|
|
|
133
133
|
const response = await agent.run({
|
|
134
|
-
model: 'qwen3
|
|
134
|
+
model: 'qwen3:8b',
|
|
135
135
|
messages: [{ role: 'user', content: 'What is the weather in Tokyo?' }],
|
|
136
136
|
});
|
|
137
137
|
|
|
@@ -191,7 +191,7 @@ import { toResponse } from '@nemesis-oss/ollama-sdk';
|
|
|
191
191
|
export async function POST(req: Request) {
|
|
192
192
|
const { messages } = await req.json();
|
|
193
193
|
const stream = await client.chatStream({
|
|
194
|
-
model: 'qwen3
|
|
194
|
+
model: 'qwen3:8b',
|
|
195
195
|
messages,
|
|
196
196
|
});
|
|
197
197
|
|
|
@@ -208,6 +208,13 @@ const openAIRes = await client.openai.chatCompletions({
|
|
|
208
208
|
messages: [{ role: 'user', content: 'Hello via OpenAI bridge' }],
|
|
209
209
|
});
|
|
210
210
|
|
|
211
|
+
// OpenAI Responses API endpoint (/v1/responses) — added in Ollama v0.13.3
|
|
212
|
+
const responsesRes = await client.openai.responses({
|
|
213
|
+
model: 'llama3.2',
|
|
214
|
+
input: 'Hello via the OpenAI Responses bridge',
|
|
215
|
+
});
|
|
216
|
+
console.log(responsesRes.output[0]?.content[0]?.text);
|
|
217
|
+
|
|
211
218
|
// Anthropic compatibility endpoint (/v1/messages)
|
|
212
219
|
const anthropicRes = await client.anthropic.messages({
|
|
213
220
|
model: 'llama3.2',
|
|
@@ -215,6 +222,25 @@ const anthropicRes = await client.anthropic.messages({
|
|
|
215
222
|
});
|
|
216
223
|
```
|
|
217
224
|
|
|
225
|
+
`/v1/responses` is implemented non-statefully by Ollama: send the full conversation in
|
|
226
|
+
`input` on every call — `previous_response_id` and `conversation` are accepted for
|
|
227
|
+
OpenAI request-shape compatibility but ignored (see `OpenAIResponsesRequest` JSDoc).
|
|
228
|
+
|
|
229
|
+
For thinking models (`deepseek-r1`, `qwen3`, etc.), both compatibility bridges' chat
|
|
230
|
+
completions request accept a reasoning effort knob:
|
|
231
|
+
|
|
232
|
+
```typescript
|
|
233
|
+
await client.openai.chatCompletions({
|
|
234
|
+
model: 'deepseek-r1:8b',
|
|
235
|
+
messages: [{ role: 'user', content: 'Solve: 17 * 23' }],
|
|
236
|
+
reasoning_effort: 'high', // or `reasoning: { effort: 'high' }`
|
|
237
|
+
});
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
`tool_choice` and `parallel_tool_calls` are also typed on the request so a standard
|
|
241
|
+
OpenAI request object type-checks unmodified, but Ollama's compat layer does not honor
|
|
242
|
+
either — see the `@remarks` on each field in `OpenAIChatCompletionRequest`.
|
|
243
|
+
|
|
218
244
|
### Multi-Endpoint High Availability Failover
|
|
219
245
|
|
|
220
246
|
```typescript
|
|
@@ -325,7 +351,7 @@ the original error via the standard `cause` property:
|
|
|
325
351
|
import { OllamaClientError, OllamaRateLimitError } from '@nemesis-oss/ollama-sdk';
|
|
326
352
|
|
|
327
353
|
try {
|
|
328
|
-
await client.chatText({ model: 'qwen3
|
|
354
|
+
await client.chatText({ model: 'qwen3:8b', messages: [...] });
|
|
329
355
|
} catch (err) {
|
|
330
356
|
if (err instanceof OllamaRateLimitError) {
|
|
331
357
|
console.warn(`Rate limited, retry after ${err.retryAfterMs}ms`);
|
|
@@ -347,7 +373,7 @@ observe per-endpoint circuit state directly.
|
|
|
347
373
|
|
|
348
374
|
## Testing
|
|
349
375
|
|
|
350
|
-
The test suite contains
|
|
376
|
+
The test suite contains 114 automated tests across 4 testing tiers:
|
|
351
377
|
|
|
352
378
|
```bash
|
|
353
379
|
# Run unit, integration, and functional test suite
|
|
@@ -28,7 +28,12 @@ interface ToolCall {
|
|
|
28
28
|
interface Message {
|
|
29
29
|
readonly role: Role;
|
|
30
30
|
readonly content: string;
|
|
31
|
-
|
|
31
|
+
/**
|
|
32
|
+
* Base64-encoded image strings, or raw `Uint8Array` image bytes. `Uint8Array` entries
|
|
33
|
+
* are base64-encoded automatically (see {@link encodeImage}) before the request reaches
|
|
34
|
+
* the wire — Ollama's REST API only ever accepts base64 strings.
|
|
35
|
+
*/
|
|
36
|
+
readonly images?: readonly (string | Uint8Array)[] | undefined;
|
|
32
37
|
readonly tool_calls?: readonly ToolCall[] | undefined;
|
|
33
38
|
/** Set on a `role: 'tool'` message to identify which {@link ToolCall.id} this answers. */
|
|
34
39
|
readonly tool_call_id?: string | undefined;
|
|
@@ -123,7 +128,12 @@ interface GenerateRequestOptions extends RequestCancellationOptions {
|
|
|
123
128
|
readonly stream?: boolean | undefined;
|
|
124
129
|
readonly raw?: boolean | undefined;
|
|
125
130
|
readonly format?: FormatOption | undefined;
|
|
126
|
-
|
|
131
|
+
/**
|
|
132
|
+
* Base64-encoded image strings, or raw `Uint8Array` image bytes. `Uint8Array` entries
|
|
133
|
+
* are base64-encoded automatically (see {@link encodeImage}) before the request reaches
|
|
134
|
+
* the wire — Ollama's REST API only ever accepts base64 strings.
|
|
135
|
+
*/
|
|
136
|
+
readonly images?: readonly (string | Uint8Array)[] | undefined;
|
|
127
137
|
readonly options?: ModelOptions | undefined;
|
|
128
138
|
readonly keep_alive?: string | number | undefined;
|
|
129
139
|
readonly think?: boolean | 'low' | 'medium' | 'high' | 'max' | undefined;
|
|
@@ -225,12 +235,29 @@ interface PushRequestOptions extends RequestCancellationOptions {
|
|
|
225
235
|
}
|
|
226
236
|
interface CreateRequestOptions extends RequestCancellationOptions {
|
|
227
237
|
readonly model: string;
|
|
238
|
+
/**
|
|
239
|
+
* @deprecated Not part of the official `/api/create` REST payload — Ollama's server
|
|
240
|
+
* only ever accepted this in older client tooling that assembled a Modelfile string
|
|
241
|
+
* client-side. Use the discrete fields below instead (`from`, `files`, `adapters`,
|
|
242
|
+
* `template`, `system`, `parameters`, `license`, `messages`), which map 1:1 onto the
|
|
243
|
+
* documented Modelfile instructions and are validated server-side. Kept for backward
|
|
244
|
+
* compatibility with existing callers; not read by this SDK, but still forwarded as-is
|
|
245
|
+
* in the request body for any server/proxy that inspects it.
|
|
246
|
+
*/
|
|
228
247
|
readonly modelfile?: string | undefined;
|
|
229
248
|
readonly stream?: boolean | undefined;
|
|
230
249
|
readonly quantize?: string | undefined;
|
|
231
250
|
readonly from?: string | undefined;
|
|
232
251
|
readonly files?: Record<string, string> | undefined;
|
|
233
252
|
readonly adapters?: Record<string, string> | undefined;
|
|
253
|
+
readonly template?: string | undefined;
|
|
254
|
+
readonly renderer?: string | undefined;
|
|
255
|
+
readonly parser?: string | undefined;
|
|
256
|
+
readonly license?: string | readonly string[] | undefined;
|
|
257
|
+
readonly system?: string | undefined;
|
|
258
|
+
/** Modelfile `PARAMETER` instructions, e.g. `{ temperature: 0.7, stop: ['\n'] }`. */
|
|
259
|
+
readonly parameters?: Record<string, unknown> | undefined;
|
|
260
|
+
readonly messages?: readonly Message[] | undefined;
|
|
234
261
|
}
|
|
235
262
|
interface DeleteRequestOptions extends RequestCancellationOptions {
|
|
236
263
|
readonly model: string;
|
|
@@ -28,7 +28,12 @@ interface ToolCall {
|
|
|
28
28
|
interface Message {
|
|
29
29
|
readonly role: Role;
|
|
30
30
|
readonly content: string;
|
|
31
|
-
|
|
31
|
+
/**
|
|
32
|
+
* Base64-encoded image strings, or raw `Uint8Array` image bytes. `Uint8Array` entries
|
|
33
|
+
* are base64-encoded automatically (see {@link encodeImage}) before the request reaches
|
|
34
|
+
* the wire — Ollama's REST API only ever accepts base64 strings.
|
|
35
|
+
*/
|
|
36
|
+
readonly images?: readonly (string | Uint8Array)[] | undefined;
|
|
32
37
|
readonly tool_calls?: readonly ToolCall[] | undefined;
|
|
33
38
|
/** Set on a `role: 'tool'` message to identify which {@link ToolCall.id} this answers. */
|
|
34
39
|
readonly tool_call_id?: string | undefined;
|
|
@@ -123,7 +128,12 @@ interface GenerateRequestOptions extends RequestCancellationOptions {
|
|
|
123
128
|
readonly stream?: boolean | undefined;
|
|
124
129
|
readonly raw?: boolean | undefined;
|
|
125
130
|
readonly format?: FormatOption | undefined;
|
|
126
|
-
|
|
131
|
+
/**
|
|
132
|
+
* Base64-encoded image strings, or raw `Uint8Array` image bytes. `Uint8Array` entries
|
|
133
|
+
* are base64-encoded automatically (see {@link encodeImage}) before the request reaches
|
|
134
|
+
* the wire — Ollama's REST API only ever accepts base64 strings.
|
|
135
|
+
*/
|
|
136
|
+
readonly images?: readonly (string | Uint8Array)[] | undefined;
|
|
127
137
|
readonly options?: ModelOptions | undefined;
|
|
128
138
|
readonly keep_alive?: string | number | undefined;
|
|
129
139
|
readonly think?: boolean | 'low' | 'medium' | 'high' | 'max' | undefined;
|
|
@@ -225,12 +235,29 @@ interface PushRequestOptions extends RequestCancellationOptions {
|
|
|
225
235
|
}
|
|
226
236
|
interface CreateRequestOptions extends RequestCancellationOptions {
|
|
227
237
|
readonly model: string;
|
|
238
|
+
/**
|
|
239
|
+
* @deprecated Not part of the official `/api/create` REST payload — Ollama's server
|
|
240
|
+
* only ever accepted this in older client tooling that assembled a Modelfile string
|
|
241
|
+
* client-side. Use the discrete fields below instead (`from`, `files`, `adapters`,
|
|
242
|
+
* `template`, `system`, `parameters`, `license`, `messages`), which map 1:1 onto the
|
|
243
|
+
* documented Modelfile instructions and are validated server-side. Kept for backward
|
|
244
|
+
* compatibility with existing callers; not read by this SDK, but still forwarded as-is
|
|
245
|
+
* in the request body for any server/proxy that inspects it.
|
|
246
|
+
*/
|
|
228
247
|
readonly modelfile?: string | undefined;
|
|
229
248
|
readonly stream?: boolean | undefined;
|
|
230
249
|
readonly quantize?: string | undefined;
|
|
231
250
|
readonly from?: string | undefined;
|
|
232
251
|
readonly files?: Record<string, string> | undefined;
|
|
233
252
|
readonly adapters?: Record<string, string> | undefined;
|
|
253
|
+
readonly template?: string | undefined;
|
|
254
|
+
readonly renderer?: string | undefined;
|
|
255
|
+
readonly parser?: string | undefined;
|
|
256
|
+
readonly license?: string | readonly string[] | undefined;
|
|
257
|
+
readonly system?: string | undefined;
|
|
258
|
+
/** Modelfile `PARAMETER` instructions, e.g. `{ temperature: 0.7, stop: ['\n'] }`. */
|
|
259
|
+
readonly parameters?: Record<string, unknown> | undefined;
|
|
260
|
+
readonly messages?: readonly Message[] | undefined;
|
|
234
261
|
}
|
|
235
262
|
interface DeleteRequestOptions extends RequestCancellationOptions {
|
|
236
263
|
readonly model: string;
|
package/dist/index.cjs
CHANGED
|
@@ -822,6 +822,7 @@ function aggregateChat(accumulated, chunk) {
|
|
|
822
822
|
},
|
|
823
823
|
model: chunk.model,
|
|
824
824
|
done: chunk.done,
|
|
825
|
+
doneReason: chunk.done ? chunk.done_reason : accumulated.doneReason,
|
|
825
826
|
totalDurationMs: chunk.done && chunk.total_duration !== void 0 ? chunk.total_duration / NANOS_PER_MS : accumulated.totalDurationMs,
|
|
826
827
|
usage: chunk.done ? extractUsage(chunk) : accumulated.usage,
|
|
827
828
|
raw: chunk
|
|
@@ -871,6 +872,7 @@ function aggregateGenerate(accumulated, chunk) {
|
|
|
871
872
|
response: accumulated.response + (chunk.response ?? ""),
|
|
872
873
|
model: chunk.model,
|
|
873
874
|
done: chunk.done,
|
|
875
|
+
doneReason: chunk.done ? chunk.done_reason : accumulated.doneReason,
|
|
874
876
|
totalDurationMs: chunk.done && chunk.total_duration !== void 0 ? chunk.total_duration / NANOS_PER_MS : accumulated.totalDurationMs,
|
|
875
877
|
usage: chunk.done ? extractUsage(chunk) : accumulated.usage,
|
|
876
878
|
raw: chunk
|
|
@@ -1159,6 +1161,16 @@ var OpenAICompatClient = class {
|
|
|
1159
1161
|
signal
|
|
1160
1162
|
});
|
|
1161
1163
|
}
|
|
1164
|
+
async createResponses(request, signal) {
|
|
1165
|
+
return this.http.request({
|
|
1166
|
+
path: "/v1/responses",
|
|
1167
|
+
body: request,
|
|
1168
|
+
signal
|
|
1169
|
+
});
|
|
1170
|
+
}
|
|
1171
|
+
async responses(request, signal) {
|
|
1172
|
+
return this.createResponses(request, signal);
|
|
1173
|
+
}
|
|
1162
1174
|
};
|
|
1163
1175
|
|
|
1164
1176
|
// src/integrations/anthropic.ts
|
|
@@ -1179,6 +1191,32 @@ var AnthropicCompatClient = class {
|
|
|
1179
1191
|
}
|
|
1180
1192
|
};
|
|
1181
1193
|
|
|
1194
|
+
// src/utils.ts
|
|
1195
|
+
async function encodeImage(image) {
|
|
1196
|
+
if (typeof image === "string") return image;
|
|
1197
|
+
const bufferCtor = globalThis.Buffer;
|
|
1198
|
+
if (bufferCtor !== void 0) return bufferCtor.from(image).toString("base64");
|
|
1199
|
+
const CHUNK_SIZE = 32768;
|
|
1200
|
+
let binary = "";
|
|
1201
|
+
for (let i = 0; i < image.length; i += CHUNK_SIZE) {
|
|
1202
|
+
binary += String.fromCharCode(...image.subarray(i, i + CHUNK_SIZE));
|
|
1203
|
+
}
|
|
1204
|
+
return btoa(binary);
|
|
1205
|
+
}
|
|
1206
|
+
async function encodeImages(images) {
|
|
1207
|
+
if (images === void 0) return void 0;
|
|
1208
|
+
return Promise.all(images.map((image) => encodeImage(image)));
|
|
1209
|
+
}
|
|
1210
|
+
async function withEncodedImages(req) {
|
|
1211
|
+
if (!req.images?.some((image) => image instanceof Uint8Array)) return req;
|
|
1212
|
+
return { ...req, images: await encodeImages(req.images) };
|
|
1213
|
+
}
|
|
1214
|
+
async function withEncodedMessageImages(messages) {
|
|
1215
|
+
if (!messages.some((message) => message.images?.some((image) => image instanceof Uint8Array)))
|
|
1216
|
+
return messages;
|
|
1217
|
+
return Promise.all(messages.map((message) => withEncodedImages(message)));
|
|
1218
|
+
}
|
|
1219
|
+
|
|
1182
1220
|
// src/client.ts
|
|
1183
1221
|
var OllamaClient = class {
|
|
1184
1222
|
registry;
|
|
@@ -1264,12 +1302,13 @@ var OllamaClient = class {
|
|
|
1264
1302
|
}
|
|
1265
1303
|
}
|
|
1266
1304
|
async chat(req) {
|
|
1305
|
+
const messages = await withEncodedMessageImages(req.messages);
|
|
1267
1306
|
if (req.stream) {
|
|
1268
1307
|
return this.executeWithFailover(async (http, signal) => {
|
|
1269
1308
|
if (req.format !== void 0) this.assertStructuredOutputSupported(http.baseUrl, req.model);
|
|
1270
1309
|
const stream = await http.requestStream({
|
|
1271
1310
|
path: "/api/chat",
|
|
1272
|
-
body: { ...req, stream: true },
|
|
1311
|
+
body: { ...req, messages, stream: true },
|
|
1273
1312
|
signal
|
|
1274
1313
|
});
|
|
1275
1314
|
return normalizeChatStream(stream);
|
|
@@ -1288,7 +1327,7 @@ var OllamaClient = class {
|
|
|
1288
1327
|
this.assertStructuredOutputSupported(http.baseUrl, req.model);
|
|
1289
1328
|
return http.request({
|
|
1290
1329
|
path: "/api/chat",
|
|
1291
|
-
body: { ...req, stream: false },
|
|
1330
|
+
body: { ...req, messages, stream: false },
|
|
1292
1331
|
signal
|
|
1293
1332
|
});
|
|
1294
1333
|
}, req);
|
|
@@ -1315,34 +1354,36 @@ var OllamaClient = class {
|
|
|
1315
1354
|
return parseStructuredOutput(res.message.content, schema);
|
|
1316
1355
|
}
|
|
1317
1356
|
async generate(req) {
|
|
1318
|
-
|
|
1357
|
+
const encodedReq = await withEncodedImages(req);
|
|
1358
|
+
if (encodedReq.stream) {
|
|
1319
1359
|
return this.executeWithFailover(async (http, signal) => {
|
|
1320
|
-
if (
|
|
1360
|
+
if (encodedReq.format !== void 0)
|
|
1361
|
+
this.assertStructuredOutputSupported(http.baseUrl, encodedReq.model);
|
|
1321
1362
|
const stream = await http.requestStream({
|
|
1322
1363
|
path: "/api/generate",
|
|
1323
|
-
body: { ...
|
|
1364
|
+
body: { ...encodedReq, stream: true },
|
|
1324
1365
|
signal
|
|
1325
1366
|
});
|
|
1326
1367
|
return normalizeGenerateStream(stream);
|
|
1327
|
-
},
|
|
1368
|
+
}, encodedReq);
|
|
1328
1369
|
}
|
|
1329
1370
|
return withSpan(
|
|
1330
|
-
`text_completion ${
|
|
1371
|
+
`text_completion ${encodedReq.model}`,
|
|
1331
1372
|
{
|
|
1332
1373
|
[ATTR_GEN_AI_SYSTEM]: GEN_AI_SYSTEM_OLLAMA,
|
|
1333
1374
|
[ATTR_GEN_AI_OPERATION_NAME]: "text_completion",
|
|
1334
|
-
[ATTR_GEN_AI_REQUEST_MODEL]:
|
|
1375
|
+
[ATTR_GEN_AI_REQUEST_MODEL]: encodedReq.model
|
|
1335
1376
|
},
|
|
1336
1377
|
async (span) => {
|
|
1337
1378
|
const res = await this.executeWithFailover((http, signal) => {
|
|
1338
|
-
if (
|
|
1339
|
-
this.assertStructuredOutputSupported(http.baseUrl,
|
|
1379
|
+
if (encodedReq.format !== void 0)
|
|
1380
|
+
this.assertStructuredOutputSupported(http.baseUrl, encodedReq.model);
|
|
1340
1381
|
return http.request({
|
|
1341
1382
|
path: "/api/generate",
|
|
1342
|
-
body: { ...
|
|
1383
|
+
body: { ...encodedReq, stream: false },
|
|
1343
1384
|
signal
|
|
1344
1385
|
});
|
|
1345
|
-
},
|
|
1386
|
+
}, encodedReq);
|
|
1346
1387
|
span?.setAttributes({
|
|
1347
1388
|
[ATTR_GEN_AI_RESPONSE_MODEL]: res.model,
|
|
1348
1389
|
...res.prompt_eval_count !== void 0 ? { [ATTR_GEN_AI_USAGE_INPUT_TOKENS]: res.prompt_eval_count } : {},
|
|
@@ -2026,6 +2067,7 @@ exports.createConsoleLogger = createConsoleLogger;
|
|
|
2026
2067
|
exports.createTimeoutSignal = createTimeoutSignal;
|
|
2027
2068
|
exports.defineTool = defineTool;
|
|
2028
2069
|
exports.detectModelCapabilities = detectModelCapabilities;
|
|
2070
|
+
exports.encodeImage = encodeImage;
|
|
2029
2071
|
exports.extractUsage = extractUsage;
|
|
2030
2072
|
exports.inferRuntimeMode = inferRuntimeMode;
|
|
2031
2073
|
exports.listAvailableModels = listAvailableModels;
|