ai-sdk-ollama 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs ADDED
@@ -0,0 +1,991 @@
1
+ 'use strict';
2
+
3
+ var provider = require('@ai-sdk/provider');
4
+ var ollama$1 = require('ollama');
5
+
6
+ // src/provider.ts
7
+
8
+ // src/utils/convert-to-ollama-messages.ts
9
+ function convertToOllamaChatMessages(prompt) {
10
+ const messages = [];
11
+ for (const message of prompt) {
12
+ switch (message.role) {
13
+ case "system": {
14
+ messages.push({
15
+ role: "system",
16
+ content: message.content
17
+ });
18
+ break;
19
+ }
20
+ case "user": {
21
+ if (typeof message.content === "string") {
22
+ messages.push({
23
+ role: "user",
24
+ content: message.content
25
+ });
26
+ } else {
27
+ const textParts = message.content.filter((part) => part.type === "text").map((part) => part.text).join("\n");
28
+ const imageParts = message.content.filter(
29
+ (part) => part.type === "file"
30
+ ).filter((part) => part.mediaType.startsWith("image/")).map((part) => {
31
+ if (part.data instanceof URL) {
32
+ return part.data.href;
33
+ } else if (typeof part.data === "string") {
34
+ if (part.data.startsWith("data:")) {
35
+ const base64Match = part.data.match(/data:[^;]+;base64,(.+)/);
36
+ if (base64Match) {
37
+ return base64Match[1];
38
+ }
39
+ }
40
+ return part.data;
41
+ } else if (part.data instanceof Uint8Array) {
42
+ return Buffer.from(part.data).toString("base64");
43
+ } else {
44
+ return String(part.data);
45
+ }
46
+ });
47
+ messages.push({
48
+ role: "user",
49
+ content: textParts,
50
+ images: imageParts.length > 0 ? imageParts.filter((img) => img !== void 0) : void 0
51
+ });
52
+ }
53
+ break;
54
+ }
55
+ case "assistant": {
56
+ let content = "";
57
+ if (typeof message.content === "string") {
58
+ content = message.content;
59
+ } else {
60
+ content = message.content.filter((part) => part.type === "text").map((part) => part.text).join("");
61
+ const toolCalls = message.content.filter(
62
+ (part) => part.type === "tool-call"
63
+ );
64
+ if (toolCalls.length > 0) {
65
+ const toolCallText = toolCalls.map((tc) => `[Tool Call: ${tc.toolName}]`).join("\n");
66
+ if (toolCallText) {
67
+ content = content ? `${content}
68
+ ${toolCallText}` : toolCallText;
69
+ }
70
+ }
71
+ }
72
+ messages.push({
73
+ role: "assistant",
74
+ content
75
+ });
76
+ break;
77
+ }
78
+ case "tool": {
79
+ messages.push({
80
+ role: "user",
81
+ content: `[Tool Result]`
82
+ });
83
+ break;
84
+ }
85
+ default: {
86
+ throw new Error(
87
+ `Unsupported message role: ${message.role}`
88
+ );
89
+ }
90
+ }
91
+ }
92
+ return messages;
93
+ }
94
+
95
+ // src/utils/map-ollama-finish-reason.ts
96
+ function mapOllamaFinishReason(reason) {
97
+ if (!reason) return "unknown";
98
+ switch (reason) {
99
+ case "stop": {
100
+ return "stop";
101
+ }
102
+ case "length": {
103
+ return "length";
104
+ }
105
+ default: {
106
+ return "unknown";
107
+ }
108
+ }
109
+ }
110
+
111
+ // src/utils/ollama-error.ts
112
+ var OllamaError = class _OllamaError extends Error {
113
+ cause;
114
+ constructor({ message, cause }) {
115
+ super(message);
116
+ this.name = "OllamaError";
117
+ this.cause = cause;
118
+ }
119
+ static isOllamaError(error) {
120
+ return error instanceof _OllamaError;
121
+ }
122
+ };
123
+
124
+ // src/utils/model-capabilities.ts
125
+ var MODEL_CAPABILITIES = {
126
+ // Llama family
127
+ "llama3.2": {
128
+ supportsToolCalling: true,
129
+ supportsVision: false,
130
+ supportsJsonMode: true,
131
+ contextWindow: 128e3,
132
+ recommendedFor: ["general", "coding", "reasoning"]
133
+ },
134
+ "llama3.2:1b": {
135
+ supportsToolCalling: true,
136
+ supportsVision: false,
137
+ supportsJsonMode: true,
138
+ contextWindow: 128e3,
139
+ recommendedFor: ["lightweight", "edge"]
140
+ },
141
+ "llama3.2:3b": {
142
+ supportsToolCalling: true,
143
+ supportsVision: false,
144
+ supportsJsonMode: true,
145
+ contextWindow: 128e3,
146
+ recommendedFor: ["balanced", "general"]
147
+ },
148
+ "llama3.2-vision": {
149
+ supportsToolCalling: true,
150
+ supportsVision: true,
151
+ supportsJsonMode: true,
152
+ contextWindow: 128e3,
153
+ recommendedFor: ["vision", "multimodal"]
154
+ },
155
+ "llama3.1": {
156
+ supportsToolCalling: true,
157
+ supportsVision: false,
158
+ supportsJsonMode: true,
159
+ contextWindow: 128e3,
160
+ recommendedFor: ["general", "coding", "reasoning"]
161
+ },
162
+ "llama3.1:8b": {
163
+ supportsToolCalling: true,
164
+ supportsVision: false,
165
+ supportsJsonMode: true,
166
+ contextWindow: 128e3,
167
+ recommendedFor: ["general", "balanced"]
168
+ },
169
+ "llama3.1:70b": {
170
+ supportsToolCalling: true,
171
+ supportsVision: false,
172
+ supportsJsonMode: true,
173
+ contextWindow: 128e3,
174
+ recommendedFor: ["complex", "reasoning", "professional"]
175
+ },
176
+ "llama3.1:405b": {
177
+ supportsToolCalling: true,
178
+ supportsVision: false,
179
+ supportsJsonMode: true,
180
+ contextWindow: 128e3,
181
+ recommendedFor: ["enterprise", "complex", "research"]
182
+ },
183
+ llama3: {
184
+ supportsToolCalling: false,
185
+ // Older Llama 3 doesn't support tools
186
+ supportsVision: false,
187
+ supportsJsonMode: true,
188
+ contextWindow: 8192,
189
+ recommendedFor: ["general", "legacy"]
190
+ },
191
+ llama2: {
192
+ supportsToolCalling: false,
193
+ supportsVision: false,
194
+ supportsJsonMode: false,
195
+ contextWindow: 4096,
196
+ recommendedFor: ["legacy"]
197
+ },
198
+ // Mistral family
199
+ mistral: {
200
+ supportsToolCalling: true,
201
+ supportsVision: false,
202
+ supportsJsonMode: true,
203
+ contextWindow: 32768,
204
+ recommendedFor: ["general", "multilingual"]
205
+ },
206
+ "mistral-nemo": {
207
+ supportsToolCalling: true,
208
+ supportsVision: false,
209
+ supportsJsonMode: true,
210
+ contextWindow: 128e3,
211
+ recommendedFor: ["general", "multilingual", "long-context"]
212
+ },
213
+ mixtral: {
214
+ supportsToolCalling: true,
215
+ supportsVision: false,
216
+ supportsJsonMode: true,
217
+ contextWindow: 32768,
218
+ recommendedFor: ["complex", "reasoning", "multilingual"]
219
+ },
220
+ // Phi family
221
+ phi3: {
222
+ supportsToolCalling: false,
223
+ // Most Phi models don't support tools yet
224
+ supportsVision: false,
225
+ supportsJsonMode: true,
226
+ contextWindow: 128e3,
227
+ recommendedFor: ["lightweight", "mobile"]
228
+ },
229
+ "phi3.5": {
230
+ supportsToolCalling: false,
231
+ supportsVision: false,
232
+ supportsJsonMode: true,
233
+ contextWindow: 128e3,
234
+ recommendedFor: ["lightweight", "mobile"]
235
+ },
236
+ "phi4-mini": {
237
+ supportsToolCalling: false,
238
+ supportsVision: false,
239
+ supportsJsonMode: true,
240
+ contextWindow: 128e3,
241
+ recommendedFor: ["lightweight", "mobile", "edge"]
242
+ },
243
+ // Qwen family
244
+ "qwen2.5": {
245
+ supportsToolCalling: true,
246
+ supportsVision: false,
247
+ supportsJsonMode: true,
248
+ contextWindow: 32768,
249
+ recommendedFor: ["general", "multilingual", "coding"]
250
+ },
251
+ "qwen2.5-coder": {
252
+ supportsToolCalling: true,
253
+ supportsVision: false,
254
+ supportsJsonMode: true,
255
+ contextWindow: 32768,
256
+ recommendedFor: ["coding", "development"]
257
+ },
258
+ qwen2: {
259
+ supportsToolCalling: true,
260
+ supportsVision: false,
261
+ supportsJsonMode: true,
262
+ contextWindow: 32768,
263
+ recommendedFor: ["general", "multilingual"]
264
+ },
265
+ // Gemma family
266
+ gemma2: {
267
+ supportsToolCalling: false,
268
+ supportsVision: false,
269
+ supportsJsonMode: true,
270
+ contextWindow: 8192,
271
+ recommendedFor: ["general", "research"]
272
+ },
273
+ gemma: {
274
+ supportsToolCalling: false,
275
+ supportsVision: false,
276
+ supportsJsonMode: true,
277
+ contextWindow: 8192,
278
+ recommendedFor: ["general", "research"]
279
+ },
280
+ // Vision models
281
+ llava: {
282
+ supportsToolCalling: false,
283
+ supportsVision: true,
284
+ supportsJsonMode: true,
285
+ contextWindow: 4096,
286
+ recommendedFor: ["vision", "image-analysis"]
287
+ },
288
+ "minicpm-v": {
289
+ supportsToolCalling: false,
290
+ supportsVision: true,
291
+ supportsJsonMode: true,
292
+ contextWindow: 8192,
293
+ recommendedFor: ["vision", "lightweight"]
294
+ },
295
+ bakllava: {
296
+ supportsToolCalling: false,
297
+ supportsVision: true,
298
+ supportsJsonMode: true,
299
+ contextWindow: 4096,
300
+ recommendedFor: ["vision", "image-analysis"]
301
+ },
302
+ // Coding models
303
+ codellama: {
304
+ supportsToolCalling: false,
305
+ supportsVision: false,
306
+ supportsJsonMode: true,
307
+ contextWindow: 16384,
308
+ recommendedFor: ["coding", "development"]
309
+ },
310
+ "deepseek-coder": {
311
+ supportsToolCalling: false,
312
+ supportsVision: false,
313
+ supportsJsonMode: true,
314
+ contextWindow: 16384,
315
+ recommendedFor: ["coding", "development"]
316
+ },
317
+ // Command-R family (Cohere)
318
+ "command-r": {
319
+ supportsToolCalling: true,
320
+ supportsVision: false,
321
+ supportsJsonMode: true,
322
+ contextWindow: 128e3,
323
+ recommendedFor: ["enterprise", "rag", "reasoning"]
324
+ },
325
+ "command-r-plus": {
326
+ supportsToolCalling: true,
327
+ supportsVision: false,
328
+ supportsJsonMode: true,
329
+ contextWindow: 128e3,
330
+ recommendedFor: ["enterprise", "complex", "rag"]
331
+ },
332
+ // Firefunction
333
+ "firefunction-v2": {
334
+ supportsToolCalling: true,
335
+ supportsVision: false,
336
+ supportsJsonMode: true,
337
+ contextWindow: 32768,
338
+ recommendedFor: ["tools", "function-calling"]
339
+ }
340
+ };
341
+ function getModelCapabilities(modelId) {
342
+ const normalizedId = normalizeModelId(modelId);
343
+ if (MODEL_CAPABILITIES[normalizedId]) {
344
+ return MODEL_CAPABILITIES[normalizedId];
345
+ }
346
+ const baseFamily = getModelFamily(normalizedId);
347
+ if (MODEL_CAPABILITIES[baseFamily]) {
348
+ return MODEL_CAPABILITIES[baseFamily];
349
+ }
350
+ return {
351
+ supportsToolCalling: false,
352
+ supportsVision: false,
353
+ supportsJsonMode: false,
354
+ contextWindow: 4096,
355
+ recommendedFor: ["unknown"]
356
+ };
357
+ }
358
+ function normalizeModelId(modelId) {
359
+ return modelId.toLowerCase().replace(/^ollama\//, "").replace(/:latest$/, "").replace(/-instruct$/, "").replace(/-chat$/, "");
360
+ }
361
+ function getModelFamily(modelId) {
362
+ const normalized = normalizeModelId(modelId);
363
+ if (normalized.includes("llama3.2")) return "llama3.2";
364
+ if (normalized.includes("llama3.1")) return "llama3.1";
365
+ if (normalized.includes("llama3")) return "llama3";
366
+ if (normalized.includes("llama2")) return "llama2";
367
+ if (normalized.includes("mistral")) return "mistral";
368
+ if (normalized.includes("mixtral")) return "mixtral";
369
+ if (normalized.includes("phi4")) return "phi4-mini";
370
+ if (normalized.includes("phi3.5")) return "phi3.5";
371
+ if (normalized.includes("phi3")) return "phi3";
372
+ if (normalized.includes("qwen2.5")) return "qwen2.5";
373
+ if (normalized.includes("qwen2")) return "qwen2";
374
+ if (normalized.includes("gemma2")) return "gemma2";
375
+ if (normalized.includes("gemma")) return "gemma";
376
+ if (normalized.includes("command-r-plus")) return "command-r-plus";
377
+ if (normalized.includes("command-r")) return "command-r";
378
+ if (normalized.includes("firefunction")) return "firefunction-v2";
379
+ if (normalized.includes("codellama")) return "codellama";
380
+ if (normalized.includes("deepseek")) return "deepseek-coder";
381
+ if (normalized.includes("llava")) return "llava";
382
+ if (normalized.includes("minicpm")) return "minicpm-v";
383
+ if (normalized.includes("bakllava")) return "bakllava";
384
+ return normalized;
385
+ }
386
+ function modelSupports(modelId, feature) {
387
+ const capabilities = getModelCapabilities(modelId);
388
+ return Boolean(capabilities[feature]);
389
+ }
390
+ function getModelInfo(modelId) {
391
+ const capabilities = getModelCapabilities(modelId);
392
+ const normalized = normalizeModelId(modelId);
393
+ return {
394
+ modelId: normalized,
395
+ capabilities,
396
+ isSupported: capabilities.recommendedFor[0] !== "unknown"
397
+ };
398
+ }
399
+
400
+ // src/models/chat-language-model.ts
401
+ var OllamaChatLanguageModel = class {
402
+ constructor(modelId, settings, config) {
403
+ this.modelId = modelId;
404
+ this.settings = settings;
405
+ this.config = config;
406
+ }
407
+ specificationVersion = "v2";
408
+ defaultObjectGenerationMode = "json";
409
+ supportsImages = false;
410
+ supportsVideoURLs = false;
411
+ supportsAudioURLs = false;
412
+ supportsVideoFile = false;
413
+ supportsAudioFile = false;
414
+ supportsImageFile = true;
415
+ supportedUrls = {};
416
+ get provider() {
417
+ return this.config.provider;
418
+ }
419
+ get supportsStructuredOutputs() {
420
+ return this.settings.structuredOutputs ?? false;
421
+ }
422
+ getCallOptions(options) {
423
+ const {
424
+ prompt,
425
+ temperature,
426
+ maxOutputTokens,
427
+ topP,
428
+ topK,
429
+ frequencyPenalty,
430
+ presencePenalty,
431
+ stopSequences,
432
+ seed,
433
+ responseFormat,
434
+ tools
435
+ } = options;
436
+ const warnings = [];
437
+ if (tools && tools.length > 0 && !modelSupports(this.modelId, "supportsToolCalling")) {
438
+ throw new Error(
439
+ `Model '${this.modelId}' does not support tool calling. Consider using models like llama3.2, llama3.1, mistral, qwen2.5, or command-r for tool calling features.`
440
+ );
441
+ }
442
+ if (responseFormat?.type === "json" && responseFormat.schema && !this.supportsStructuredOutputs) {
443
+ throw new Error(
444
+ "JSON schema is only supported when structuredOutputs is enabled"
445
+ );
446
+ }
447
+ if (responseFormat?.type === "json" && !modelSupports(this.modelId, "supportsJsonMode")) {
448
+ throw new Error(
449
+ `Model '${this.modelId}' does not support JSON mode. Consider using models like llama3.2, mistral, or qwen2.5 for structured output support.`
450
+ );
451
+ }
452
+ const ollamaTools = tools ? tools.map((tool) => {
453
+ if (tool.type === "function") {
454
+ return {
455
+ type: "function",
456
+ function: {
457
+ name: tool.name,
458
+ description: tool.description,
459
+ parameters: tool.inputSchema
460
+ // Ollama expects a specific schema format
461
+ }
462
+ };
463
+ }
464
+ throw new Error(
465
+ `Provider-defined tools are not supported by Ollama. Use function tools instead.`
466
+ );
467
+ }) : void 0;
468
+ const ollamaOptions = {
469
+ // Start with AI SDK parameters mapped to Ollama names
470
+ ...temperature !== void 0 && { temperature },
471
+ ...maxOutputTokens !== void 0 && { num_predict: maxOutputTokens },
472
+ ...topP !== void 0 && { top_p: topP },
473
+ ...topK !== void 0 && { top_k: topK },
474
+ ...frequencyPenalty !== void 0 && {
475
+ frequency_penalty: frequencyPenalty
476
+ },
477
+ ...presencePenalty !== void 0 && {
478
+ presence_penalty: presencePenalty
479
+ },
480
+ ...stopSequences !== void 0 && { stop: stopSequences },
481
+ ...seed !== void 0 && { seed },
482
+ // Ollama model options override AI SDK parameters
483
+ ...this.settings.options
484
+ };
485
+ for (const key of Object.keys(ollamaOptions)) {
486
+ if (ollamaOptions[key] === void 0) {
487
+ delete ollamaOptions[key];
488
+ }
489
+ }
490
+ let format;
491
+ if (responseFormat?.type === "json") {
492
+ format = "json";
493
+ }
494
+ const messages = convertToOllamaChatMessages(prompt);
495
+ return {
496
+ messages,
497
+ options: ollamaOptions,
498
+ format,
499
+ tools: ollamaTools,
500
+ warnings
501
+ };
502
+ }
503
+ async doGenerate(options) {
504
+ const {
505
+ messages,
506
+ options: ollamaOptions,
507
+ format,
508
+ tools,
509
+ warnings
510
+ } = this.getCallOptions(options);
511
+ try {
512
+ const response = await this.config.client.chat({
513
+ model: this.modelId,
514
+ messages,
515
+ options: ollamaOptions,
516
+ format,
517
+ tools,
518
+ stream: false
519
+ });
520
+ const text = response.message.content;
521
+ const toolCalls = response.message.tool_calls;
522
+ const content = [];
523
+ if (text) {
524
+ content.push({ type: "text", text });
525
+ }
526
+ if (toolCalls && toolCalls.length > 0) {
527
+ for (const toolCall of toolCalls) {
528
+ content.push({
529
+ type: "tool-call",
530
+ toolCallId: crypto.randomUUID(),
531
+ // Ollama doesn't provide IDs
532
+ toolName: toolCall.function.name,
533
+ input: JSON.stringify(toolCall.function.arguments || {})
534
+ });
535
+ }
536
+ }
537
+ return {
538
+ content,
539
+ finishReason: mapOllamaFinishReason(
540
+ response.done_reason
541
+ ),
542
+ usage: {
543
+ inputTokens: response.prompt_eval_count || 0,
544
+ outputTokens: response.eval_count || 0,
545
+ totalTokens: (response.prompt_eval_count || 0) + (response.eval_count || 0)
546
+ },
547
+ providerMetadata: {
548
+ ollama: {
549
+ model: response.model,
550
+ created_at: response.created_at ? new Date(response.created_at).toISOString() : void 0,
551
+ total_duration: response.total_duration,
552
+ load_duration: response.load_duration,
553
+ eval_duration: response.eval_duration
554
+ }
555
+ },
556
+ request: {
557
+ body: JSON.stringify({
558
+ model: this.modelId,
559
+ messages,
560
+ options: ollamaOptions,
561
+ format,
562
+ tools
563
+ })
564
+ },
565
+ response: {
566
+ timestamp: /* @__PURE__ */ new Date(),
567
+ modelId: this.modelId
568
+ },
569
+ warnings
570
+ };
571
+ } catch (error) {
572
+ throw new OllamaError({
573
+ message: error instanceof Error ? error.message : String(error),
574
+ cause: error
575
+ });
576
+ }
577
+ }
578
+ async doStream(options) {
579
+ const {
580
+ messages,
581
+ options: ollamaOptions,
582
+ format,
583
+ tools,
584
+ warnings
585
+ } = this.getCallOptions(options);
586
+ try {
587
+ const stream = await this.config.client.chat({
588
+ model: this.modelId,
589
+ messages,
590
+ options: ollamaOptions,
591
+ format,
592
+ tools,
593
+ stream: true
594
+ });
595
+ let usage = {
596
+ inputTokens: 0,
597
+ outputTokens: 0,
598
+ totalTokens: 0
599
+ };
600
+ let finishReason = "unknown";
601
+ const transformStream = new TransformStream({
602
+ async transform(chunk, controller) {
603
+ if (chunk.done) {
604
+ usage = {
605
+ inputTokens: chunk.prompt_eval_count || 0,
606
+ outputTokens: chunk.eval_count || 0,
607
+ totalTokens: (chunk.prompt_eval_count || 0) + (chunk.eval_count || 0)
608
+ };
609
+ finishReason = mapOllamaFinishReason(
610
+ chunk.done_reason
611
+ );
612
+ controller.enqueue({
613
+ type: "finish",
614
+ finishReason,
615
+ usage
616
+ });
617
+ } else {
618
+ if (chunk.message.tool_calls && chunk.message.tool_calls.length > 0) {
619
+ for (const toolCall of chunk.message.tool_calls) {
620
+ controller.enqueue({
621
+ type: "tool-call",
622
+ toolCallId: crypto.randomUUID(),
623
+ // Ollama doesn't provide IDs
624
+ toolName: toolCall.function.name,
625
+ input: JSON.stringify(toolCall.function.arguments || {})
626
+ });
627
+ }
628
+ } else if (chunk.message.content) {
629
+ controller.enqueue({
630
+ type: "text-delta",
631
+ id: crypto.randomUUID(),
632
+ // Generate unique ID for each text chunk
633
+ delta: chunk.message.content
634
+ });
635
+ }
636
+ }
637
+ }
638
+ });
639
+ const readableStream = new ReadableStream({
640
+ async start(controller) {
641
+ try {
642
+ for await (const chunk of stream) {
643
+ controller.enqueue(chunk);
644
+ }
645
+ controller.close();
646
+ } catch (error) {
647
+ controller.error(error);
648
+ }
649
+ }
650
+ });
651
+ return {
652
+ stream: readableStream.pipeThrough(transformStream),
653
+ rawCall: {
654
+ rawPrompt: messages,
655
+ rawSettings: {
656
+ model: this.modelId,
657
+ options: ollamaOptions,
658
+ format,
659
+ tools
660
+ }
661
+ },
662
+ warnings: warnings.length > 0 ? warnings : void 0
663
+ };
664
+ } catch (error) {
665
+ throw new OllamaError({
666
+ message: error instanceof Error ? error.message : String(error),
667
+ cause: error
668
+ });
669
+ }
670
+ }
671
+ };
672
+
673
+ // src/models/embedding-model.ts
674
+ var OllamaEmbeddingModel = class {
675
+ constructor(modelId, settings, config) {
676
+ this.settings = settings;
677
+ this.config = config;
678
+ this.modelId = modelId;
679
+ }
680
+ specificationVersion = "v2";
681
+ modelId;
682
+ maxEmbeddingsPerCall = 2048;
683
+ supportsParallelCalls = true;
684
+ get provider() {
685
+ return this.config.provider;
686
+ }
687
+ async doEmbed(params) {
688
+ const { values, abortSignal } = params;
689
+ if (values.length > this.maxEmbeddingsPerCall) {
690
+ throw new OllamaError({
691
+ message: `Too many values to embed. Maximum: ${this.maxEmbeddingsPerCall}, Received: ${values.length}`
692
+ });
693
+ }
694
+ if (values.length === 0) {
695
+ return { embeddings: [] };
696
+ }
697
+ try {
698
+ const embeddings = [];
699
+ for (const value of values) {
700
+ if (value === void 0 || value === null) {
701
+ continue;
702
+ }
703
+ const response = await this.config.client.embed({
704
+ model: this.modelId,
705
+ input: value,
706
+ options: this.settings.options
707
+ });
708
+ if (!response.embeddings) {
709
+ throw new OllamaError({
710
+ message: `No embeddings field in response`
711
+ });
712
+ }
713
+ if (response.embeddings.length === 0) {
714
+ throw new OllamaError({
715
+ message: `Empty embeddings array returned`
716
+ });
717
+ }
718
+ embeddings.push(response.embeddings[0]);
719
+ if (abortSignal?.aborted) {
720
+ throw new Error("Embedding generation aborted");
721
+ }
722
+ }
723
+ if (embeddings.length === 0) {
724
+ throw new OllamaError({
725
+ message: `No valid values provided for embedding (all were undefined/null)`
726
+ });
727
+ }
728
+ return {
729
+ embeddings
730
+ };
731
+ } catch (error) {
732
+ if (error instanceof OllamaError) {
733
+ throw error;
734
+ }
735
+ throw new OllamaError({
736
+ message: error instanceof Error ? error.message : String(error),
737
+ cause: error
738
+ });
739
+ }
740
+ }
741
+ };
742
+
743
+ // src/utils/model-suggestions.ts
744
+ function suggestModelsForFeatures(requirements) {
745
+ const suggestions = [];
746
+ if (requirements.toolCalling) {
747
+ suggestions.push(
748
+ {
749
+ modelId: "llama3.2",
750
+ reason: "Excellent tool calling support with good general performance",
751
+ capabilities: getModelCapabilities("llama3.2")
752
+ },
753
+ {
754
+ modelId: "llama3.1:8b",
755
+ reason: "Strong tool calling with larger model for complex tasks",
756
+ capabilities: getModelCapabilities("llama3.1:8b")
757
+ },
758
+ {
759
+ modelId: "mistral",
760
+ reason: "Fast tool calling with multilingual support",
761
+ capabilities: getModelCapabilities("mistral")
762
+ },
763
+ {
764
+ modelId: "qwen2.5",
765
+ reason: "Excellent coding and tool calling capabilities",
766
+ capabilities: getModelCapabilities("qwen2.5")
767
+ }
768
+ );
769
+ }
770
+ if (requirements.vision) {
771
+ suggestions.push(
772
+ {
773
+ modelId: "llama3.2-vision",
774
+ reason: "Best vision model with tool calling support",
775
+ capabilities: getModelCapabilities("llama3.2-vision")
776
+ },
777
+ {
778
+ modelId: "llava",
779
+ reason: "Specialized vision model for image analysis",
780
+ capabilities: getModelCapabilities("llava")
781
+ },
782
+ {
783
+ modelId: "minicpm-v",
784
+ reason: "Lightweight vision model for edge deployments",
785
+ capabilities: getModelCapabilities("minicpm-v")
786
+ }
787
+ );
788
+ }
789
+ if (requirements.performance === "fast") {
790
+ suggestions.push(
791
+ {
792
+ modelId: "llama3.2:1b",
793
+ reason: "Fastest model with modern features",
794
+ capabilities: getModelCapabilities("llama3.2:1b")
795
+ },
796
+ {
797
+ modelId: "phi4-mini",
798
+ reason: "Optimized for speed and low resource usage",
799
+ capabilities: getModelCapabilities("phi4-mini")
800
+ }
801
+ );
802
+ } else if (requirements.performance === "quality") {
803
+ suggestions.push(
804
+ {
805
+ modelId: "llama3.1:70b",
806
+ reason: "Highest quality responses for complex tasks",
807
+ capabilities: getModelCapabilities("llama3.1:70b")
808
+ },
809
+ {
810
+ modelId: "mixtral",
811
+ reason: "Excellent reasoning and multilingual capabilities",
812
+ capabilities: getModelCapabilities("mixtral")
813
+ }
814
+ );
815
+ }
816
+ if (requirements.contextSize === "large") {
817
+ suggestions.push(
818
+ {
819
+ modelId: "llama3.2",
820
+ reason: "128K context window for long documents",
821
+ capabilities: getModelCapabilities("llama3.2")
822
+ },
823
+ {
824
+ modelId: "mistral-nemo",
825
+ reason: "128K context with excellent multilingual support",
826
+ capabilities: getModelCapabilities("mistral-nemo")
827
+ }
828
+ );
829
+ }
830
+ const uniqueSuggestions = suggestions.filter(
831
+ (suggestion, index, array) => array.findIndex((s) => s.modelId === suggestion.modelId) === index
832
+ );
833
+ return uniqueSuggestions.filter((suggestion) => {
834
+ if (requirements.toolCalling && !suggestion.capabilities.supportsToolCalling) {
835
+ return false;
836
+ }
837
+ if (requirements.vision && !suggestion.capabilities.supportsVision) {
838
+ return false;
839
+ }
840
+ if (requirements.jsonMode && !suggestion.capabilities.supportsJsonMode) {
841
+ return false;
842
+ }
843
+ return true;
844
+ });
845
+ }
846
+ function getFeatureNotSupportedMessage(modelId, feature) {
847
+ const suggestions = suggestModelsForFeatures({ [feature]: true });
848
+ const topSuggestions = suggestions.slice(0, 3);
849
+ const featureNames = {
850
+ toolCalling: "tool calling",
851
+ vision: "vision/image analysis",
852
+ jsonMode: "JSON mode"
853
+ };
854
+ let message = `Model '${modelId}' does not support ${featureNames[feature]}.`;
855
+ if (topSuggestions.length > 0) {
856
+ message += `
857
+
858
+ Recommended models for ${featureNames[feature]}:`;
859
+ for (const suggestion of topSuggestions) {
860
+ message += `
861
+ \u2022 ${suggestion.modelId} - ${suggestion.reason}`;
862
+ }
863
+ }
864
+ return message;
865
+ }
866
+ function validateModelConfiguration(modelId, options) {
867
+ const capabilities = getModelCapabilities(modelId);
868
+ const issues = [];
869
+ const suggestions = [];
870
+ if (options.hasTools && !capabilities.supportsToolCalling) {
871
+ issues.push(`Tool calling not supported by '${modelId}'`);
872
+ const toolModels = suggestModelsForFeatures({ toolCalling: true }).slice(
873
+ 0,
874
+ 2
875
+ );
876
+ suggestions.push(
877
+ `For tool calling, consider: ${toolModels.map((m) => m.modelId).join(", ")}`
878
+ );
879
+ }
880
+ if (options.hasImages && !capabilities.supportsVision) {
881
+ issues.push(`Vision/image analysis not supported by '${modelId}'`);
882
+ const visionModels = suggestModelsForFeatures({ vision: true }).slice(0, 2);
883
+ suggestions.push(
884
+ `For vision tasks, consider: ${visionModels.map((m) => m.modelId).join(", ")}`
885
+ );
886
+ }
887
+ if (options.needsJsonMode && !capabilities.supportsJsonMode) {
888
+ issues.push(`JSON mode may not work reliably with '${modelId}'`);
889
+ const jsonModels = suggestModelsForFeatures({ jsonMode: true }).slice(0, 2);
890
+ suggestions.push(
891
+ `For structured output, consider: ${jsonModels.map((m) => m.modelId).join(", ")}`
892
+ );
893
+ }
894
+ if (options.expectedTokens && options.expectedTokens > capabilities.contextWindow) {
895
+ issues.push(
896
+ `Expected tokens (${options.expectedTokens}) exceed model context window (${capabilities.contextWindow})`
897
+ );
898
+ const largeContextModels = suggestModelsForFeatures({
899
+ contextSize: "large"
900
+ }).slice(0, 2);
901
+ suggestions.push(
902
+ `For large context, consider: ${largeContextModels.map((m) => m.modelId).join(", ")}`
903
+ );
904
+ }
905
+ return {
906
+ isValid: issues.length === 0,
907
+ issues,
908
+ suggestions,
909
+ capabilities
910
+ };
911
+ }
912
+
913
+ // src/utils/validate-model.ts
914
+ function validateModel(modelId, context) {
915
+ const validation = validateModelConfiguration(modelId, context || {});
916
+ return validation;
917
+ }
918
+ function getModelStatus(modelId) {
919
+ const capabilities = getModelCapabilities(modelId);
920
+ const status = {
921
+ isKnown: capabilities.recommendedFor[0] !== "unknown",
922
+ features: {
923
+ toolCalling: capabilities.supportsToolCalling,
924
+ vision: capabilities.supportsVision,
925
+ jsonMode: capabilities.supportsJsonMode
926
+ },
927
+ contextWindow: capabilities.contextWindow,
928
+ recommendedFor: capabilities.recommendedFor,
929
+ isLegacy: capabilities.recommendedFor.includes("legacy")
930
+ };
931
+ return status;
932
+ }
933
+
934
+ // src/provider.ts
935
+ function createOllama(options = {}) {
936
+ const client = new ollama$1.Ollama({
937
+ host: options.baseURL,
938
+ fetch: options.fetch,
939
+ headers: options.headers
940
+ });
941
+ const createChatModel = (modelId, settings = {}) => {
942
+ validateModel(modelId);
943
+ return new OllamaChatLanguageModel(modelId, settings, {
944
+ client,
945
+ provider: "ollama"
946
+ });
947
+ };
948
+ const createEmbeddingModel = (modelId, settings = {}) => {
949
+ return new OllamaEmbeddingModel(modelId, settings, {
950
+ client,
951
+ provider: "ollama"
952
+ });
953
+ };
954
+ const provider$1 = function(modelId, settings) {
955
+ if (new.target) {
956
+ throw new Error(
957
+ "The Ollama provider cannot be called with the new keyword."
958
+ );
959
+ }
960
+ return createChatModel(modelId, settings);
961
+ };
962
+ provider$1.chat = createChatModel;
963
+ provider$1.languageModel = createChatModel;
964
+ provider$1.embedding = createEmbeddingModel;
965
+ provider$1.textEmbedding = createEmbeddingModel;
966
+ provider$1.textEmbeddingModel = createEmbeddingModel;
967
+ provider$1.imageModel = (modelId) => {
968
+ throw new provider.NoSuchModelError({
969
+ modelId,
970
+ modelType: "imageModel",
971
+ message: "Image generation is not supported by Ollama"
972
+ });
973
+ };
974
+ return provider$1;
975
+ }
976
+ var ollama = createOllama();
977
+
978
+ exports.OllamaChatLanguageModel = OllamaChatLanguageModel;
979
+ exports.OllamaEmbeddingModel = OllamaEmbeddingModel;
980
+ exports.OllamaError = OllamaError;
981
+ exports.createOllama = createOllama;
982
+ exports.getFeatureNotSupportedMessage = getFeatureNotSupportedMessage;
983
+ exports.getModelCapabilities = getModelCapabilities;
984
+ exports.getModelInfo = getModelInfo;
985
+ exports.getModelStatus = getModelStatus;
986
+ exports.modelSupports = modelSupports;
987
+ exports.ollama = ollama;
988
+ exports.suggestModelsForFeatures = suggestModelsForFeatures;
989
+ exports.validateModel = validateModel;
990
+ //# sourceMappingURL=index.cjs.map
991
+ //# sourceMappingURL=index.cjs.map