@warlock.js/ai-ollama 5.30.0 → 5.31.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.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: setup-ollama
3
- description: 'Wire @warlock.js/ai-ollama — new OllamaSDK({host?, headers?}) for local / self-hosted Ollama via the official ollama client (not OpenAI-compat). chat + embed, daemon-down error handling. .model({name, vision?, reasoning?}) with cost-truth capabilities — reasoning inferred from thinking-model tags (deepseek-r1/qwq/qwen3/gpt-oss…), promptCaching/audio/pdf honestly false; options.reasoning → Ollama native `think` flag; usage reasoningTokens/cachedTokens stay undefined (none reported). Triggers: `OllamaSDK`, `ollama.model`, `ollama.embedder`, `embedder.embedMany`, `ollama.count`, `host`, `headers`, `reasoning`, `think`, thinking models, `deepseek-r1`, `qwq`, `qwen3`, `gpt-oss`; "use ollama with warlock", "run llama3 locally", "self-hosted llama", "ollama reasoning / thinking models", "deepseek-r1 with warlock"; typical import `import { OllamaSDK } from "@warlock.js/ai-ollama"`. Skip: agent loop — the `run-ai-agent` topic of the `warlock-js-ai` skill; provider choice — the `pick-ai-provider` topic of the `warlock-js-ai` skill; embeddings core — the `embed-text` topic of the `warlock-js-ai` skill; siblings `@warlock.js/ai-openai`, `@warlock.js/ai-anthropic`, `@warlock.js/ai-google`; raw `ollama` npm, Vercel `@ai-sdk/ollama`; OpenAI-compat gateway via `@warlock.js/ai-openai` `baseURL`.'
3
+ description: 'Wire @warlock.js/ai-ollama — new OllamaSDK({host?, headers?}) for local / self-hosted Ollama via the official ollama client (not OpenAI-compat). chat + embed, daemon-down error handling. .model({name, vision?, reasoning?}) with cost-truth capabilities — reasoning inferred from thinking-model tags (deepseek-r1/qwq/qwen3/gpt-oss…), promptCaching/audio/pdf honestly false; options.reasoning → Ollama native `think` flag; usage reasoningTokens/cachedTokens stay undefined (none reported). Triggers: `providerOptions`, `OllamaCallOptions`, `countTokens`, `OllamaSDK`, `ollama.model`, `ollama.embedder`, `embedder.embedMany`, `ollama.count`, `host`, `headers`, `reasoning`, `think`, thinking models, `streamChunks`, `message.thinking`, `deepseek-r1`, `qwq`, `qwen3`, `gpt-oss`; "use ollama with warlock", "run llama3 locally", "self-hosted llama", "ollama reasoning / thinking models", "deepseek-r1 with warlock"; typical import `import { OllamaSDK } from "@warlock.js/ai-ollama"`. Skip: agent loop — the `run-ai-agent` topic of the `warlock-js-ai` skill; provider choice — the `pick-ai-provider` topic of the `warlock-js-ai` skill; embeddings core — the `embed-text` topic of the `warlock-js-ai` skill; siblings `@warlock.js/ai-openai`, `@warlock.js/ai-anthropic`, `@warlock.js/ai-google`; raw `ollama` npm, Vercel `@ai-sdk/ollama`; OpenAI-compat gateway via `@warlock.js/ai-openai` `baseURL`.'
4
4
  ---
5
5
 
6
6
  # `@warlock.js/ai-ollama`
@@ -12,9 +12,9 @@ Provider adapter that turns a local (or self-hosted) Ollama server into a vendor
12
12
  ```ts
13
13
  import { OllamaSDK } from "@warlock.js/ai-ollama";
14
14
 
15
- const ollama = new OllamaSDK(); // local default host
15
+ const ollama = new OllamaSDK(); // local default host
16
16
  const remote = new OllamaSDK({ host: "http://gpu-box.internal:11434" });
17
- const gated = new OllamaSDK({
17
+ const gated = new OllamaSDK({
18
18
  host: "https://ollama.internal",
19
19
  headers: { Authorization: `Bearer ${process.env.OLLAMA_TOKEN}` },
20
20
  });
@@ -25,20 +25,20 @@ const gated = new OllamaSDK({
25
25
  ## Producing a model
26
26
 
27
27
  ```ts
