Skip to content
Closed
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion core/llm/llms/Anthropic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import {
} from "../../index.js";
import { safeParseToolCallArgs } from "../../tools/parseArgs.js";
import { renderChatMessage, stripImages } from "../../util/messageContent.js";
import { extractBase64FromDataUrl } from "../../util/url.js";
import { DEFAULT_REASONING_TOKENS } from "../constants.js";
import { BaseLLM } from "../index.js";

Expand Down Expand Up @@ -107,7 +108,7 @@ class Anthropic extends BaseLLM {
source: {
type: "base64",
media_type: getAnthropicMediaTypeFromDataUrl(part.imageUrl.url),
data: part.imageUrl.url.split(",")[1],
data: extractBase64FromDataUrl(part.imageUrl.url),
},
};
});
Expand Down
3 changes: 2 additions & 1 deletion core/llm/llms/Bedrock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import type { CompletionOptions } from "../../index.js";
import { ChatMessage, Chunk, LLMOptions, MessageContent } from "../../index.js";
import { safeParseToolCallArgs } from "../../tools/parseArgs.js";
import { renderChatMessage, stripImages } from "../../util/messageContent.js";
import { parseDataUrl } from "../../util/url.js";
import { BaseLLM } from "../index.js";
import { PROVIDER_TOOL_SUPPORT } from "../toolSupport.js";
import { getSecureID } from "../utils/getSecureID.js";
Expand Down Expand Up @@ -546,7 +547,7 @@ class Bedrock extends BaseLLM {
blocks.push({ text: part.text });
} else if (part.type === "imageUrl" && part.imageUrl) {
try {
const [mimeType, base64Data] = part.imageUrl.url.split(",");
const { mimeType, base64Data } = parseDataUrl(part.imageUrl.url);
const format = mimeType.split("/")[1]?.split(";")[0] || "jpeg";
if (
format === ImageFormat.JPEG ||
Expand Down
3 changes: 2 additions & 1 deletion core/llm/llms/Gemini.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
} from "../../index.js";
import { safeParseToolCallArgs } from "../../tools/parseArgs.js";
import { renderChatMessage, stripImages } from "../../util/messageContent.js";
import { extractBase64FromDataUrl } from "../../util/url.js";
import { BaseLLM } from "../index.js";
import {
GeminiChatContent,
Expand Down Expand Up @@ -191,7 +192,7 @@ class Gemini extends BaseLLM {
: {
inlineData: {
mimeType: "image/jpeg",
data: part.imageUrl?.url.split(",")[1],
data: part.imageUrl?.url ? extractBase64FromDataUrl(part.imageUrl.url) : ""
},
};
}
Expand Down
3 changes: 2 additions & 1 deletion core/llm/llms/Ollama.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
} from "../../index.js";
import { renderChatMessage } from "../../util/messageContent.js";
import { getRemoteModelInfo } from "../../util/ollamaHelper.js";
import { extractBase64FromDataUrl } from "../../util/url.js";
import { BaseLLM } from "../index.js";

type OllamaChatMessage = {
Expand Down Expand Up @@ -303,7 +304,7 @@ class Ollama extends BaseLLM implements ModelInstaller {
const images: string[] = [];
message.content.forEach((part) => {
if (part.type === "imageUrl" && part.imageUrl) {
const image = part.imageUrl?.url.split(",").at(-1);
const image = part.imageUrl?.url ? extractBase64FromDataUrl(part.imageUrl.url) : undefined
if (image) {
images.push(image);
}
Expand Down
31 changes: 31 additions & 0 deletions core/util/url.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,34 @@ export function canParseUrl(url: string): boolean {
return false;
}
}

export function parseDataUrl(dataUrl: string): {
mimeType: string;
base64Data: string
} {
const urlParts = dataUrl.split(",");

if(urlParts.length < 2 ) {
throw new Error("Invalid data URL format: expected 'data:type;base64,data' format")
}

const [mimeType, ...base64Parts] = urlParts;
const base64Data = base64Parts.join(",");

return { mimeType, base64Data }
}

export function extractBase64FromDataUrl(dataUrl: string): string {
return parseDataUrl(dataUrl).base64Data
}

export function safeSplit(input: string, delimiter: string, expectedParts: number, errorContext: string = "input") : string[] {

const parts = input.split(delimiter);

if(parts.length !== expectedParts) {
throw new Error(`Invalid ${errorContext} format: expected ${expectedParts} parts separated by "${delimiter}", got ${parts.length}`)
}

return parts;
}
3 changes: 2 additions & 1 deletion packages/openai-adapters/src/apis/Anthropic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import {
usageChatChunk,
} from "../util.js";
import { EMPTY_CHAT_COMPLETION } from "../util/emptyChatCompletion.js";
import { extractBase64FromDataUrl } from "../../../../core/util/url.js"
import { safeParseArgs } from "../util/parseArgs.js";
import {
CACHING_STRATEGIES,
Expand Down Expand Up @@ -199,7 +200,7 @@ export class AnthropicApi implements BaseLlmApi {
source: {
type: "base64",
media_type: getAnthropicMediaTypeFromDataUrl(dataUrl),
data: dataUrl.split(",")[1],
data: extractBase64FromDataUrl(dataUrl);
},
});
}
Expand Down
8 changes: 5 additions & 3 deletions packages/openai-adapters/src/apis/Bedrock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,9 @@ import {
} from "openai/resources/index";

import { fromNodeProviderChain } from "@aws-sdk/credential-providers";

import { fromStatic } from "@aws-sdk/token-providers";
import { parseDataUrl } from "../../../../core/util/url.js";
import { BedrockConfig } from "../types.js";
import { chatChunk, chatChunkFromDelta, embedding, rerank } from "../util.js";
import { safeParseArgs } from "../util/parseArgs.js";
Expand Down Expand Up @@ -135,9 +137,9 @@ export class BedrockApi implements BaseLlmApi {
case "image_url":
default:
try {
const [mimeType, base64Data] = (
part as ChatCompletionContentPartImage
).image_url.url.split(",");
const { mimeType, base64Data } = parseDataUrl(
(part as ChatCompletionContentPartImage).image_url.url,
);
const format = mimeType.split("/")[1]?.split(";")[0] || "jpeg";
if (
format === ImageFormat.JPEG ||
Expand Down
Loading