ai-sdk-ollama 3.7.1 → 3.8.1
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 +181 -47
- package/dist/index.browser.cjs.map +1 -1
- package/dist/index.browser.d.cts +34 -28
- package/dist/index.browser.d.ts +34 -28
- package/dist/index.browser.js +182 -47
- package/dist/index.browser.js.map +1 -1
- package/dist/index.cjs +4601 -4447
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +68 -30
- package/dist/index.d.ts +68 -30
- package/dist/index.js +4789 -4641
- package/dist/index.js.map +1 -1
- package/package.json +9 -10
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 3.8.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- aeafdd6: - Update `generateText` to detect and use the stable `output` option instead of the deprecated `experimental_output` when `enableToolsWithStructuredOutput` is enabled, including a two-phase tools + structured output path and an integration test to guard this behavior.
|
|
8
|
+
|
|
9
|
+
## 3.8.0
|
|
10
|
+
|
|
11
|
+
### Minor Changes
|
|
12
|
+
|
|
13
|
+
- 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).
|
|
14
|
+
- **New:** Export provider option Zod schemas: `ollamaProviderOptionsSchema`, `ollamaChatProviderOptionsSchema`, `ollamaEmbeddingProviderOptionsSchema`, and `ollamaRerankingProviderOptionsSchema` for validation or parsing.
|
|
15
|
+
- **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`).
|
|
16
|
+
- **Change:** Reranking model now uses `parseProviderOptions` from `@ai-sdk/provider-utils` for `providerOptions.ollama`; `OllamaRerankingProviderOptions` is now a type inferred from the exported schema.
|
|
17
|
+
|
|
3
18
|
## 3.7.1
|
|
4
19
|
|
|
5
20
|
### Patch 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
|
|
@@ -16423,6 +16422,7 @@ var ollamaRerankingResponseSchema = external_exports.object({
|
|
|
16423
16422
|
)
|
|
16424
16423
|
});
|
|
16425
16424
|
var ollamaRerankingProviderOptionsSchema = external_exports.object({
|
|
16425
|
+
/** Custom instruction for this specific reranking call. Overrides the instruction set in model settings. */
|
|
16426
16426
|
instruction: external_exports.string().optional()
|
|
16427
16427
|
});
|
|
16428
16428
|
var ollamaErrorSchema = external_exports.object({
|
|
@@ -16455,15 +16455,11 @@ var OllamaRerankingModel = class {
|
|
|
16455
16455
|
providerOptions
|
|
16456
16456
|
}) {
|
|
16457
16457
|
const warnings = [];
|
|
16458
|
-
|
|
16459
|
-
|
|
16460
|
-
|
|
16461
|
-
|
|
16462
|
-
|
|
16463
|
-
if (parsed.success) {
|
|
16464
|
-
rerankingOptions = parsed.data;
|
|
16465
|
-
}
|
|
16466
|
-
}
|
|
16458
|
+
const rerankingOptions = await (0, import_provider_utils2.parseProviderOptions)({
|
|
16459
|
+
provider: "ollama",
|
|
16460
|
+
providerOptions,
|
|
16461
|
+
schema: ollamaRerankingProviderOptionsSchema
|
|
16462
|
+
});
|
|
16467
16463
|
let documentValues;
|
|
16468
16464
|
if (documents.type === "object") {
|
|
16469
16465
|
warnings.push({
|
|
@@ -16657,6 +16653,115 @@ var OllamaEmbeddingRerankingModel = class {
|
|
|
16657
16653
|
}
|
|
16658
16654
|
};
|
|
16659
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
|
+
|
|
16660
16765
|
// src/tool/web-search.ts
|
|
16661
16766
|
var import_ai = require("ai");
|
|
16662
16767
|
var webSearchInputSchema = external_exports.object({
|
|
@@ -16909,10 +17014,10 @@ function createOllama(options = {}) {
|
|
|
16909
17014
|
});
|
|
16910
17015
|
};
|
|
16911
17016
|
const createRerankingModel = (modelId, settings = {}) => {
|
|
16912
|
-
const
|
|
17017
|
+
const baseURL2 = options.baseURL ?? "http://127.0.0.1:11434";
|
|
16913
17018
|
return new OllamaRerankingModel(modelId, settings, {
|
|
16914
17019
|
provider: "ollama.reranking",
|
|
16915
|
-
baseURL,
|
|
17020
|
+
baseURL: baseURL2,
|
|
16916
17021
|
headers: () => normalizedHeaders,
|
|
16917
17022
|
fetch: options.fetch
|
|
16918
17023
|
});
|
|
@@ -16939,13 +17044,15 @@ function createOllama(options = {}) {
|
|
|
16939
17044
|
provider.reranking = createRerankingModel;
|
|
16940
17045
|
provider.rerankingModel = createRerankingModel;
|
|
16941
17046
|
provider.embeddingReranking = createEmbeddingRerankingModel;
|
|
16942
|
-
|
|
16943
|
-
|
|
16944
|
-
|
|
16945
|
-
|
|
16946
|
-
|
|
16947
|
-
|
|
16948
|
-
|
|
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;
|
|
16949
17056
|
const toolsWithClient = {
|
|
16950
17057
|
webSearch: (options2 = {}) => ollamaTools.webSearch({ ...options2, client }),
|
|
16951
17058
|
webFetch: (options2 = {}) => ollamaTools.webFetch({ ...options2, client })
|
|
@@ -16959,6 +17066,17 @@ var ollama = createOllama();
|
|
|
16959
17066
|
|
|
16960
17067
|
// src/functions/generate-text.ts
|
|
16961
17068
|
var import_ai3 = require("ai");
|
|
17069
|
+
function resultWithOverrides(base, overrides) {
|
|
17070
|
+
const descriptors = {};
|
|
17071
|
+
for (const key of Object.keys(overrides)) {
|
|
17072
|
+
descriptors[key] = {
|
|
17073
|
+
value: overrides[key],
|
|
17074
|
+
enumerable: true,
|
|
17075
|
+
configurable: true
|
|
17076
|
+
};
|
|
17077
|
+
}
|
|
17078
|
+
return Object.create(base, descriptors);
|
|
17079
|
+
}
|
|
16962
17080
|
async function generateText(options) {
|
|
16963
17081
|
const { enhancedOptions = {}, ...generateTextOptions } = options;
|
|
16964
17082
|
const {
|
|
@@ -16969,33 +17087,42 @@ async function generateText(options) {
|
|
|
16969
17087
|
enableToolsWithStructuredOutput = false
|
|
16970
17088
|
} = enhancedOptions;
|
|
16971
17089
|
const hasTools = generateTextOptions.tools && Object.keys(generateTextOptions.tools).length > 0;
|
|
16972
|
-
const
|
|
17090
|
+
const hasOutput = "output" in generateTextOptions;
|
|
16973
17091
|
const requiresTools = generateTextOptions.toolChoice === "required" || typeof generateTextOptions.toolChoice === "object" && "type" in generateTextOptions.toolChoice && generateTextOptions.toolChoice.type === "tool";
|
|
16974
|
-
if (enableToolsWithStructuredOutput &&
|
|
16975
|
-
const
|
|
16976
|
-
|
|
16977
|
-
|
|
16978
|
-
|
|
17092
|
+
if (enableToolsWithStructuredOutput && hasOutput && requiresTools && hasTools) {
|
|
17093
|
+
const phase1Options = Object.fromEntries(
|
|
17094
|
+
Object.entries(generateTextOptions).filter(([key]) => key !== "output")
|
|
17095
|
+
);
|
|
17096
|
+
const toolResult = await (0, import_ai3.generateText)(
|
|
17097
|
+
phase1Options
|
|
17098
|
+
);
|
|
16979
17099
|
if (toolResult.toolCalls && toolResult.toolCalls.length > 0) {
|
|
16980
17100
|
const toolContext = toolResult.toolResults?.map(
|
|
16981
|
-
(tr, i) => `${toolResult.toolCalls?.[i]?.toolName}: ${JSON.stringify(tr.output
|
|
17101
|
+
(tr, i) => `${toolResult.toolCalls?.[i]?.toolName}: ${JSON.stringify(tr.output ?? tr)}`
|
|
16982
17102
|
).join("\n");
|
|
16983
|
-
const
|
|
17103
|
+
const toolResultsSuffix = `
|
|
16984
17104
|
|
|
16985
17105
|
Tool Results:
|
|
16986
17106
|
${toolContext}
|
|
16987
17107
|
|
|
16988
|
-
Please provide a structured response based on these tool results
|
|
16989
|
-
const
|
|
16990
|
-
|
|
16991
|
-
|
|
16992
|
-
|
|
16993
|
-
|
|
16994
|
-
}
|
|
17108
|
+
Please provide a structured response based on these tool results.`;
|
|
17109
|
+
const phase2Base = Object.fromEntries(
|
|
17110
|
+
Object.entries(generateTextOptions).filter(
|
|
17111
|
+
([key]) => key !== "tools" && key !== "toolChoice"
|
|
17112
|
+
)
|
|
17113
|
+
);
|
|
17114
|
+
const phase2OptionsWithPrompt = typeof phase2Base.prompt === "string" ? { ...phase2Base, prompt: phase2Base.prompt + toolResultsSuffix } : phase2Base.messages ? {
|
|
17115
|
+
...phase2Base,
|
|
17116
|
+
prompt: void 0,
|
|
17117
|
+
messages: [
|
|
17118
|
+
...phase2Base.messages,
|
|
17119
|
+
{ role: "user", content: toolResultsSuffix.trim() }
|
|
17120
|
+
]
|
|
17121
|
+
} : phase2Base;
|
|
16995
17122
|
const structuredResult = await (0, import_ai3.generateText)(
|
|
16996
|
-
|
|
17123
|
+
phase2OptionsWithPrompt
|
|
16997
17124
|
);
|
|
16998
|
-
const enhancedResult =
|
|
17125
|
+
const enhancedResult = resultWithOverrides(structuredResult, {
|
|
16999
17126
|
toolCalls: toolResult.toolCalls,
|
|
17000
17127
|
toolResults: toolResult.toolResults,
|
|
17001
17128
|
staticToolCalls: toolResult.staticToolCalls,
|
|
@@ -17003,9 +17130,9 @@ Please provide a structured response based on these tool results.` : generateTex
|
|
|
17003
17130
|
staticToolResults: toolResult.staticToolResults,
|
|
17004
17131
|
dynamicToolResults: toolResult.dynamicToolResults,
|
|
17005
17132
|
usage: {
|
|
17006
|
-
inputTokens: (toolResult.usage.inputTokens
|
|
17007
|
-
outputTokens: (toolResult.usage.outputTokens
|
|
17008
|
-
totalTokens: (toolResult.usage.totalTokens
|
|
17133
|
+
inputTokens: (toolResult.usage.inputTokens ?? 0) + (structuredResult.usage.inputTokens ?? 0),
|
|
17134
|
+
outputTokens: (toolResult.usage.outputTokens ?? 0) + (structuredResult.usage.outputTokens ?? 0),
|
|
17135
|
+
totalTokens: (toolResult.usage.totalTokens ?? 0) + (structuredResult.usage.totalTokens ?? 0)
|
|
17009
17136
|
}
|
|
17010
17137
|
});
|
|
17011
17138
|
return enhancedResult;
|
|
@@ -17016,7 +17143,9 @@ Please provide a structured response based on these tool results.` : generateTex
|
|
|
17016
17143
|
// Only set stopWhen default if user didn't provide one and tools are enabled
|
|
17017
17144
|
stopWhen: generateTextOptions.stopWhen ?? (hasTools ? (0, import_ai3.stepCountIs)(5) : void 0)
|
|
17018
17145
|
});
|
|
17019
|
-
const toolsWereCalled = result.steps?.some(
|
|
17146
|
+
const toolsWereCalled = result.steps?.some(
|
|
17147
|
+
(step) => step.toolCalls && step.toolCalls.length > 0
|
|
17148
|
+
) ?? false;
|
|
17020
17149
|
const hasMinimalText = !result.text || result.text.trim().length < minResponseLength;
|
|
17021
17150
|
if (!hasTools || !toolsWereCalled || !hasMinimalText || !enableSynthesis) {
|
|
17022
17151
|
return result;
|
|
@@ -17048,11 +17177,16 @@ Tool results:
|
|
|
17048
17177
|
${toolContext}
|
|
17049
17178
|
|
|
17050
17179
|
${synthesisPrompt}`;
|
|
17051
|
-
const
|
|
17052
|
-
const
|
|
17180
|
+
const generateOptions = generateTextOptions;
|
|
17181
|
+
const baseOptions = Object.fromEntries(
|
|
17182
|
+
Object.entries(generateOptions).filter(
|
|
17183
|
+
([key]) => key !== "tools" && key !== "prompt" && key !== "messages"
|
|
17184
|
+
)
|
|
17185
|
+
);
|
|
17186
|
+
const synthesisOptions = generateOptions.messages ? {
|
|
17053
17187
|
...baseOptions,
|
|
17054
17188
|
messages: [
|
|
17055
|
-
...messages || [],
|
|
17189
|
+
...generateOptions.messages || [],
|
|
17056
17190
|
{ role: "user", content: fullSynthesisPrompt }
|
|
17057
17191
|
]
|
|
17058
17192
|
} : {
|
|
@@ -17063,12 +17197,12 @@ ${synthesisPrompt}`;
|
|
|
17063
17197
|
synthesisOptions
|
|
17064
17198
|
);
|
|
17065
17199
|
if (synthesisResult.text && synthesisResult.text.trim().length >= minResponseLength) {
|
|
17066
|
-
const enhancedResult =
|
|
17200
|
+
const enhancedResult = resultWithOverrides(result, {
|
|
17067
17201
|
text: synthesisResult.text,
|
|
17068
17202
|
usage: {
|
|
17069
|
-
inputTokens: (result.usage.inputTokens
|
|
17070
|
-
outputTokens: (result.usage.outputTokens
|
|
17071
|
-
totalTokens: (result.usage.totalTokens
|
|
17203
|
+
inputTokens: (result.usage.inputTokens ?? 0) + (synthesisResult.usage.inputTokens ?? 0),
|
|
17204
|
+
outputTokens: (result.usage.outputTokens ?? 0) + (synthesisResult.usage.outputTokens ?? 0),
|
|
17205
|
+
totalTokens: (result.usage.totalTokens ?? 0) + (synthesisResult.usage.totalTokens ?? 0)
|
|
17072
17206
|
}
|
|
17073
17207
|
});
|
|
17074
17208
|
return enhancedResult;
|