28
- ollama.model({ name: "llama3.1" })
29
- ollama.model({ name: "qwen2.5:14b", temperature: 0.2 })
30
- ollama.model({ name: "llama3.2-vision", maxTokens: 1024 })
28
+ ollama.model({ name: "llama3.1" });
29
+ ollama.model({ name: "qwen2.5:14b", temperature: 0.2 });
30
+ ollama.model({ name: "llama3.2-vision", maxTokens: 1024 });
31
31
  ```
32
32
 
33
33
  ## Capabilities — what's auto-set
34
34
 
35
- | Flag | Default |
36
- | --- | --- |
37
- | `structuredOutput` | `true` (via Ollama's native `format` JSON-schema field) |
38
- | `vision` | Inferred from model tag substring. `true` for `llava`, `bakllava`, `*-vision`, `moondream`, `minicpm-v`, `qwen2-vl`, `qwen2.5-vl`, `llama4`, `gemma3`; `false` otherwise. |
39
- | `reasoning` | Inferred from model tag substring. `true` for `deepseek-r1`, `qwq`, `qwen3`, `magistral`, `phi4-reasoning`, `phi4-mini-reasoning`, `cogito`, `smallthinker`, `exaone-deep`, `gpt-oss`; `false` otherwise. |
40
- | `promptCaching` | `false` — Ollama has no provider-side prompt cache. |
41
- | `audio` / `pdf` | `false` — the Ollama chat API takes no audio / PDF content parts. |
35
+ | Flag | Default |
36
+ | ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
37
+ | `structuredOutput` | `true` (via Ollama's native `format` JSON-schema field) |
38
+ | `vision` | Inferred from model tag substring. `true` for `llava`, `bakllava`, `*-vision`, `moondream`, `minicpm-v`, `qwen2-vl`, `qwen2.5-vl`, `llama4`, `gemma3`; `false` otherwise. |
39
+ | `reasoning` | Inferred from model tag substring. `true` for `deepseek-r1`, `qwq`, `qwen3`, `magistral`, `phi4-reasoning`, `phi4-mini-reasoning`, `cogito`, `smallthinker`, `exaone-deep`, `gpt-oss`; `false` otherwise. |
40
+ | `promptCaching` | `false` — Ollama has no provider-side prompt cache. |
41
+ | `audio` / `pdf` | `false` — the Ollama chat API takes no audio / PDF content parts. |
42
42
 
43
43
  Explicit config always wins (`vision`, `structuredOutput`, `reasoning` overrides on `ollama.model({ … })`).
44
44
 
@@ -47,9 +47,9 @@ Explicit config always wins (`vision`, `structuredOutput`, `reasoning` overrides
47
47
  For thinking-capable families the adapter maps `ModelCallOptions.reasoning` onto Ollama's `think` request flag (`boolean | 'low' | 'medium' | 'high'`):
48
48
 
49
49
  ```ts
50
- ollama.model({ name: "deepseek-r1:7b" })
51
- await model.complete(messages, { reasoning: { effort: "high" } }); // → chat({ think: "high" })
52
- await model.complete(messages, { reasoning: { maxTokens: 2048 } }); // → chat({ think: true })
50
+ ollama.model({ name: "deepseek-r1:7b" });
51
+ await model.complete(messages, { reasoning: { effort: "high" } }); // → chat({ think: "high" })
52
+ await model.complete(messages, { reasoning: { maxTokens: 2048 } }); // → chat({ think: true })
53
53
  ```
54
54
 
55
55
  - `reasoning.effort` (`low` / `medium` / `high`) passes straight through to `think`.
@@ -83,7 +83,17 @@ A multipart user message collapses to a single `content` string + an `images` ar
83
83
 
84
84
  `model.stream()` drains `chat({ stream: true })` (an `AbortableAsyncIterator`). Each chunk's `message.content` → `{ type: "delta" }`; `message.tool_calls` are emitted as `{ type: "tool-call" }` **fully formed**. Terminal `{ type: "done", finishReason, usage }` — usage from the final (`done: true`) chunk's `prompt_eval_count` / `eval_count`.
85
85
 
86
- **`options.signal` is honored** by calling the iterator's `abort()` (stream path; non-stream `complete()` ignores it — the agent still honors the signal at trip boundaries).
86
+ Opt-in (`options.streamChunks`, set by `agent.stream()`): `{ type: "reasoning", delta }` per `message.thinking` piece when `think` is on:
87
+
88
+ ```ts
89
+ for await (const chunk of model.stream(messages, { reasoning: { effort: "high" }, streamChunks: { reasoning: true } })) {
90
+ if (chunk.type === "reasoning") process.stderr.write(chunk.delta);
91
+ }
92
+ ```
93
+
94
+ Ollama's chat API has no built-in tools: an `ai.providerTool()` for another provider is left out with a warning.
95
+
96
+ **`options.signal` is honored** on `stream()` and `complete()`: a pre-aborted signal rejects without sending, and aborting mid-call cancels the HTTP request. The `ollama` client has no per-request signal, so `OllamaSDK` wraps its `fetch` (yours, when you pass `fetch`) to apply each call's signal from async context; the stream iterator's `abort()` is called too.
87
97
 
88
98
  ## Finish-reason mapping
89
99
 
@@ -94,7 +104,7 @@ A multipart user message collapses to a single `content` string + an `images` ar
94
104
  ```ts
