@elizaos/plugin-ollama 2.0.3-beta.2 → 2.0.3-beta.4
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/dist/browser/index.browser.js +1080 -0
- package/dist/browser/index.browser.js.map +17 -0
- package/dist/browser/index.d.ts +2 -0
- package/dist/cjs/index.d.ts +2 -0
- package/dist/cjs/index.node.cjs +1108 -0
- package/dist/cjs/index.node.cjs.map +17 -0
- package/dist/node/auto-enable.d.ts +4 -0
- package/dist/node/auto-enable.d.ts.map +1 -0
- package/dist/node/build.d.ts +2 -0
- package/dist/node/build.d.ts.map +1 -0
- package/dist/node/generated/specs/specs.d.ts +38 -0
- package/dist/node/generated/specs/specs.d.ts.map +1 -0
- package/dist/node/index.browser.d.ts +7 -0
- package/dist/node/index.browser.d.ts.map +1 -0
- package/dist/node/index.d.ts +2 -0
- package/dist/node/index.d.ts.map +1 -0
- package/dist/node/index.node.d.ts +7 -0
- package/dist/node/index.node.d.ts.map +1 -0
- package/dist/node/index.node.js +1080 -0
- package/dist/node/index.node.js.map +17 -0
- package/dist/node/models/availability.d.ts +2 -0
- package/dist/node/models/availability.d.ts.map +1 -0
- package/dist/node/models/embedding.d.ts +10 -0
- package/dist/node/models/embedding.d.ts.map +1 -0
- package/dist/node/models/index.d.ts +4 -0
- package/dist/node/models/index.d.ts.map +1 -0
- package/dist/node/models/text.d.ts +83 -0
- package/dist/node/models/text.d.ts.map +1 -0
- package/dist/node/plugin.d.ts +37 -0
- package/dist/node/plugin.d.ts.map +1 -0
- package/dist/node/types/index.d.ts +47 -0
- package/dist/node/types/index.d.ts.map +1 -0
- package/dist/node/utils/ai-sdk-wire.d.ts +42 -0
- package/dist/node/utils/ai-sdk-wire.d.ts.map +1 -0
- package/dist/node/utils/config.d.ts +40 -0
- package/dist/node/utils/config.d.ts.map +1 -0
- package/dist/node/utils/index.d.ts +4 -0
- package/dist/node/utils/index.d.ts.map +1 -0
- package/dist/node/utils/modelUsage.d.ts +13 -0
- package/dist/node/utils/modelUsage.d.ts.map +1 -0
- package/dist/node/vitest.config.d.ts +3 -0
- package/dist/node/vitest.config.d.ts.map +1 -0
- package/package.json +6 -5
- package/registry-entry.json +96 -0
|
@@ -0,0 +1,1108 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
5
|
+
function __accessProp(key) {
|
|
6
|
+
return this[key];
|
|
7
|
+
}
|
|
8
|
+
var __toCommonJS = (from) => {
|
|
9
|
+
var entry = (__moduleCache ??= new WeakMap).get(from), desc;
|
|
10
|
+
if (entry)
|
|
11
|
+
return entry;
|
|
12
|
+
entry = __defProp({}, "__esModule", { value: true });
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (var key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(entry, key))
|
|
16
|
+
__defProp(entry, key, {
|
|
17
|
+
get: __accessProp.bind(from, key),
|
|
18
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
__moduleCache.set(from, entry);
|
|
22
|
+
return entry;
|
|
23
|
+
};
|
|
24
|
+
var __moduleCache;
|
|
25
|
+
var __returnValue = (v) => v;
|
|
26
|
+
function __exportSetter(name, newValue) {
|
|
27
|
+
this[name] = __returnValue.bind(null, newValue);
|
|
28
|
+
}
|
|
29
|
+
var __export = (target, all) => {
|
|
30
|
+
for (var name in all)
|
|
31
|
+
__defProp(target, name, {
|
|
32
|
+
get: all[name],
|
|
33
|
+
enumerable: true,
|
|
34
|
+
configurable: true,
|
|
35
|
+
set: __exportSetter.bind(all, name)
|
|
36
|
+
});
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
// index.node.ts
|
|
40
|
+
var exports_index_node = {};
|
|
41
|
+
__export(exports_index_node, {
|
|
42
|
+
ollamaPlugin: () => ollamaPlugin,
|
|
43
|
+
isOllamaStructuredOutputDisabled: () => isOllamaStructuredOutputDisabled,
|
|
44
|
+
getSmallModel: () => getSmallModel,
|
|
45
|
+
getSetting: () => getSetting,
|
|
46
|
+
getResponseHandlerModel: () => getResponseHandlerModel,
|
|
47
|
+
getNanoModel: () => getNanoModel,
|
|
48
|
+
getMegaModel: () => getMegaModel,
|
|
49
|
+
getMediumModel: () => getMediumModel,
|
|
50
|
+
getLargeModel: () => getLargeModel,
|
|
51
|
+
getEmbeddingModel: () => getEmbeddingModel,
|
|
52
|
+
getBaseURL: () => getBaseURL,
|
|
53
|
+
getApiBase: () => getApiBase,
|
|
54
|
+
getActionPlannerModel: () => getActionPlannerModel,
|
|
55
|
+
default: () => index_node_default,
|
|
56
|
+
DEFAULT_SMALL_MODEL: () => DEFAULT_SMALL_MODEL,
|
|
57
|
+
DEFAULT_OLLAMA_URL: () => DEFAULT_OLLAMA_URL,
|
|
58
|
+
DEFAULT_LARGE_MODEL: () => DEFAULT_LARGE_MODEL,
|
|
59
|
+
DEFAULT_EMBEDDING_MODEL: () => DEFAULT_EMBEDDING_MODEL
|
|
60
|
+
});
|
|
61
|
+
module.exports = __toCommonJS(exports_index_node);
|
|
62
|
+
|
|
63
|
+
// plugin.ts
|
|
64
|
+
var import_core5 = require("@elizaos/core");
|
|
65
|
+
|
|
66
|
+
// models/embedding.ts
|
|
67
|
+
var import_core3 = require("@elizaos/core");
|
|
68
|
+
var import_ai = require("ai");
|
|
69
|
+
var import_ollama_ai_provider_v2 = require("ollama-ai-provider-v2");
|
|
70
|
+
|
|
71
|
+
// utils/config.ts
|
|
72
|
+
var DEFAULT_OLLAMA_URL = "http://localhost:11434";
|
|
73
|
+
var DEFAULT_SMALL_MODEL = "eliza-1-2b";
|
|
74
|
+
var DEFAULT_LARGE_MODEL = "eliza-1-4b";
|
|
75
|
+
var DEFAULT_EMBEDDING_MODEL = "eliza-1-2b";
|
|
76
|
+
function getEnvValue(key) {
|
|
77
|
+
if (typeof process === "undefined" || !process.env) {
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
const value = process.env[key];
|
|
81
|
+
return value === undefined ? undefined : String(value);
|
|
82
|
+
}
|
|
83
|
+
function getSetting(runtime, key, defaultValue) {
|
|
84
|
+
const value = runtime.getSetting(key);
|
|
85
|
+
if (value !== undefined && value !== null) {
|
|
86
|
+
return String(value).trim();
|
|
87
|
+
}
|
|
88
|
+
return getEnvValue(key)?.trim() ?? defaultValue;
|
|
89
|
+
}
|
|
90
|
+
function getBaseURL(runtime) {
|
|
91
|
+
const apiEndpoint = getSetting(runtime, "OLLAMA_API_ENDPOINT") || getSetting(runtime, "OLLAMA_API_URL") || getSetting(runtime, "OLLAMA_BASE_URL") || DEFAULT_OLLAMA_URL;
|
|
92
|
+
if (!apiEndpoint.endsWith("/api")) {
|
|
93
|
+
return apiEndpoint.endsWith("/") ? `${apiEndpoint}api` : `${apiEndpoint}/api`;
|
|
94
|
+
}
|
|
95
|
+
return apiEndpoint;
|
|
96
|
+
}
|
|
97
|
+
function getApiBase(runtime) {
|
|
98
|
+
const baseURL = getBaseURL(runtime);
|
|
99
|
+
return baseURL.endsWith("/api") ? baseURL.slice(0, -4) : baseURL;
|
|
100
|
+
}
|
|
101
|
+
function getSmallModel(runtime) {
|
|
102
|
+
return getSetting(runtime, "OLLAMA_SMALL_MODEL") || getSetting(runtime, "SMALL_MODEL") || DEFAULT_SMALL_MODEL;
|
|
103
|
+
}
|
|
104
|
+
function getNanoModel(runtime) {
|
|
105
|
+
return getSetting(runtime, "OLLAMA_NANO_MODEL") || getSetting(runtime, "NANO_MODEL") || getSmallModel(runtime);
|
|
106
|
+
}
|
|
107
|
+
function getMediumModel(runtime) {
|
|
108
|
+
return getSetting(runtime, "OLLAMA_MEDIUM_MODEL") || getSetting(runtime, "MEDIUM_MODEL") || getSmallModel(runtime);
|
|
109
|
+
}
|
|
110
|
+
function getLargeModel(runtime) {
|
|
111
|
+
return getSetting(runtime, "OLLAMA_LARGE_MODEL") || getSetting(runtime, "LARGE_MODEL") || DEFAULT_LARGE_MODEL;
|
|
112
|
+
}
|
|
113
|
+
function getMegaModel(runtime) {
|
|
114
|
+
return getSetting(runtime, "OLLAMA_MEGA_MODEL") || getSetting(runtime, "MEGA_MODEL") || getLargeModel(runtime);
|
|
115
|
+
}
|
|
116
|
+
function getResponseHandlerModel(runtime) {
|
|
117
|
+
return getSetting(runtime, "OLLAMA_RESPONSE_HANDLER_MODEL") || getSetting(runtime, "OLLAMA_SHOULD_RESPOND_MODEL") || getSetting(runtime, "RESPONSE_HANDLER_MODEL") || getSetting(runtime, "SHOULD_RESPOND_MODEL") || getNanoModel(runtime);
|
|
118
|
+
}
|
|
119
|
+
function getActionPlannerModel(runtime) {
|
|
120
|
+
return getSetting(runtime, "OLLAMA_ACTION_PLANNER_MODEL") || getSetting(runtime, "OLLAMA_PLANNER_MODEL") || getSetting(runtime, "ACTION_PLANNER_MODEL") || getSetting(runtime, "PLANNER_MODEL") || getMediumModel(runtime);
|
|
121
|
+
}
|
|
122
|
+
function getEmbeddingModel(runtime) {
|
|
123
|
+
return getSetting(runtime, "OLLAMA_EMBEDDING_MODEL") || DEFAULT_EMBEDDING_MODEL;
|
|
124
|
+
}
|
|
125
|
+
function isOllamaStructuredOutputDisabled(runtime) {
|
|
126
|
+
const v = getSetting(runtime, "OLLAMA_DISABLE_STRUCTURED_OUTPUT")?.trim().toLowerCase();
|
|
127
|
+
return v === "1" || v === "true" || v === "yes" || v === "on";
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// utils/modelUsage.ts
|
|
131
|
+
var import_core = require("@elizaos/core");
|
|
132
|
+
function toFiniteNumber(value) {
|
|
133
|
+
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
return Math.max(0, Math.round(value));
|
|
137
|
+
}
|
|
138
|
+
function normalizeTokenUsage(usage) {
|
|
139
|
+
if (!usage || typeof usage !== "object") {
|
|
140
|
+
return null;
|
|
141
|
+
}
|
|
142
|
+
const record = usage;
|
|
143
|
+
const promptTokens = toFiniteNumber(record.inputTokens ?? record.promptTokens);
|
|
144
|
+
const completionTokens = toFiniteNumber(record.outputTokens ?? record.completionTokens);
|
|
145
|
+
const totalTokens = toFiniteNumber(record.totalTokens);
|
|
146
|
+
if (promptTokens === undefined && completionTokens === undefined && totalTokens === undefined) {
|
|
147
|
+
return null;
|
|
148
|
+
}
|
|
149
|
+
const normalizedPromptTokens = promptTokens ?? (completionTokens === undefined && totalTokens !== undefined ? totalTokens : Math.max(0, (totalTokens ?? 0) - (completionTokens ?? 0)));
|
|
150
|
+
const normalizedCompletionTokens = completionTokens ?? Math.max(0, (totalTokens ?? normalizedPromptTokens) - normalizedPromptTokens);
|
|
151
|
+
return {
|
|
152
|
+
promptTokens: normalizedPromptTokens,
|
|
153
|
+
completionTokens: normalizedCompletionTokens,
|
|
154
|
+
totalTokens: totalTokens ?? normalizedPromptTokens + normalizedCompletionTokens
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
function estimateTokenCount(text) {
|
|
158
|
+
return text.length === 0 ? 0 : Math.ceil(text.length / 4);
|
|
159
|
+
}
|
|
160
|
+
function stringifyForUsage(value) {
|
|
161
|
+
if (typeof value === "string") {
|
|
162
|
+
return value;
|
|
163
|
+
}
|
|
164
|
+
try {
|
|
165
|
+
return JSON.stringify(value);
|
|
166
|
+
} catch {
|
|
167
|
+
return String(value);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
function estimateUsage(prompt, response) {
|
|
171
|
+
const promptTokens = estimateTokenCount(prompt);
|
|
172
|
+
const completionTokens = estimateTokenCount(stringifyForUsage(response));
|
|
173
|
+
return {
|
|
174
|
+
promptTokens,
|
|
175
|
+
completionTokens,
|
|
176
|
+
totalTokens: promptTokens + completionTokens,
|
|
177
|
+
estimated: true
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
function estimateEmbeddingUsage(text) {
|
|
181
|
+
const promptTokens = estimateTokenCount(text);
|
|
182
|
+
return {
|
|
183
|
+
promptTokens,
|
|
184
|
+
completionTokens: 0,
|
|
185
|
+
totalTokens: promptTokens,
|
|
186
|
+
estimated: true
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
function emitModelUsed(runtime, type, model, usage) {
|
|
190
|
+
runtime.emitEvent(import_core.EventType.MODEL_USED, {
|
|
191
|
+
runtime,
|
|
192
|
+
source: "ollama",
|
|
193
|
+
provider: "ollama",
|
|
194
|
+
type,
|
|
195
|
+
model,
|
|
196
|
+
modelName: model,
|
|
197
|
+
tokens: {
|
|
198
|
+
prompt: usage.promptTokens,
|
|
199
|
+
completion: usage.completionTokens,
|
|
200
|
+
total: usage.totalTokens,
|
|
201
|
+
...usage.estimated ? { estimated: true } : {}
|
|
202
|
+
},
|
|
203
|
+
...usage.estimated ? { usageEstimated: true } : {}
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// models/availability.ts
|
|
208
|
+
var import_core2 = require("@elizaos/core");
|
|
209
|
+
async function ensureModelAvailable(model, providedBaseURL, customFetch) {
|
|
210
|
+
const baseURL = providedBaseURL || "http://localhost:11434/api";
|
|
211
|
+
const apiBase = baseURL.endsWith("/api") ? baseURL.slice(0, -4) : baseURL;
|
|
212
|
+
const fetcher = customFetch ?? fetch;
|
|
213
|
+
try {
|
|
214
|
+
const showRes = await fetcher(`${apiBase}/api/show`, {
|
|
215
|
+
method: "POST",
|
|
216
|
+
headers: { "Content-Type": "application/json" },
|
|
217
|
+
body: JSON.stringify({ model })
|
|
218
|
+
});
|
|
219
|
+
if (showRes.ok) {
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
import_core2.logger.info(`[Ollama] Model ${model} not found locally. Downloading...`);
|
|
223
|
+
const pullRes = await fetcher(`${apiBase}/api/pull`, {
|
|
224
|
+
method: "POST",
|
|
225
|
+
headers: { "Content-Type": "application/json" },
|
|
226
|
+
body: JSON.stringify({ model, stream: false })
|
|
227
|
+
});
|
|
228
|
+
if (!pullRes.ok) {
|
|
229
|
+
import_core2.logger.error(`Failed to pull model ${model}: ${pullRes.statusText}`);
|
|
230
|
+
} else {
|
|
231
|
+
import_core2.logger.info(`[Ollama] Downloaded model ${model}`);
|
|
232
|
+
}
|
|
233
|
+
} catch (err) {
|
|
234
|
+
import_core2.logger.error({ error: err }, "Error ensuring model availability");
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// models/embedding.ts
|
|
239
|
+
var INIT_PROBE_TEXT = "dimension probe";
|
|
240
|
+
function extractText(params) {
|
|
241
|
+
if (params === null) {
|
|
242
|
+
return null;
|
|
243
|
+
}
|
|
244
|
+
if (typeof params === "string") {
|
|
245
|
+
return params;
|
|
246
|
+
}
|
|
247
|
+
if (typeof params === "object" && typeof params.text === "string") {
|
|
248
|
+
return params.text;
|
|
249
|
+
}
|
|
250
|
+
throw new Error("Invalid input format for embedding: expected string or { text: string }");
|
|
251
|
+
}
|
|
252
|
+
async function handleTextEmbedding(runtime, params) {
|
|
253
|
+
const text = extractText(params);
|
|
254
|
+
const isInitProbe = text === null;
|
|
255
|
+
if (!isInitProbe && !text.trim()) {
|
|
256
|
+
throw new Error("Cannot generate embedding for empty text");
|
|
257
|
+
}
|
|
258
|
+
try {
|
|
259
|
+
const baseURL = getBaseURL(runtime);
|
|
260
|
+
const customFetch = runtime.fetch ?? undefined;
|
|
261
|
+
const ollama = import_ollama_ai_provider_v2.createOllama({
|
|
262
|
+
...customFetch ? { fetch: customFetch } : {},
|
|
263
|
+
baseURL
|
|
264
|
+
});
|
|
265
|
+
const modelName = getEmbeddingModel(runtime);
|
|
266
|
+
import_core3.logger.log(`[Ollama] Using TEXT_EMBEDDING model: ${modelName}`);
|
|
267
|
+
await ensureModelAvailable(modelName, baseURL, customFetch);
|
|
268
|
+
const maxChars = 8000 * 4;
|
|
269
|
+
let embeddingText = isInitProbe ? INIT_PROBE_TEXT : text;
|
|
270
|
+
if (embeddingText.length > maxChars) {
|
|
271
|
+
import_core3.logger.warn(`[Ollama] Embedding input too long (~${Math.ceil(embeddingText.length / 4)} tokens), truncating to ~8000 tokens`);
|
|
272
|
+
embeddingText = embeddingText.slice(0, maxChars);
|
|
273
|
+
}
|
|
274
|
+
const embedParams = {
|
|
275
|
+
model: ollama.embedding(modelName),
|
|
276
|
+
value: embeddingText
|
|
277
|
+
};
|
|
278
|
+
const { embedding, usage } = await import_ai.embed(embedParams);
|
|
279
|
+
if (!isInitProbe) {
|
|
280
|
+
emitModelUsed(runtime, import_core3.ModelType.TEXT_EMBEDDING, modelName, normalizeTokenUsage(usage) ?? estimateEmbeddingUsage(embeddingText));
|
|
281
|
+
}
|
|
282
|
+
return embedding;
|
|
283
|
+
} catch (error) {
|
|
284
|
+
import_core3.logger.error({ error }, "Error in TEXT_EMBEDDING model");
|
|
285
|
+
throw error instanceof Error ? error : new Error(String(error));
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// models/text.ts
|
|
290
|
+
var import_core4 = require("@elizaos/core");
|
|
291
|
+
var import_ai3 = require("ai");
|
|
292
|
+
var import_ollama_ai_provider_v22 = require("ollama-ai-provider-v2");
|
|
293
|
+
|
|
294
|
+
// utils/ai-sdk-wire.ts
|
|
295
|
+
var import_ai2 = require("ai");
|
|
296
|
+
function normalizeNativeTools(tools) {
|
|
297
|
+
if (!tools) {
|
|
298
|
+
return;
|
|
299
|
+
}
|
|
300
|
+
if (!Array.isArray(tools)) {
|
|
301
|
+
return tools;
|
|
302
|
+
}
|
|
303
|
+
const toolSet = {};
|
|
304
|
+
for (const rawTool of tools) {
|
|
305
|
+
const tool = asRecord(rawTool);
|
|
306
|
+
const functionTool = asRecord(tool.function);
|
|
307
|
+
const name = firstString(tool.name, functionTool.name);
|
|
308
|
+
if (!name) {
|
|
309
|
+
throw new Error("[Ollama] Native tool definition is missing a name.");
|
|
310
|
+
}
|
|
311
|
+
const description = firstString(tool.description, functionTool.description);
|
|
312
|
+
const rawSchema = tool.parameters ?? functionTool.parameters ?? { type: "object" };
|
|
313
|
+
const inputSchema = sanitizeJsonSchema(rawSchema, true);
|
|
314
|
+
toolSet[name] = {
|
|
315
|
+
...description ? { description } : {},
|
|
316
|
+
inputSchema: import_ai2.jsonSchema(inputSchema)
|
|
317
|
+
};
|
|
318
|
+
}
|
|
319
|
+
return Object.keys(toolSet).length > 0 ? toolSet : undefined;
|
|
320
|
+
}
|
|
321
|
+
function normalizeNativeMessages(messages) {
|
|
322
|
+
if (!Array.isArray(messages)) {
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
325
|
+
return messages.map((message) => normalizeNativeMessage(message));
|
|
326
|
+
}
|
|
327
|
+
function normalizeToolChoice(toolChoice) {
|
|
328
|
+
if (!toolChoice) {
|
|
329
|
+
return;
|
|
330
|
+
}
|
|
331
|
+
if (typeof toolChoice === "string" && (toolChoice === "auto" || toolChoice === "none" || toolChoice === "required")) {
|
|
332
|
+
return toolChoice;
|
|
333
|
+
}
|
|
334
|
+
const choice = asRecord(toolChoice);
|
|
335
|
+
if (choice.type === "tool") {
|
|
336
|
+
if (typeof choice.toolName === "string" && choice.toolName.length > 0) {
|
|
337
|
+
return toolChoice;
|
|
338
|
+
}
|
|
339
|
+
const toolName = firstString(choice.toolName, choice.name);
|
|
340
|
+
if (toolName) {
|
|
341
|
+
return { type: "tool", toolName };
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
if (choice.type === "function") {
|
|
345
|
+
const fn = asRecord(choice.function);
|
|
346
|
+
const toolName = firstString(fn.name);
|
|
347
|
+
if (toolName) {
|
|
348
|
+
return { type: "tool", toolName };
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
const namedTool = firstString(choice.name);
|
|
352
|
+
if (namedTool) {
|
|
353
|
+
return { type: "tool", toolName: namedTool };
|
|
354
|
+
}
|
|
355
|
+
return toolChoice;
|
|
356
|
+
}
|
|
357
|
+
function parseJsonIfPossible(value) {
|
|
358
|
+
if (typeof value !== "string") {
|
|
359
|
+
return value;
|
|
360
|
+
}
|
|
361
|
+
try {
|
|
362
|
+
return JSON.parse(value);
|
|
363
|
+
} catch {
|
|
364
|
+
return value;
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
function mapAiSdkToolCallsToCore(toolCalls) {
|
|
368
|
+
if (!Array.isArray(toolCalls) || toolCalls.length === 0) {
|
|
369
|
+
return [];
|
|
370
|
+
}
|
|
371
|
+
const out = [];
|
|
372
|
+
for (const tc of toolCalls) {
|
|
373
|
+
const mapped = mapOneToolCall(tc);
|
|
374
|
+
if (mapped) {
|
|
375
|
+
out.push(mapped);
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
return out;
|
|
379
|
+
}
|
|
380
|
+
function mapOneToolCall(tc) {
|
|
381
|
+
const r = asRecord(tc);
|
|
382
|
+
const id = String(firstString(r.toolCallId, r.id) ?? "");
|
|
383
|
+
const name = String(firstString(r.toolName, r.name) ?? "").trim();
|
|
384
|
+
if (!name) {
|
|
385
|
+
return null;
|
|
386
|
+
}
|
|
387
|
+
const rawInput = r.input ?? r.arguments ?? r.args;
|
|
388
|
+
let args;
|
|
389
|
+
if (typeof rawInput === "string") {
|
|
390
|
+
const parsed = parseJsonIfPossible(rawInput);
|
|
391
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
392
|
+
args = parsed;
|
|
393
|
+
} else {
|
|
394
|
+
args = rawInput;
|
|
395
|
+
}
|
|
396
|
+
} else if (rawInput && typeof rawInput === "object" && !Array.isArray(rawInput)) {
|
|
397
|
+
args = rawInput;
|
|
398
|
+
} else {
|
|
399
|
+
args = {};
|
|
400
|
+
}
|
|
401
|
+
return { id, name, arguments: args };
|
|
402
|
+
}
|
|
403
|
+
function normalizeNativeMessage(message) {
|
|
404
|
+
const raw = asRecord(message);
|
|
405
|
+
const providerOptions = asOptionalRecord(raw.providerOptions);
|
|
406
|
+
if (raw.role === "system") {
|
|
407
|
+
return {
|
|
408
|
+
role: "system",
|
|
409
|
+
content: stringifyMessageContent(raw.content),
|
|
410
|
+
...providerOptions ? { providerOptions } : {}
|
|
411
|
+
};
|
|
412
|
+
}
|
|
413
|
+
if (raw.role === "assistant") {
|
|
414
|
+
return {
|
|
415
|
+
role: "assistant",
|
|
416
|
+
content: normalizeAssistantContent(raw),
|
|
417
|
+
...providerOptions ? { providerOptions } : {}
|
|
418
|
+
};
|
|
419
|
+
}
|
|
420
|
+
if (raw.role === "tool") {
|
|
421
|
+
return {
|
|
422
|
+
role: "tool",
|
|
423
|
+
content: normalizeToolContent(raw),
|
|
424
|
+
...providerOptions ? { providerOptions } : {}
|
|
425
|
+
};
|
|
426
|
+
}
|
|
427
|
+
return {
|
|
428
|
+
role: "user",
|
|
429
|
+
content: normalizeUserContent(raw.content),
|
|
430
|
+
...providerOptions ? { providerOptions } : {}
|
|
431
|
+
};
|
|
432
|
+
}
|
|
433
|
+
function normalizeAssistantContent(message) {
|
|
434
|
+
const toolCalls = Array.isArray(message.toolCalls) ? message.toolCalls : [];
|
|
435
|
+
if (toolCalls.length === 0) {
|
|
436
|
+
if (Array.isArray(message.content) || typeof message.content === "string") {
|
|
437
|
+
return message.content;
|
|
438
|
+
}
|
|
439
|
+
return "";
|
|
440
|
+
}
|
|
441
|
+
const parts = [];
|
|
442
|
+
if (typeof message.content === "string" && message.content.length > 0) {
|
|
443
|
+
parts.push({ type: "text", text: message.content });
|
|
444
|
+
} else if (Array.isArray(message.content)) {
|
|
445
|
+
parts.push(...message.content);
|
|
446
|
+
}
|
|
447
|
+
for (const toolCall of toolCalls) {
|
|
448
|
+
const rawCall = asRecord(toolCall);
|
|
449
|
+
const rawFunction = asRecord(rawCall.function);
|
|
450
|
+
const toolCallId = firstString(rawCall.toolCallId, rawCall.id);
|
|
451
|
+
const toolName = firstString(rawCall.toolName, rawCall.name, rawFunction.name);
|
|
452
|
+
if (!toolCallId || !toolName) {
|
|
453
|
+
continue;
|
|
454
|
+
}
|
|
455
|
+
parts.push({
|
|
456
|
+
type: "tool-call",
|
|
457
|
+
toolCallId,
|
|
458
|
+
toolName,
|
|
459
|
+
input: parseToolCallInput(rawCall, rawFunction)
|
|
460
|
+
});
|
|
461
|
+
}
|
|
462
|
+
return parts;
|
|
463
|
+
}
|
|
464
|
+
function normalizeToolContent(message) {
|
|
465
|
+
if (Array.isArray(message.content)) {
|
|
466
|
+
return message.content;
|
|
467
|
+
}
|
|
468
|
+
const toolCallId = firstString(message.toolCallId, message.id) ?? "tool-call";
|
|
469
|
+
const toolName = firstString(message.toolName, message.name) ?? "tool";
|
|
470
|
+
const parsed = parseJsonIfPossible(message.content);
|
|
471
|
+
return [
|
|
472
|
+
{
|
|
473
|
+
type: "tool-result",
|
|
474
|
+
toolCallId,
|
|
475
|
+
toolName,
|
|
476
|
+
output: typeof parsed === "string" ? { type: "text", value: parsed } : { type: "json", value: parsed }
|
|
477
|
+
}
|
|
478
|
+
];
|
|
479
|
+
}
|
|
480
|
+
function normalizeUserContent(content) {
|
|
481
|
+
if (Array.isArray(content)) {
|
|
482
|
+
return content;
|
|
483
|
+
}
|
|
484
|
+
return stringifyMessageContent(content);
|
|
485
|
+
}
|
|
486
|
+
function stringifyMessageContent(content) {
|
|
487
|
+
if (typeof content === "string") {
|
|
488
|
+
return content;
|
|
489
|
+
}
|
|
490
|
+
if (content == null) {
|
|
491
|
+
return "";
|
|
492
|
+
}
|
|
493
|
+
return typeof content === "object" ? JSON.stringify(content) : String(content);
|
|
494
|
+
}
|
|
495
|
+
function parseToolCallInput(rawCall, rawFunction) {
|
|
496
|
+
if ("input" in rawCall) {
|
|
497
|
+
return rawCall.input;
|
|
498
|
+
}
|
|
499
|
+
return parseJsonIfPossible(rawCall.arguments ?? rawFunction.arguments ?? {});
|
|
500
|
+
}
|
|
501
|
+
function sanitizeJsonSchema(schema, isRoot = false) {
|
|
502
|
+
if (!schema || typeof schema !== "object" || Array.isArray(schema)) {
|
|
503
|
+
return { type: "object" };
|
|
504
|
+
}
|
|
505
|
+
const record = schema;
|
|
506
|
+
const sanitized = { ...record };
|
|
507
|
+
if (typeof sanitized.type !== "string") {
|
|
508
|
+
const inferredType = inferJsonSchemaType(sanitized, isRoot);
|
|
509
|
+
if (inferredType) {
|
|
510
|
+
sanitized.type = inferredType;
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
if (sanitized.properties && typeof sanitized.properties === "object" && !Array.isArray(sanitized.properties)) {
|
|
514
|
+
const properties = {};
|
|
515
|
+
for (const [key, value] of Object.entries(sanitized.properties)) {
|
|
516
|
+
properties[key] = sanitizeJsonSchema(value);
|
|
517
|
+
}
|
|
518
|
+
sanitized.properties = properties;
|
|
519
|
+
}
|
|
520
|
+
if (sanitized.items) {
|
|
521
|
+
sanitized.items = Array.isArray(sanitized.items) ? sanitized.items.map((item) => sanitizeJsonSchema(item)) : sanitizeJsonSchema(sanitized.items);
|
|
522
|
+
}
|
|
523
|
+
for (const unionKey of ["anyOf", "oneOf", "allOf"]) {
|
|
524
|
+
const value = sanitized[unionKey];
|
|
525
|
+
if (Array.isArray(value)) {
|
|
526
|
+
sanitized[unionKey] = value.map((item) => sanitizeJsonSchema(item));
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
return sanitized;
|
|
530
|
+
}
|
|
531
|
+
function inferJsonSchemaType(schema, isRoot) {
|
|
532
|
+
if ("items" in schema && !("properties" in schema)) {
|
|
533
|
+
return "array";
|
|
534
|
+
}
|
|
535
|
+
if ("properties" in schema || "required" in schema || "additionalProperties" in schema || isRoot) {
|
|
536
|
+
return "object";
|
|
537
|
+
}
|
|
538
|
+
if (Array.isArray(schema.enum) && schema.enum.length > 0) {
|
|
539
|
+
const types = new Set(schema.enum.map((value) => typeof value));
|
|
540
|
+
if (types.size === 1) {
|
|
541
|
+
const [type] = [...types];
|
|
542
|
+
if (type === "string" || type === "number" || type === "boolean") {
|
|
543
|
+
return type;
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
return;
|
|
548
|
+
}
|
|
549
|
+
function asRecord(value) {
|
|
550
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
551
|
+
}
|
|
552
|
+
function asOptionalRecord(value) {
|
|
553
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : undefined;
|
|
554
|
+
}
|
|
555
|
+
function firstString(...values) {
|
|
556
|
+
for (const value of values) {
|
|
557
|
+
if (typeof value === "string" && value.length > 0) {
|
|
558
|
+
return value;
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
return;
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
// models/text.ts
|
|
565
|
+
var TEXT_NANO_MODEL_TYPE = import_core4.ModelType.TEXT_NANO;
|
|
566
|
+
var TEXT_MEDIUM_MODEL_TYPE = import_core4.ModelType.TEXT_MEDIUM;
|
|
567
|
+
var TEXT_MEGA_MODEL_TYPE = import_core4.ModelType.TEXT_MEGA;
|
|
568
|
+
var RESPONSE_HANDLER_MODEL_TYPE = import_core4.ModelType.RESPONSE_HANDLER;
|
|
569
|
+
var ACTION_PLANNER_MODEL_TYPE = import_core4.ModelType.ACTION_PLANNER;
|
|
570
|
+
function summarizeAiSdkErrorForLogs(error, depth = 0) {
|
|
571
|
+
if (depth > 4) {
|
|
572
|
+
return { note: "max depth summarizing nested error" };
|
|
573
|
+
}
|
|
574
|
+
if (error == null) {
|
|
575
|
+
return { raw: String(error) };
|
|
576
|
+
}
|
|
577
|
+
if (typeof error !== "object") {
|
|
578
|
+
return { message: String(error) };
|
|
579
|
+
}
|
|
580
|
+
const e = error;
|
|
581
|
+
const out = {};
|
|
582
|
+
if (typeof e.name === "string")
|
|
583
|
+
out.errorName = e.name;
|
|
584
|
+
if (typeof e.message === "string")
|
|
585
|
+
out.message = e.message;
|
|
586
|
+
if (typeof e.reason === "string")
|
|
587
|
+
out.reason = e.reason;
|
|
588
|
+
if (typeof e.url === "string")
|
|
589
|
+
out.requestUrl = e.url;
|
|
590
|
+
if (typeof e.statusCode === "number")
|
|
591
|
+
out.httpStatus = e.statusCode;
|
|
592
|
+
if (typeof e.responseBody === "string")
|
|
593
|
+
out.ollamaResponseBody = e.responseBody;
|
|
594
|
+
if (Array.isArray(e.errors)) {
|
|
595
|
+
out.attemptErrors = e.errors.map((sub, i) => ({
|
|
596
|
+
attempt: i + 1,
|
|
597
|
+
...summarizeAiSdkErrorForLogs(sub, depth + 1)
|
|
598
|
+
}));
|
|
599
|
+
}
|
|
600
|
+
if (e.cause != null && typeof e.cause === "object") {
|
|
601
|
+
out.cause = summarizeAiSdkErrorForLogs(e.cause, depth + 1);
|
|
602
|
+
}
|
|
603
|
+
return out;
|
|
604
|
+
}
|
|
605
|
+
function logOllamaTextFailure(phase, modelType, modelId, endpoint, error) {
|
|
606
|
+
import_core4.logger.error({
|
|
607
|
+
src: "plugin:ollama:text",
|
|
608
|
+
phase,
|
|
609
|
+
modelType,
|
|
610
|
+
modelId,
|
|
611
|
+
ollamaApiEndpoint: endpoint,
|
|
612
|
+
...summarizeAiSdkErrorForLogs(error)
|
|
613
|
+
}, `[Ollama] ${phase} failed (${modelType}, model=${modelId}). See ollamaResponseBody / attemptErrors for Ollama’s JSON (e.g. insufficient RAM, model missing).`);
|
|
614
|
+
}
|
|
615
|
+
function buildStructuredOutput(responseSchema) {
|
|
616
|
+
if (responseSchema && typeof responseSchema === "object" && "responseFormat" in responseSchema && "parseCompleteOutput" in responseSchema) {
|
|
617
|
+
return responseSchema;
|
|
618
|
+
}
|
|
619
|
+
const schemaOptions = responseSchema && typeof responseSchema === "object" && "schema" in responseSchema ? responseSchema : { schema: responseSchema };
|
|
620
|
+
return import_ai3.Output.object({
|
|
621
|
+
schema: import_ai3.jsonSchema(schemaOptions.schema),
|
|
622
|
+
...schemaOptions.name ? { name: schemaOptions.name } : {},
|
|
623
|
+
...schemaOptions.description ? { description: schemaOptions.description } : {}
|
|
624
|
+
});
|
|
625
|
+
}
|
|
626
|
+
function serializeStructuredGenerateTextResult(result) {
|
|
627
|
+
if (result.output !== undefined && result.output !== null) {
|
|
628
|
+
return typeof result.output === "string" ? result.output : JSON.stringify(result.output);
|
|
629
|
+
}
|
|
630
|
+
const trimmed = result.text.trim();
|
|
631
|
+
if (trimmed)
|
|
632
|
+
return trimmed;
|
|
633
|
+
throw new Error("[Ollama] Structured generation returned no text or output.");
|
|
634
|
+
}
|
|
635
|
+
function buildNativeResultCast(result, modelName, usage) {
|
|
636
|
+
const payload = {
|
|
637
|
+
text: result.text,
|
|
638
|
+
toolCalls: mapAiSdkToolCallsToCore(result.toolCalls),
|
|
639
|
+
finishReason: String(result.finishReason),
|
|
640
|
+
usage,
|
|
641
|
+
providerMetadata: { modelName }
|
|
642
|
+
};
|
|
643
|
+
return payload;
|
|
644
|
+
}
|
|
645
|
+
function buildOllamaStreamTextResult(args) {
|
|
646
|
+
const streamResult = import_ai3.streamText(args.streamParams);
|
|
647
|
+
const textPromise = Promise.resolve(streamResult.text).catch(() => "");
|
|
648
|
+
const finishReasonPromise = Promise.resolve(streamResult.finishReason).catch(() => {
|
|
649
|
+
return;
|
|
650
|
+
});
|
|
651
|
+
const usagePromise = Promise.resolve(streamResult.usage).then(async (usage) => {
|
|
652
|
+
const fullText = await textPromise;
|
|
653
|
+
return normalizeTokenUsage(usage) ?? estimateUsage(args.promptForEstimate, fullText);
|
|
654
|
+
}).catch(() => {
|
|
655
|
+
return;
|
|
656
|
+
});
|
|
657
|
+
async function* textStreamWithUsage() {
|
|
658
|
+
let completed = false;
|
|
659
|
+
try {
|
|
660
|
+
for await (const chunk of streamResult.textStream) {
|
|
661
|
+
yield chunk;
|
|
662
|
+
}
|
|
663
|
+
completed = true;
|
|
664
|
+
} catch (streamErr) {
|
|
665
|
+
logOllamaTextFailure("streamText.textStream", String(args.modelType), args.model, args.endpoint, streamErr);
|
|
666
|
+
throw streamErr;
|
|
667
|
+
} finally {
|
|
668
|
+
if (completed) {
|
|
669
|
+
const usage = await usagePromise.catch(() => {
|
|
670
|
+
return;
|
|
671
|
+
});
|
|
672
|
+
if (usage) {
|
|
673
|
+
emitModelUsed(args.runtime, args.modelType, args.model, usage);
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
return {
|
|
679
|
+
textStream: textStreamWithUsage(),
|
|
680
|
+
text: textPromise,
|
|
681
|
+
usage: usagePromise,
|
|
682
|
+
finishReason: finishReasonPromise
|
|
683
|
+
};
|
|
684
|
+
}
|
|
685
|
+
function stringifyPlannerToolArgs(arguments_) {
|
|
686
|
+
if (typeof arguments_ === "string") {
|
|
687
|
+
return arguments_;
|
|
688
|
+
}
|
|
689
|
+
return JSON.stringify(arguments_);
|
|
690
|
+
}
|
|
691
|
+
function buildOllamaStreamWithToolsResult(args) {
|
|
692
|
+
const streamResult = import_ai3.streamText(args.streamParams);
|
|
693
|
+
const sdkTextPromise = Promise.resolve(streamResult.text).catch(() => "");
|
|
694
|
+
const finishReasonPromise = Promise.resolve(streamResult.finishReason).catch(() => {
|
|
695
|
+
return;
|
|
696
|
+
});
|
|
697
|
+
const toolCallsPromise = Promise.resolve(streamResult.toolCalls).then((calls) => mapAiSdkToolCallsToCore(calls)).catch(() => []);
|
|
698
|
+
const usagePromise = Promise.resolve(streamResult.usage).then(async (usage) => {
|
|
699
|
+
const fullText = await sdkTextPromise;
|
|
700
|
+
return normalizeTokenUsage(usage) ?? estimateUsage(args.promptForEstimate, fullText);
|
|
701
|
+
}).catch(() => {
|
|
702
|
+
return;
|
|
703
|
+
});
|
|
704
|
+
const isNativePlannerType = args.modelType === RESPONSE_HANDLER_MODEL_TYPE || args.modelType === ACTION_PLANNER_MODEL_TYPE;
|
|
705
|
+
const textPromise = isNativePlannerType ? toolCallsPromise.then(async (mapped) => {
|
|
706
|
+
const first = mapped[0];
|
|
707
|
+
if (first) {
|
|
708
|
+
return stringifyPlannerToolArgs(first.arguments);
|
|
709
|
+
}
|
|
710
|
+
return sdkTextPromise;
|
|
711
|
+
}) : sdkTextPromise;
|
|
712
|
+
async function* textStreamWithUsage() {
|
|
713
|
+
let completed = false;
|
|
714
|
+
try {
|
|
715
|
+
if (isNativePlannerType) {
|
|
716
|
+
for await (const _ of streamResult.textStream) {}
|
|
717
|
+
const mapped = await toolCallsPromise;
|
|
718
|
+
const first = mapped[0];
|
|
719
|
+
if (first) {
|
|
720
|
+
yield stringifyPlannerToolArgs(first.arguments);
|
|
721
|
+
} else {
|
|
722
|
+
const fallbackText = await sdkTextPromise;
|
|
723
|
+
if (fallbackText) {
|
|
724
|
+
yield fallbackText;
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
} else {
|
|
728
|
+
for await (const chunk of streamResult.textStream) {
|
|
729
|
+
yield chunk;
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
completed = true;
|
|
733
|
+
} catch (streamErr) {
|
|
734
|
+
logOllamaTextFailure("streamText.textStream", String(args.modelType), args.model, args.endpoint, streamErr);
|
|
735
|
+
throw streamErr;
|
|
736
|
+
} finally {
|
|
737
|
+
if (completed) {
|
|
738
|
+
const usage = await usagePromise.catch(() => {
|
|
739
|
+
return;
|
|
740
|
+
});
|
|
741
|
+
if (usage) {
|
|
742
|
+
emitModelUsed(args.runtime, args.modelType, args.model, usage);
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
return {
|
|
748
|
+
textStream: textStreamWithUsage(),
|
|
749
|
+
text: textPromise,
|
|
750
|
+
usage: usagePromise,
|
|
751
|
+
finishReason: finishReasonPromise,
|
|
752
|
+
toolCalls: toolCallsPromise
|
|
753
|
+
};
|
|
754
|
+
}
|
|
755
|
+
function getModelNameForType(runtime, modelType) {
|
|
756
|
+
switch (modelType) {
|
|
757
|
+
case TEXT_NANO_MODEL_TYPE:
|
|
758
|
+
return getNanoModel(runtime);
|
|
759
|
+
case TEXT_MEDIUM_MODEL_TYPE:
|
|
760
|
+
return getMediumModel(runtime);
|
|
761
|
+
case import_core4.ModelType.TEXT_SMALL:
|
|
762
|
+
return getSmallModel(runtime);
|
|
763
|
+
case import_core4.ModelType.TEXT_LARGE:
|
|
764
|
+
return getLargeModel(runtime);
|
|
765
|
+
case TEXT_MEGA_MODEL_TYPE:
|
|
766
|
+
return getMegaModel(runtime);
|
|
767
|
+
case RESPONSE_HANDLER_MODEL_TYPE:
|
|
768
|
+
return getResponseHandlerModel(runtime);
|
|
769
|
+
case ACTION_PLANNER_MODEL_TYPE:
|
|
770
|
+
return getActionPlannerModel(runtime);
|
|
771
|
+
default:
|
|
772
|
+
return getLargeModel(runtime);
|
|
773
|
+
}
|
|
774
|
+
}
|
|
775
|
+
async function handleTextWithModelType(runtime, modelType, params) {
|
|
776
|
+
const extended = params;
|
|
777
|
+
const { prompt, temperature = 0.7, frequencyPenalty = 0.7, presencePenalty = 0.7 } = params;
|
|
778
|
+
const maxTokens = params.omitMaxTokens ? undefined : params.maxTokens ?? 8192;
|
|
779
|
+
let modelIdForLog = "";
|
|
780
|
+
try {
|
|
781
|
+
const structuredDisabled = isOllamaStructuredOutputDisabled(runtime);
|
|
782
|
+
let responseSchema = extended.responseSchema;
|
|
783
|
+
if (structuredDisabled && extended.responseSchema) {
|
|
784
|
+
import_core4.logger.debug("[Ollama] OLLAMA_DISABLE_STRUCTURED_OUTPUT is set — ignoring responseSchema for this call.");
|
|
785
|
+
responseSchema = undefined;
|
|
786
|
+
}
|
|
787
|
+
const tools = normalizeNativeTools(extended.tools);
|
|
788
|
+
const baseURL = getBaseURL(runtime);
|
|
789
|
+
const customFetch = runtime.fetch ?? undefined;
|
|
790
|
+
const ollama = import_ollama_ai_provider_v22.createOllama({
|
|
791
|
+
...customFetch ? { fetch: customFetch } : {},
|
|
792
|
+
baseURL
|
|
793
|
+
});
|
|
794
|
+
const model = getModelNameForType(runtime, modelType);
|
|
795
|
+
modelIdForLog = model;
|
|
796
|
+
import_core4.logger.log(`[Ollama] Using ${modelType} model: ${model}`);
|
|
797
|
+
await ensureModelAvailable(model, baseURL, customFetch);
|
|
798
|
+
const system = import_core4.resolveEffectiveSystemPrompt({
|
|
799
|
+
params,
|
|
800
|
+
fallback: import_core4.buildCanonicalSystemPrompt({ character: runtime.character })
|
|
801
|
+
});
|
|
802
|
+
let outputSpec = responseSchema !== undefined && responseSchema !== null ? buildStructuredOutput(responseSchema) : undefined;
|
|
803
|
+
if (tools && outputSpec) {
|
|
804
|
+
import_core4.logger.debug("[Ollama] tools and responseSchema both present — omitting structured output for this call.");
|
|
805
|
+
outputSpec = undefined;
|
|
806
|
+
}
|
|
807
|
+
const wireRaw = import_core4.dropDuplicateLeadingSystemMessage(extended.messages, system);
|
|
808
|
+
const normalizedMessages = normalizeNativeMessages(wireRaw);
|
|
809
|
+
const hasChatMessages = Array.isArray(normalizedMessages) && normalizedMessages.length > 0;
|
|
810
|
+
const toolChoice = tools ? normalizeToolChoice(extended.toolChoice) : undefined;
|
|
811
|
+
const shouldReturnNative = Boolean(hasChatMessages || tools || extended.toolChoice || outputSpec !== undefined);
|
|
812
|
+
const renderedPrompt = hasChatMessages ? "" : import_core4.renderChatMessagesForPrompt(params.messages, {
|
|
813
|
+
...system ? { omitDuplicateSystem: system } : {}
|
|
814
|
+
}) ?? prompt ?? "";
|
|
815
|
+
const promptOrMessages = hasChatMessages ? { messages: normalizedMessages } : { prompt: renderedPrompt };
|
|
816
|
+
const resolvedStopSequences = Array.isArray(params.stopSequences) && params.stopSequences.length > 0 ? params.stopSequences : undefined;
|
|
817
|
+
const promptForUsageEstimate = hasChatMessages ? JSON.stringify(normalizedMessages) : renderedPrompt;
|
|
818
|
+
const baseGenerateArgs = {
|
|
819
|
+
model: ollama(model),
|
|
820
|
+
...promptOrMessages,
|
|
821
|
+
system,
|
|
822
|
+
temperature,
|
|
823
|
+
frequencyPenalty,
|
|
824
|
+
presencePenalty,
|
|
825
|
+
...typeof maxTokens === "number" ? { maxOutputTokens: maxTokens } : {},
|
|
826
|
+
...resolvedStopSequences ? { stopSequences: resolvedStopSequences } : {},
|
|
827
|
+
...tools ? { tools, ...toolChoice ? { toolChoice } : {} } : {},
|
|
828
|
+
...outputSpec ? { output: outputSpec } : {}
|
|
829
|
+
};
|
|
830
|
+
if (params.stream) {
|
|
831
|
+
if (tools) {
|
|
832
|
+
return buildOllamaStreamWithToolsResult({
|
|
833
|
+
runtime,
|
|
834
|
+
modelType,
|
|
835
|
+
model,
|
|
836
|
+
endpoint: baseURL,
|
|
837
|
+
streamParams: baseGenerateArgs,
|
|
838
|
+
promptForEstimate: promptForUsageEstimate
|
|
839
|
+
});
|
|
840
|
+
}
|
|
841
|
+
if (!extended.toolChoice) {
|
|
842
|
+
if (!outputSpec) {
|
|
843
|
+
return buildOllamaStreamTextResult({
|
|
844
|
+
runtime,
|
|
845
|
+
modelType,
|
|
846
|
+
model,
|
|
847
|
+
endpoint: baseURL,
|
|
848
|
+
streamParams: baseGenerateArgs,
|
|
849
|
+
promptForEstimate: promptForUsageEstimate
|
|
850
|
+
});
|
|
851
|
+
}
|
|
852
|
+
import_core4.logger.debug({ src: "plugin:ollama:text", modelType }, "[Ollama] stream=true with responseSchema (no tools) — using generateText. Why: ollama-ai-provider-v2 does not support structured JSON output on the streamText path for this adapter.");
|
|
853
|
+
} else {
|
|
854
|
+
import_core4.logger.debug({ src: "plugin:ollama:text", modelType }, "[Ollama] stream=true with toolChoice but no tools on wire — using generateText. Why: streamText+tools requires a ToolSet; callers should pass tools alongside toolChoice.");
|
|
855
|
+
}
|
|
856
|
+
}
|
|
857
|
+
const result = await import_ai3.generateText(baseGenerateArgs);
|
|
858
|
+
const usage = normalizeTokenUsage(result.usage) ?? estimateUsage(promptForUsageEstimate, result.text);
|
|
859
|
+
emitModelUsed(runtime, modelType, model, usage);
|
|
860
|
+
if (shouldReturnNative) {
|
|
861
|
+
if (outputSpec !== undefined) {
|
|
862
|
+
return serializeStructuredGenerateTextResult(result);
|
|
863
|
+
}
|
|
864
|
+
return buildNativeResultCast(result, model, usage);
|
|
865
|
+
}
|
|
866
|
+
return result.text;
|
|
867
|
+
} catch (error) {
|
|
868
|
+
let endpoint = "";
|
|
869
|
+
try {
|
|
870
|
+
endpoint = getBaseURL(runtime);
|
|
871
|
+
} catch {}
|
|
872
|
+
logOllamaTextFailure("generateText", String(modelType), modelIdForLog || "(unknown)", endpoint, error);
|
|
873
|
+
throw error;
|
|
874
|
+
}
|
|
875
|
+
}
|
|
876
|
+
async function handleTextSmall(runtime, params) {
|
|
877
|
+
return handleTextWithModelType(runtime, import_core4.ModelType.TEXT_SMALL, params);
|
|
878
|
+
}
|
|
879
|
+
async function handleTextNano(runtime, params) {
|
|
880
|
+
return handleTextWithModelType(runtime, TEXT_NANO_MODEL_TYPE, params);
|
|
881
|
+
}
|
|
882
|
+
async function handleTextMedium(runtime, params) {
|
|
883
|
+
return handleTextWithModelType(runtime, TEXT_MEDIUM_MODEL_TYPE, params);
|
|
884
|
+
}
|
|
885
|
+
async function handleTextLarge(runtime, params) {
|
|
886
|
+
return handleTextWithModelType(runtime, import_core4.ModelType.TEXT_LARGE, params);
|
|
887
|
+
}
|
|
888
|
+
async function handleTextMega(runtime, params) {
|
|
889
|
+
return handleTextWithModelType(runtime, TEXT_MEGA_MODEL_TYPE, params);
|
|
890
|
+
}
|
|
891
|
+
async function handleResponseHandler(runtime, params) {
|
|
892
|
+
return handleTextWithModelType(runtime, RESPONSE_HANDLER_MODEL_TYPE, params);
|
|
893
|
+
}
|
|
894
|
+
async function handleActionPlanner(runtime, params) {
|
|
895
|
+
return handleTextWithModelType(runtime, ACTION_PLANNER_MODEL_TYPE, params);
|
|
896
|
+
}
|
|
897
|
+
|
|
898
|
+
// plugin.ts
|
|
899
|
+
var _globalThis = globalThis;
|
|
900
|
+
_globalThis.AI_SDK_LOG_WARNINGS ??= false;
|
|
901
|
+
function getProcessEnv() {
|
|
902
|
+
if (typeof process === "undefined" || !process.env) {
|
|
903
|
+
return {};
|
|
904
|
+
}
|
|
905
|
+
return process.env;
|
|
906
|
+
}
|
|
907
|
+
var env = getProcessEnv();
|
|
908
|
+
var TEXT_NANO_MODEL_TYPE2 = import_core5.ModelType.TEXT_NANO;
|
|
909
|
+
var TEXT_MEDIUM_MODEL_TYPE2 = import_core5.ModelType.TEXT_MEDIUM;
|
|
910
|
+
var TEXT_MEGA_MODEL_TYPE2 = import_core5.ModelType.TEXT_MEGA;
|
|
911
|
+
var RESPONSE_HANDLER_MODEL_TYPE2 = import_core5.ModelType.RESPONSE_HANDLER;
|
|
912
|
+
var ACTION_PLANNER_MODEL_TYPE2 = import_core5.ModelType.ACTION_PLANNER;
|
|
913
|
+
var ollamaPlugin = {
|
|
914
|
+
name: "ollama",
|
|
915
|
+
description: "Ollama plugin for local LLM inference",
|
|
916
|
+
autoEnable: {
|
|
917
|
+
envKeys: ["OLLAMA_BASE_URL"]
|
|
918
|
+
},
|
|
919
|
+
config: {
|
|
920
|
+
OLLAMA_API_ENDPOINT: env.OLLAMA_API_ENDPOINT ?? null,
|
|
921
|
+
OLLAMA_NANO_MODEL: env.OLLAMA_NANO_MODEL ?? null,
|
|
922
|
+
OLLAMA_SMALL_MODEL: env.OLLAMA_SMALL_MODEL ?? null,
|
|
923
|
+
OLLAMA_MEDIUM_MODEL: env.OLLAMA_MEDIUM_MODEL ?? null,
|
|
924
|
+
OLLAMA_LARGE_MODEL: env.OLLAMA_LARGE_MODEL ?? null,
|
|
925
|
+
OLLAMA_MEGA_MODEL: env.OLLAMA_MEGA_MODEL ?? null,
|
|
926
|
+
OLLAMA_RESPONSE_HANDLER_MODEL: env.OLLAMA_RESPONSE_HANDLER_MODEL ?? null,
|
|
927
|
+
OLLAMA_SHOULD_RESPOND_MODEL: env.OLLAMA_SHOULD_RESPOND_MODEL ?? null,
|
|
928
|
+
OLLAMA_ACTION_PLANNER_MODEL: env.OLLAMA_ACTION_PLANNER_MODEL ?? null,
|
|
929
|
+
OLLAMA_PLANNER_MODEL: env.OLLAMA_PLANNER_MODEL ?? null,
|
|
930
|
+
NANO_MODEL: env.NANO_MODEL ?? null,
|
|
931
|
+
MEDIUM_MODEL: env.MEDIUM_MODEL ?? null,
|
|
932
|
+
SMALL_MODEL: env.SMALL_MODEL ?? null,
|
|
933
|
+
LARGE_MODEL: env.LARGE_MODEL ?? null,
|
|
934
|
+
MEGA_MODEL: env.MEGA_MODEL ?? null,
|
|
935
|
+
RESPONSE_HANDLER_MODEL: env.RESPONSE_HANDLER_MODEL ?? null,
|
|
936
|
+
SHOULD_RESPOND_MODEL: env.SHOULD_RESPOND_MODEL ?? null,
|
|
937
|
+
ACTION_PLANNER_MODEL: env.ACTION_PLANNER_MODEL ?? null,
|
|
938
|
+
PLANNER_MODEL: env.PLANNER_MODEL ?? null,
|
|
939
|
+
OLLAMA_EMBEDDING_MODEL: env.OLLAMA_EMBEDDING_MODEL ?? null,
|
|
940
|
+
OLLAMA_DISABLE_STRUCTURED_OUTPUT: env.OLLAMA_DISABLE_STRUCTURED_OUTPUT ?? null
|
|
941
|
+
},
|
|
942
|
+
async init(_config, runtime) {
|
|
943
|
+
const baseURL = getBaseURL(runtime);
|
|
944
|
+
const apiBase = getApiBase(runtime);
|
|
945
|
+
if (!baseURL || baseURL === "http://localhost:11434/api") {
|
|
946
|
+
const endpoint = runtime.getSetting("OLLAMA_API_ENDPOINT");
|
|
947
|
+
if (!endpoint) {
|
|
948
|
+
import_core5.logger.warn("OLLAMA_API_ENDPOINT not set, using default localhost:11434");
|
|
949
|
+
}
|
|
950
|
+
}
|
|
951
|
+
try {
|
|
952
|
+
const fetchImpl = runtime.fetch ?? fetch;
|
|
953
|
+
const response = await fetchImpl(`${apiBase}/api/tags`, {
|
|
954
|
+
method: "GET",
|
|
955
|
+
headers: { "Content-Type": "application/json" }
|
|
956
|
+
});
|
|
957
|
+
if (!response.ok) {
|
|
958
|
+
import_core5.logger.warn(`Ollama API validation failed: ${response.statusText}`);
|
|
959
|
+
}
|
|
960
|
+
} catch (fetchError) {
|
|
961
|
+
const message = fetchError instanceof Error ? fetchError.message : String(fetchError);
|
|
962
|
+
import_core5.logger.warn(`Ollama API validation error: ${message}`);
|
|
963
|
+
}
|
|
964
|
+
},
|
|
965
|
+
models: {
|
|
966
|
+
[import_core5.ModelType.TEXT_EMBEDDING]: async (runtime, params) => {
|
|
967
|
+
return handleTextEmbedding(runtime, params);
|
|
968
|
+
},
|
|
969
|
+
[TEXT_NANO_MODEL_TYPE2]: async (runtime, params) => {
|
|
970
|
+
return handleTextNano(runtime, params);
|
|
971
|
+
},
|
|
972
|
+
[import_core5.ModelType.TEXT_SMALL]: async (runtime, params) => {
|
|
973
|
+
return handleTextSmall(runtime, params);
|
|
974
|
+
},
|
|
975
|
+
[TEXT_MEDIUM_MODEL_TYPE2]: async (runtime, params) => {
|
|
976
|
+
return handleTextMedium(runtime, params);
|
|
977
|
+
},
|
|
978
|
+
[import_core5.ModelType.TEXT_LARGE]: async (runtime, params) => {
|
|
979
|
+
return handleTextLarge(runtime, params);
|
|
980
|
+
},
|
|
981
|
+
[TEXT_MEGA_MODEL_TYPE2]: async (runtime, params) => {
|
|
982
|
+
return handleTextMega(runtime, params);
|
|
983
|
+
},
|
|
984
|
+
[RESPONSE_HANDLER_MODEL_TYPE2]: async (runtime, params) => {
|
|
985
|
+
return handleResponseHandler(runtime, params);
|
|
986
|
+
},
|
|
987
|
+
[ACTION_PLANNER_MODEL_TYPE2]: async (runtime, params) => {
|
|
988
|
+
return handleActionPlanner(runtime, params);
|
|
989
|
+
}
|
|
990
|
+
},
|
|
991
|
+
tests: [
|
|
992
|
+
{
|
|
993
|
+
name: "ollama_plugin_tests",
|
|
994
|
+
tests: [
|
|
995
|
+
{
|
|
996
|
+
name: "ollama_test_url_validation",
|
|
997
|
+
fn: async (runtime) => {
|
|
998
|
+
try {
|
|
999
|
+
const apiBase = getApiBase(runtime);
|
|
1000
|
+
const response = await fetch(`${apiBase}/api/tags`);
|
|
1001
|
+
if (!response.ok) {
|
|
1002
|
+
import_core5.logger.error(`Failed to validate Ollama API: ${response.statusText}`);
|
|
1003
|
+
}
|
|
1004
|
+
} catch (error) {
|
|
1005
|
+
import_core5.logger.error({ error }, "Error in ollama_test_url_validation");
|
|
1006
|
+
}
|
|
1007
|
+
}
|
|
1008
|
+
},
|
|
1009
|
+
{
|
|
1010
|
+
name: "ollama_test_text_embedding",
|
|
1011
|
+
fn: async (runtime) => {
|
|
1012
|
+
try {
|
|
1013
|
+
const runModel = runtime.useModel.bind(runtime);
|
|
1014
|
+
const embedding = await runModel(import_core5.ModelType.TEXT_EMBEDDING, {
|
|
1015
|
+
text: "Hello, world!"
|
|
1016
|
+
});
|
|
1017
|
+
import_core5.logger.log({ embedding }, "Generated embedding");
|
|
1018
|
+
} catch (error) {
|
|
1019
|
+
import_core5.logger.error({ error }, "Error in test_text_embedding");
|
|
1020
|
+
}
|
|
1021
|
+
}
|
|
1022
|
+
},
|
|
1023
|
+
{
|
|
1024
|
+
name: "ollama_test_text_large",
|
|
1025
|
+
fn: async (runtime) => {
|
|
1026
|
+
try {
|
|
1027
|
+
const runModel = runtime.useModel.bind(runtime);
|
|
1028
|
+
const text = await runModel(import_core5.ModelType.TEXT_LARGE, {
|
|
1029
|
+
prompt: "What is the nature of reality in 10 words?"
|
|
1030
|
+
});
|
|
1031
|
+
if (text.length === 0) {
|
|
1032
|
+
import_core5.logger.error("Failed to generate text");
|
|
1033
|
+
return;
|
|
1034
|
+
}
|
|
1035
|
+
import_core5.logger.log({ text }, "Generated with test_text_large");
|
|
1036
|
+
} catch (error) {
|
|
1037
|
+
import_core5.logger.error({ error }, "Error in test_text_large");
|
|
1038
|
+
}
|
|
1039
|
+
}
|
|
1040
|
+
},
|
|
1041
|
+
{
|
|
1042
|
+
name: "ollama_test_text_small",
|
|
1043
|
+
fn: async (runtime) => {
|
|
1044
|
+
try {
|
|
1045
|
+
const runModel = runtime.useModel.bind(runtime);
|
|
1046
|
+
const text = await runModel(import_core5.ModelType.TEXT_SMALL, {
|
|
1047
|
+
prompt: "What is the nature of reality in 10 words?"
|
|
1048
|
+
});
|
|
1049
|
+
if (text.length === 0) {
|
|
1050
|
+
import_core5.logger.error("Failed to generate text");
|
|
1051
|
+
return;
|
|
1052
|
+
}
|
|
1053
|
+
import_core5.logger.log({ text }, "Generated with test_text_small");
|
|
1054
|
+
} catch (error) {
|
|
1055
|
+
import_core5.logger.error({ error }, "Error in test_text_small");
|
|
1056
|
+
}
|
|
1057
|
+
}
|
|
1058
|
+
},
|
|
1059
|
+
{
|
|
1060
|
+
name: "ollama_test_structured_output_via_text_small",
|
|
1061
|
+
fn: async (runtime) => {
|
|
1062
|
+
try {
|
|
1063
|
+
const runModel = runtime.useModel.bind(runtime);
|
|
1064
|
+
const result = await runModel(import_core5.ModelType.TEXT_SMALL, {
|
|
1065
|
+
prompt: "Generate a JSON object representing a user profile with name, age, and hobbies",
|
|
1066
|
+
temperature: 0.7,
|
|
1067
|
+
responseSchema: {
|
|
1068
|
+
type: "object",
|
|
1069
|
+
properties: {
|
|
1070
|
+
name: { type: "string" },
|
|
1071
|
+
age: { type: "number" },
|
|
1072
|
+
hobbies: { type: "array", items: { type: "string" } }
|
|
1073
|
+
},
|
|
1074
|
+
required: ["name", "age", "hobbies"]
|
|
1075
|
+
}
|
|
1076
|
+
});
|
|
1077
|
+
import_core5.logger.log({ result }, "Generated structured output via TEXT_SMALL");
|
|
1078
|
+
} catch (error) {
|
|
1079
|
+
import_core5.logger.error({ error }, "Error in test_structured_output_via_text_small");
|
|
1080
|
+
}
|
|
1081
|
+
}
|
|
1082
|
+
},
|
|
1083
|
+
{
|
|
1084
|
+
name: "ollama_test_structured_output_via_text_large",
|
|
1085
|
+
fn: async (runtime) => {
|
|
1086
|
+
try {
|
|
1087
|
+
const runModel = runtime.useModel.bind(runtime);
|
|
1088
|
+
const result = await runModel(import_core5.ModelType.TEXT_LARGE, {
|
|
1089
|
+
prompt: "Generate a detailed JSON object representing a restaurant with name, cuisine type, menu items with prices, and customer reviews",
|
|
1090
|
+
temperature: 0.7,
|
|
1091
|
+
responseSchema: { type: "object" }
|
|
1092
|
+
});
|
|
1093
|
+
import_core5.logger.log({ result }, "Generated structured output via TEXT_LARGE");
|
|
1094
|
+
} catch (error) {
|
|
1095
|
+
import_core5.logger.error({ error }, "Error in test_structured_output_via_text_large");
|
|
1096
|
+
}
|
|
1097
|
+
}
|
|
1098
|
+
}
|
|
1099
|
+
]
|
|
1100
|
+
}
|
|
1101
|
+
]
|
|
1102
|
+
};
|
|
1103
|
+
// index.node.ts
|
|
1104
|
+
var defaultOllamaPlugin = ollamaPlugin;
|
|
1105
|
+
var index_node_default = defaultOllamaPlugin;
|
|
1106
|
+
|
|
1107
|
+
//# debugId=88040C7A9C8949A364756E2164756E21
|
|
1108
|
+
//# sourceMappingURL=index.node.cjs.map
|