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