ai-sdk-ollama 3.7.0 → 3.8.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 +15 -0
- package/dist/index.browser.cjs +188 -52
- package/dist/index.browser.cjs.map +1 -1
- package/dist/index.browser.d.cts +31 -25
- package/dist/index.browser.d.ts +31 -25
- package/dist/index.browser.js +189 -52
- package/dist/index.browser.js.map +1 -1
- package/dist/index.cjs +4578 -4422
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +65 -27
- package/dist/index.d.ts +65 -27
- package/dist/index.js +5144 -4994
- package/dist/index.js.map +1 -1
- package/package.json +8 -8
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 3.8.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- ed617f1: - **Fix:** Make `OllamaProviderOptions`, `OllamaChatProviderOptions`, and `OllamaEmbeddingProviderOptions` compatible with the AI SDK's `providerOptions` (`Record<string, JSONObject>`). They are now defined as type aliases from Zod schemas (`z.infer`), so passing e.g. `providerOptions: { ollama: { structuredOutputs: true } }` into `streamText`, `generateText`, or `ToolLoopAgent` type-checks correctly (fixes #548).
|
|
8
|
+
- **New:** Export provider option Zod schemas: `ollamaProviderOptionsSchema`, `ollamaChatProviderOptionsSchema`, `ollamaEmbeddingProviderOptionsSchema`, and `ollamaRerankingProviderOptionsSchema` for validation or parsing.
|
|
9
|
+
- **New:** Image generation support via `ollama.imageModel(modelId)` and `OllamaImageModel`, using the AI SDK's `generateImage()` and experimental Ollama image models (e.g. `x/z-image-turbo`, `x/flux2-klein`). Supports `providerOptions.ollama` (e.g. `steps`, `negative_prompt`).
|
|
10
|
+
- **Change:** Reranking model now uses `parseProviderOptions` from `@ai-sdk/provider-utils` for `providerOptions.ollama`; `OllamaRerankingProviderOptions` is now a type inferred from the exported schema.
|
|
11
|
+
|
|
12
|
+
## 3.7.1
|
|
13
|
+
|
|
14
|
+
### Patch Changes
|
|
15
|
+
|
|
16
|
+
- 70def97: Support media/image content in tool result messages. When a tool returns `output.type === 'content'` with `image-data`, `image-url`, or `file-data` (image) parts, the provider now sends them in the tool message's `images` array to Ollama. Fixes #527.
|
|
17
|
+
|
|
3
18
|
## 3.7.0
|
|
4
19
|
|
|
5
20
|
### Minor Changes
|
package/dist/index.browser.cjs
CHANGED
|
@@ -35,7 +35,6 @@ __export(index_browser_exports, {
|
|
|
35
35
|
module.exports = __toCommonJS(index_browser_exports);
|
|
36
36
|
|
|
37
37
|
// src/provider.browser.ts
|
|
38
|
-
var import_provider = require("@ai-sdk/provider");
|
|
39
38
|
var import_browser = require("ollama/browser");
|
|
40
39
|
|
|
41
40
|
// src/utils/tool-calling-reliability.ts
|
|
@@ -375,6 +374,29 @@ ${finalInstruction}`;
|
|
|
375
374
|
}
|
|
376
375
|
|
|
377
376
|
// src/utils/convert-to-ollama-messages.ts
|
|
377
|
+
function normalizeImageDataForOllama(imageData) {
|
|
378
|
+
if (imageData instanceof URL) {
|
|
379
|
+
if (imageData.protocol === "data:") {
|
|
380
|
+
const base64Match = imageData.href.match(/data:[^;]+;base64,(.+)/);
|
|
381
|
+
const extracted = base64Match?.[1];
|
|
382
|
+
if (typeof extracted === "string") return extracted;
|
|
383
|
+
return imageData.href;
|
|
384
|
+
}
|
|
385
|
+
return imageData.href;
|
|
386
|
+
}
|
|
387
|
+
if (typeof imageData === "string") {
|
|
388
|
+
if (imageData.startsWith("data:")) {
|
|
389
|
+
const base64Match = imageData.match(/data:[^;]+;base64,(.+)/);
|
|
390
|
+
const extracted = base64Match?.[1];
|
|
391
|
+
if (typeof extracted === "string") return extracted;
|
|
392
|
+
}
|
|
393
|
+
return imageData;
|
|
394
|
+
}
|
|
395
|
+
if (imageData instanceof Uint8Array) {
|
|
396
|
+
return Buffer.from(imageData).toString("base64");
|
|
397
|
+
}
|
|
398
|
+
return null;
|
|
399
|
+
}
|
|
378
400
|
function convertToOllamaChatMessages(prompt) {
|
|
379
401
|
const messages = [];
|
|
380
402
|
for (const message of prompt) {
|
|
@@ -396,35 +418,7 @@ function convertToOllamaChatMessages(prompt) {
|
|
|
396
418
|
const textParts = message.content.filter((part) => part.type === "text").map((part) => part.text).join("\n");
|
|
397
419
|
const imageParts = message.content.filter(
|
|
398
420
|
(part) => part.type === "file"
|
|
399
|
-
).filter((part) =>
|
|
400
|
-
return part.mediaType?.startsWith("image/") || false;
|
|
401
|
-
}).map((part) => {
|
|
402
|
-
const imageData = part.data;
|
|
403
|
-
if (imageData instanceof URL) {
|
|
404
|
-
if (imageData.protocol === "data:") {
|
|
405
|
-
const base64Match = imageData.href.match(
|
|
406
|
-
/data:[^;]+;base64,(.+)/
|
|
407
|
-
);
|
|
408
|
-
if (base64Match) {
|
|
409
|
-
return base64Match[1];
|
|
410
|
-
}
|
|
411
|
-
return imageData.href;
|
|
412
|
-
}
|
|
413
|
-
return imageData.href;
|
|
414
|
-
} else if (typeof imageData === "string") {
|
|
415
|
-
if (imageData.startsWith("data:")) {
|
|
416
|
-
const base64Match = imageData.match(/data:[^;]+;base64,(.+)/);
|
|
417
|
-
if (base64Match) {
|
|
418
|
-
return base64Match[1];
|
|
419
|
-
}
|
|
420
|
-
}
|
|
421
|
-
return imageData;
|
|
422
|
-
} else if (imageData instanceof Uint8Array) {
|
|
423
|
-
return Buffer.from(imageData).toString("base64");
|
|
424
|
-
} else {
|
|
425
|
-
return null;
|
|
426
|
-
}
|
|
427
|
-
}).filter((img) => img !== null);
|
|
421
|
+
).filter((part) => part.mediaType?.startsWith("image/") ?? false).map((part) => normalizeImageDataForOllama(part.data)).filter((img) => img !== null);
|
|
428
422
|
messages.push({
|
|
429
423
|
role: "user",
|
|
430
424
|
content: textParts || "",
|
|
@@ -473,14 +467,48 @@ function convertToOllamaChatMessages(prompt) {
|
|
|
473
467
|
});
|
|
474
468
|
} else {
|
|
475
469
|
for (const part of message.content) {
|
|
476
|
-
if (part.type
|
|
477
|
-
|
|
470
|
+
if (part.type !== "tool-result") continue;
|
|
471
|
+
if (part.output.type === "content") {
|
|
472
|
+
const textParts = [];
|
|
473
|
+
const imageParts = [];
|
|
474
|
+
for (const item of part.output.value) {
|
|
475
|
+
switch (item.type) {
|
|
476
|
+
case "text": {
|
|
477
|
+
textParts.push(item.text);
|
|
478
|
+
break;
|
|
479
|
+
}
|
|
480
|
+
case "image-data": {
|
|
481
|
+
const normalized = normalizeImageDataForOllama(item.data);
|
|
482
|
+
if (normalized) imageParts.push(normalized);
|
|
483
|
+
break;
|
|
484
|
+
}
|
|
485
|
+
case "image-url": {
|
|
486
|
+
imageParts.push(item.url);
|
|
487
|
+
break;
|
|
488
|
+
}
|
|
489
|
+
case "file-data": {
|
|
490
|
+
if (item.mediaType?.startsWith("image/")) {
|
|
491
|
+
const normalized = normalizeImageDataForOllama(item.data);
|
|
492
|
+
if (normalized) imageParts.push(normalized);
|
|
493
|
+
}
|
|
494
|
+
break;
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
}
|
|
478
498
|
messages.push({
|
|
479
499
|
role: "tool",
|
|
480
|
-
content:
|
|
481
|
-
tool_name: part.toolName
|
|
500
|
+
content: textParts.join("\n") || "",
|
|
501
|
+
tool_name: part.toolName,
|
|
502
|
+
images: imageParts.length > 0 ? imageParts : void 0
|
|
482
503
|
});
|
|
504
|
+
continue;
|
|
483
505
|
}
|
|
506
|
+
const contentValue = part.output.type === "text" || part.output.type === "error-text" ? part.output.value : part.output.type === "json" || part.output.type === "error-json" ? JSON.stringify(part.output.value) : part.output.type === "execution-denied" ? "" : JSON.stringify(part.output);
|
|
507
|
+
messages.push({
|
|
508
|
+
role: "tool",
|
|
509
|
+
content: contentValue,
|
|
510
|
+
tool_name: part.toolName
|
|
511
|
+
});
|
|
484
512
|
}
|
|
485
513
|
}
|
|
486
514
|
break;
|
|
@@ -16394,6 +16422,7 @@ var ollamaRerankingResponseSchema = external_exports.object({
|
|
|
16394
16422
|
)
|
|
16395
16423
|
});
|
|
16396
16424
|
var ollamaRerankingProviderOptionsSchema = external_exports.object({
|
|
16425
|
+
/** Custom instruction for this specific reranking call. Overrides the instruction set in model settings. */
|
|
16397
16426
|
instruction: external_exports.string().optional()
|
|
16398
16427
|
});
|
|
16399
16428
|
var ollamaErrorSchema = external_exports.object({
|
|
@@ -16426,15 +16455,11 @@ var OllamaRerankingModel = class {
|
|
|
16426
16455
|
providerOptions
|
|
16427
16456
|
}) {
|
|
16428
16457
|
const warnings = [];
|
|
16429
|
-
|
|
16430
|
-
|
|
16431
|
-
|
|
16432
|
-
|
|
16433
|
-
|
|
16434
|
-
if (parsed.success) {
|
|
16435
|
-
rerankingOptions = parsed.data;
|
|
16436
|
-
}
|
|
16437
|
-
}
|
|
16458
|
+
const rerankingOptions = await (0, import_provider_utils2.parseProviderOptions)({
|
|
16459
|
+
provider: "ollama",
|
|
16460
|
+
providerOptions,
|
|
16461
|
+
schema: ollamaRerankingProviderOptionsSchema
|
|
16462
|
+
});
|
|
16438
16463
|
let documentValues;
|
|
16439
16464
|
if (documents.type === "object") {
|
|
16440
16465
|
warnings.push({
|
|
@@ -16628,6 +16653,115 @@ var OllamaEmbeddingRerankingModel = class {
|
|
|
16628
16653
|
}
|
|
16629
16654
|
};
|
|
16630
16655
|
|
|
16656
|
+
// src/models/image-model.ts
|
|
16657
|
+
var import_provider_utils3 = require("@ai-sdk/provider-utils");
|
|
16658
|
+
var ollamaImageProviderOptionsSchema = external_exports.object({
|
|
16659
|
+
steps: external_exports.number().optional(),
|
|
16660
|
+
negative_prompt: external_exports.string().optional()
|
|
16661
|
+
});
|
|
16662
|
+
var OllamaImageModel = class {
|
|
16663
|
+
constructor(modelId, config2) {
|
|
16664
|
+
this.config = config2;
|
|
16665
|
+
__publicField(this, "specificationVersion", "v3");
|
|
16666
|
+
__publicField(this, "provider");
|
|
16667
|
+
__publicField(this, "modelId");
|
|
16668
|
+
__publicField(this, "maxImagesPerCall", 1);
|
|
16669
|
+
this.modelId = modelId;
|
|
16670
|
+
this.provider = config2.provider;
|
|
16671
|
+
}
|
|
16672
|
+
async doGenerate(options) {
|
|
16673
|
+
const {
|
|
16674
|
+
prompt,
|
|
16675
|
+
size,
|
|
16676
|
+
aspectRatio,
|
|
16677
|
+
seed,
|
|
16678
|
+
providerOptions,
|
|
16679
|
+
abortSignal,
|
|
16680
|
+
headers: optHeaders
|
|
16681
|
+
} = options;
|
|
16682
|
+
if (prompt == null || prompt === "") {
|
|
16683
|
+
throw new OllamaError({ message: "Image generation requires a prompt" });
|
|
16684
|
+
}
|
|
16685
|
+
const url2 = `${this.config.baseURL.replace(/\/$/, "")}/api/generate`;
|
|
16686
|
+
const body = {
|
|
16687
|
+
model: this.modelId,
|
|
16688
|
+
prompt,
|
|
16689
|
+
stream: false
|
|
16690
|
+
};
|
|
16691
|
+
if (size) {
|
|
16692
|
+
const [w, h] = size.split("x").map(Number);
|
|
16693
|
+
if (!Number.isNaN(w)) body.width = w;
|
|
16694
|
+
if (!Number.isNaN(h)) body.height = h;
|
|
16695
|
+
}
|
|
16696
|
+
if (aspectRatio && body.width == null && body.height == null) {
|
|
16697
|
+
const [ratioW, ratioH] = aspectRatio.split(":").map(Number);
|
|
16698
|
+
if (ratioW != null && ratioH != null && !Number.isNaN(ratioW) && !Number.isNaN(ratioH) && ratioW > 0 && ratioH > 0) {
|
|
16699
|
+
const target = 1024;
|
|
16700
|
+
body.width = Math.round(target * Math.sqrt(ratioW / ratioH));
|
|
16701
|
+
body.height = Math.round(target * Math.sqrt(ratioH / ratioW));
|
|
16702
|
+
}
|
|
16703
|
+
}
|
|
16704
|
+
if (seed != null) {
|
|
16705
|
+
const opts = typeof body.options === "object" && body.options ? body.options : {};
|
|
16706
|
+
body.options = { ...opts, seed };
|
|
16707
|
+
}
|
|
16708
|
+
const po = await (0, import_provider_utils3.parseProviderOptions)({
|
|
16709
|
+
provider: "ollama",
|
|
16710
|
+
providerOptions,
|
|
16711
|
+
schema: ollamaImageProviderOptionsSchema
|
|
16712
|
+
});
|
|
16713
|
+
if (po?.steps != null) body.steps = po.steps;
|
|
16714
|
+
if (po?.negative_prompt != null) body.negative_prompt = po.negative_prompt;
|
|
16715
|
+
const headers = {
|
|
16716
|
+
"Content-Type": "application/json",
|
|
16717
|
+
...this.config.headers(),
|
|
16718
|
+
...optHeaders
|
|
16719
|
+
};
|
|
16720
|
+
const res = await (this.config.fetch ?? fetch)(url2, {
|
|
16721
|
+
method: "POST",
|
|
16722
|
+
headers,
|
|
16723
|
+
body: JSON.stringify(body),
|
|
16724
|
+
signal: abortSignal
|
|
16725
|
+
});
|
|
16726
|
+
if (!res.ok) {
|
|
16727
|
+
const errBody = await res.json().catch(() => ({}));
|
|
16728
|
+
throw new OllamaError({
|
|
16729
|
+
message: errBody.error ?? `Ollama API error: ${res.status} ${res.statusText}`
|
|
16730
|
+
});
|
|
16731
|
+
}
|
|
16732
|
+
const data = await res.json();
|
|
16733
|
+
const images = [];
|
|
16734
|
+
if (Array.isArray(data.images) && data.images.length > 0) {
|
|
16735
|
+
images.push(...data.images);
|
|
16736
|
+
} else if (typeof data.image === "string" && data.image.trim().length > 0) {
|
|
16737
|
+
images.push(data.image.trim());
|
|
16738
|
+
} else if (typeof data.response === "string" && data.response.trim().length > 0) {
|
|
16739
|
+
const trimmed = data.response.trim();
|
|
16740
|
+
if (/^[A-Za-z0-9+/=]+$/.test(trimmed) && trimmed.length > 100) {
|
|
16741
|
+
images.push(trimmed);
|
|
16742
|
+
}
|
|
16743
|
+
}
|
|
16744
|
+
const responseHeaders = {};
|
|
16745
|
+
for (const [key, value] of res.headers.entries()) {
|
|
16746
|
+
responseHeaders[key] = value;
|
|
16747
|
+
}
|
|
16748
|
+
return {
|
|
16749
|
+
images,
|
|
16750
|
+
warnings: [],
|
|
16751
|
+
response: {
|
|
16752
|
+
timestamp: /* @__PURE__ */ new Date(),
|
|
16753
|
+
modelId: data.model ?? this.modelId,
|
|
16754
|
+
headers: Object.keys(responseHeaders).length > 0 ? responseHeaders : void 0
|
|
16755
|
+
},
|
|
16756
|
+
usage: data.eval_count == null ? void 0 : {
|
|
16757
|
+
inputTokens: void 0,
|
|
16758
|
+
outputTokens: data.eval_count,
|
|
16759
|
+
totalTokens: data.eval_count
|
|
16760
|
+
}
|
|
16761
|
+
};
|
|
16762
|
+
}
|
|
16763
|
+
};
|
|
16764
|
+
|
|
16631
16765
|
// src/tool/web-search.ts
|
|
16632
16766
|
var import_ai = require("ai");
|
|
16633
16767
|
var webSearchInputSchema = external_exports.object({
|
|
@@ -16880,10 +17014,10 @@ function createOllama(options = {}) {
|
|
|
16880
17014
|
});
|
|
16881
17015
|
};
|
|
16882
17016
|
const createRerankingModel = (modelId, settings = {}) => {
|
|
16883
|
-
const
|
|
17017
|
+
const baseURL2 = options.baseURL ?? "http://127.0.0.1:11434";
|
|
16884
17018
|
return new OllamaRerankingModel(modelId, settings, {
|
|
16885
17019
|
provider: "ollama.reranking",
|
|
16886
|
-
baseURL,
|
|
17020
|
+
baseURL: baseURL2,
|
|
16887
17021
|
headers: () => normalizedHeaders,
|
|
16888
17022
|
fetch: options.fetch
|
|
16889
17023
|
});
|
|
@@ -16910,13 +17044,15 @@ function createOllama(options = {}) {
|
|
|
16910
17044
|
provider.reranking = createRerankingModel;
|
|
16911
17045
|
provider.rerankingModel = createRerankingModel;
|
|
16912
17046
|
provider.embeddingReranking = createEmbeddingRerankingModel;
|
|
16913
|
-
|
|
16914
|
-
|
|
16915
|
-
|
|
16916
|
-
|
|
16917
|
-
|
|
16918
|
-
|
|
16919
|
-
|
|
17047
|
+
const baseURL = options.baseURL ?? "http://127.0.0.1:11434";
|
|
17048
|
+
const createImageModel = (modelId) => new OllamaImageModel(modelId, {
|
|
17049
|
+
provider: "ollama",
|
|
17050
|
+
modelId,
|
|
17051
|
+
baseURL,
|
|
17052
|
+
headers: () => normalizedHeaders,
|
|
17053
|
+
fetch: options.fetch
|
|
17054
|
+
});
|
|
17055
|
+
provider.imageModel = createImageModel;
|
|
16920
17056
|
const toolsWithClient = {
|
|
16921
17057
|
webSearch: (options2 = {}) => ollamaTools.webSearch({ ...options2, client }),
|
|
16922
17058
|
webFetch: (options2 = {}) => ollamaTools.webFetch({ ...options2, client })
|