import type { Message } from "../types/message";
export interface ExtractedImage {
url: string;
alt?: string;
}
/**
* Extracts images from a message's content or result.
* Handles both JSON array format (MsgResult[]) and Rich Text (Markdown/HTML).
*
* @param result - The content or result field from a message
* @returns Array of extracted images
*/
export const extractImagesFromResult = (result: unknown): ExtractedImage[] => {
const images: ExtractedImage[] = [];
if (!result) return images;
// Case 0: result IS the message content which might be an array directly
if (Array.isArray(result)) {
result.forEach((item) => {
if (typeof item === "object" && item !== null) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const msgItem = item as any;
// 1. Check for standard OpenAI-like image_url
if (msgItem.image_url && typeof msgItem.image_url === "object" && msgItem.image_url.url) {
images.push({
url: msgItem.image_url.url,
alt: "Attached Image",
});
}
// 2. Check for type="image" with source
if (msgItem.type === "image" && msgItem.source && typeof msgItem.source === "object") {
const source = msgItem.source;
if (source.data) {
const mimeType = source.media_type || "image/png";
images.push({
url: `data:${mimeType};base64,${source.data}`,
alt: "Base64 Image",
});
} else if (source.url) {
images.push({
url: source.url,
alt: "Image URL",
});
}
}
}
});
}
// Case 2: result is a string (Rich Text / Markdown)
if (typeof result === "string") {
// 1. Match Markdown images: 
const markdownRegex = /!\[([^\]]*)\]\(([^)]+)\)/g;
let match;
while ((match = markdownRegex.exec(result)) !== null) {
images.push({
alt: match[1] || "Markdown Image",
url: match[2],
});
}
// 2. Match HTML img tags:
const htmlRegex = /
]*src=["']([^"']+)["'][^>]*>/g;
while ((match = htmlRegex.exec(result)) !== null) {
images.push({
alt: "HTML Image",
url: match[1],
});
}
// 3. Match JSON "image_url": "..." patterns embedded in text
// Matches "image_url"\s*:\s*"([^"]+)"
const jsonRegex = /"image_url"\s*:\s*"([^"]+)"/g;
while ((match = jsonRegex.exec(result)) !== null) {
// Basic filtering to avoid matching non-URL strings if the key is reused
if (match[1].startsWith("http") || match[1].startsWith("data:")) {
images.push({
alt: "JSON Image",
url: match[1],
});
}
}
}
return images;
};
/**
* Helper to extract images from a Message object
*/
export const extractImagesFromMessage = (message: Message): ExtractedImage[] => {
if (!message.content) return [];
// If content is a string, treat it as result
if (typeof message.content === "string") {
return extractImagesFromResult(message.content);
}
// If content is an object (MessageContent)
if (typeof message.content === "object") {
// Check 'result' field
if ("result" in message.content && message.content.result) {
return extractImagesFromResult(message.content.result);
}
// Also check if content itself is an array (e.g. standard MessageContent array)
if (Array.isArray(message.content)) {
return extractImagesFromResult(message.content);
}
}
return [];
};