ai-sdk-ollama 3.7.0 → 3.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # Changelog
2
2
 
3
+ ## 3.7.1
4
+
5
+ ### Patch Changes
6
+
7
+ - 70def97: Support media/image content in tool result messages. When a tool returns `output.type === 'content'` with `image-data`, `image-url`, or `file-data` (image) parts, the provider now sends them in the tool message's `images` array to Ollama. Fixes #527.
8
+
3
9
  ## 3.7.0
4
10
 
5
11
  ### Minor Changes
@@ -375,6 +375,29 @@ ${finalInstruction}`;
375
375
  }
376
376
 
377
377
  // src/utils/convert-to-ollama-messages.ts
378
+ function normalizeImageDataForOllama(imageData) {
379
+ if (imageData instanceof URL) {
380
+ if (imageData.protocol === "data:") {
381
+ const base64Match = imageData.href.match(/data:[^;]+;base64,(.+)/);
382
+ const extracted = base64Match?.[1];
383
+ if (typeof extracted === "string") return extracted;
384
+ return imageData.href;
385
+ }
386
+ return imageData.href;
387
+ }
388
+ if (typeof imageData === "string") {
389
+ if (imageData.startsWith("data:")) {
390
+ const base64Match = imageData.match(/data:[^;]+;base64,(.+)/);
391
+ const extracted = base64Match?.[1];
392
+ if (typeof extracted === "string") return extracted;
393
+ }
394
+ return imageData;
395
+ }
396
+ if (imageData instanceof Uint8Array) {
397
+ return Buffer.from(imageData).toString("base64");
398
+ }
399
+ return null;
400
+ }
378
401
  function convertToOllamaChatMessages(prompt) {
379
402
  const messages = [];
380
403
  for (const message of prompt) {
@@ -396,35 +419,7 @@ function convertToOllamaChatMessages(prompt) {
396
419
  const textParts = message.content.filter((part) => part.type === "text").map((part) => part.text).join("\n");
397
420
  const imageParts = message.content.filter(
398
421
  (part) => part.type === "file"
399
- ).filter((part) => {
400
- return part.mediaType?.startsWith("image/") || false;
401
- }).map((part) => {
402
- const imageData = part.data;
403
- if (imageData instanceof URL) {
404
- if (imageData.protocol === "data:") {
405
- const base64Match = imageData.href.match(
406
- /data:[^;]+;base64,(.+)/
407
- );
408
- if (base64Match) {
409
- return base64Match[1];
410
- }
411
- return imageData.href;
412
- }
413
- return imageData.href;
414
- } else if (typeof imageData === "string") {
415
- if (imageData.startsWith("data:")) {
416
- const base64Match = imageData.match(/data:[^;]+;base64,(.+)/);
417
- if (base64Match) {
418
- return base64Match[1];
419
- }
420
- }
421
- return imageData;
422
- } else if (imageData instanceof Uint8Array) {
423
- return Buffer.from(imageData).toString("base64");
424
- } else {
425
- return null;
426
- }
427
- }).filter((img) => img !== null);
422
+ ).filter((part) => part.mediaType?.startsWith("image/") ?? false).map((part) => normalizeImageDataForOllama(part.data)).filter((img) => img !== null);
428
423
  messages.push({
429
424
  role: "user",
430
425
  content: textParts || "",
@@ -473,14 +468,48 @@ function convertToOllamaChatMessages(prompt) {
473
468
  });
474
469
  } else {
475
470
  for (const part of message.content) {
476
- if (part.type === "tool-result") {
477
- const contentValue = part.output.type === "text" || part.output.type === "error-text" ? part.output.value : part.output.type === "json" || part.output.type === "error-json" ? JSON.stringify(part.output.value) : JSON.stringify(part.output);
471
+ if (part.type !== "tool-result") continue;
472
+ if (part.output.type === "content") {
473
+ const textParts = [];
474
+ const imageParts = [];
475
+ for (const item of part.output.value) {
476
+ switch (item.type) {
477
+ case "text": {
478
+ textParts.push(item.text);
479
+ break;
480
+ }
481
+ case "image-data": {
482
+ const normalized = normalizeImageDataForOllama(item.data);
483
+ if (normalized) imageParts.push(normalized);
484
+ break;
485
+ }
486
+ case "image-url": {
487
+ imageParts.push(item.url);
488
+ break;
489
+ }
490
+ case "file-data": {
491
+ if (item.mediaType?.startsWith("image/")) {
492
+ const normalized = normalizeImageDataForOllama(item.data);
493
+ if (normalized) imageParts.push(normalized);
494
+ }
495
+ break;
496
+ }
497
+ }
498
+ }
478
499
  messages.push({
479
500
  role: "tool",
480
- content: contentValue,
481
- tool_name: part.toolName
501
+ content: textParts.join("\n") || "",
502
+ tool_name: part.toolName,
503
+ images: imageParts.length > 0 ? imageParts : void 0
482
504
  });
505
+ continue;
483
506
  }
507
+ const contentValue = part.output.type === "text" || part.output.type === "error-text" ? part.output.value : part.output.type === "json" || part.output.type === "error-json" ? JSON.stringify(part.output.value) : part.output.type === "execution-denied" ? "" : JSON.stringify(part.output);
508
+ messages.push({
509
+ role: "tool",
510
+ content: contentValue,
511
+ tool_name: part.toolName
512
+ });
484
513
  }
485
514
  }
486
515
  break;