95
105
  const embedder = ollama.embedder({ name: "nomic-embed-text" });
96
106
  const { vector } = await embedder.embed("Hello world");
97
- const { vectors } = await embedder.embedMany(["a", "b"]); // single batched call
107
+ const { vectors } = await embedder.embedMany(["a", "b"]); // single batched call
98
108
  const truncated = ollama.embedder({ name: "mxbai-embed-large", dimensions: 512 });
99
109
  ```
100
110
 
@@ -116,14 +126,52 @@ Wrapped into the typed `@warlock.js/ai` `AIError` hierarchy. The `ollama` client
116
126
  ## Token counting & usage (cost-truth)
117
127
 
118
128
  ```ts
119
- await ollama.count("some text") // approximate heuristic, offline
129
+ await ollama.count("some text"); // approximate heuristic, offline
120
130
  ```
121
131
 
122
132
  Per-call `Usage` comes from the chat response's `prompt_eval_count` (→ `input`) and `eval_count` (→ `output`); `total` is their sum. The adapter reports usage **truthfully** — it never fabricates a field Ollama doesn't return:
123
133
 
124
134
  - `cachedTokens` / `cacheWriteTokens` — **always undefined**. Ollama has no provider prompt cache, so there is no cache-hit or cache-write accounting (honest absence, not a false `0`).
125
- - `reasoningTokens` — **always undefined**. Reasoning models emit their thinking as the `message.thinking` *string*, but the wire format carries **no separate reasoning-token count** — those tokens are already folded into `eval_count`. The adapter does not estimate a count from the thinking text.
126
- - `cost` — populated only when a `ModelPricing` is configured (local Ollama is free, so usually undefined).
135
+ - `reasoningTokens` — **always undefined**. Reasoning models emit their thinking as the `message.thinking` _string_, but the wire format carries **no separate reasoning-token count** — those tokens are already folded into `eval_count`. The adapter does not estimate a count from the thinking text.
136
+ - `cost` — attached to each `complete()` / streaming `done` usage only when a `ModelPricing` is configured. The adapter ships no price table (a local model costs nothing per token), so it is usually undefined; set `pricing` for chargeback or a hosted Ollama endpoint.
137
+ - `inputIncludesCache` — left unset: with no cache counters there is nothing to include or exclude.
138
+
139
+ ```ts
140
+ import { totalCost } from "@warlock.js/ai";
141
+ import { OllamaSDK } from "@warlock.js/ai-ollama";
142
+
143
+ const ollama = new OllamaSDK({ pricing: { "llama3.2": { input: 0.05, output: 0.1 } } }); // internal chargeback rate
144
+ const { usage } = await ollama
145
+ .model({ name: "llama3.2" })
146
+ .complete([{ role: "user", content: "hi" }]);
147
+ console.log(totalCost(usage.cost));
148
+ ```
149
+
150
+ ## Provider body options
151
+
152
+ Native body fields merge after neutral options. When both values are plain
153
+ objects, their immediate keys merge and caller-provided keys win; untouched
154
+ neutral fields remain. Arrays, scalars and `null` replace the field. This merge
155
+ is one level deep: a nested object explicitly supplied by the caller replaces
156
+ that nested value.
157
+
158
+ `providerOptions.ollama.body` adds native chat request fields after neutral
159
+ options, for completion and streaming:
160
+
161
+ ```ts
162
+ await model.complete(messages, {
163
+ providerOptions: { ollama: { body: { keep_alive: "10m", options: { top_k: 4 } } } },
164
+ });
165
+ ```
166
+
167
+ The key stays `ollama` for custom provider labels. Other provider keys are
168
+ ignored. `OllamaCallOptions` is exported. `model`, `messages`, `tools` and `stream`
169
+ are protected; replacing them throws `InvalidRequestError`. Fields merge
170
+ shallowly: a provider `options` object replaces the generated inference options,
171
+ so include any neutral sampling settings you want to retain in that object.
172
+
173
+ Ollama's chat transport has no native preflight token-count endpoint, so its
174
+ model has no `countTokens` method. `sdk.count(text)` remains an offline estimate.
127
175
 
128
176
  ## When NOT to use this skill
129
177