import { t } from "../../../i18n" // Image generation types interface ImageGenerationResponse { choices?: Array<{ message?: { content?: string images?: Array<{ type?: string image_url?: { url?: string } }> } }> error?: { message?: string type?: string code?: string } } interface ImagesApiResponse { data?: Array<{ b64_json?: string url?: string }> error?: { message?: string type?: string code?: string } } export interface ImageGenerationResult { success: boolean imageData?: string imageFormat?: string error?: string } interface ImageGenerationOptions { baseURL: string authToken: string model: string prompt: string inputImage?: string } interface ImagesApiOptions { baseURL: string authToken: string model: string prompt: string inputImage?: string size?: string quality?: string outputFormat?: string } /** * Shared image generation implementation for OpenRouter and Roo Code Cloud providers */ export async function generateImageWithProvider(options: ImageGenerationOptions): Promise { const { baseURL, authToken, model, prompt, inputImage } = options try { const response = await fetch(`${baseURL}/chat/completions`, { method: "POST", headers: { Authorization: `Bearer ${authToken}`, "Content-Type": "application/json", "HTTP-Referer": "https://github.com/RooVetGit/Roo-Code", "X-Title": "Roo Code", }, body: JSON.stringify({ model, messages: [ { role: "user", content: inputImage ? [ { type: "text", text: prompt, }, { type: "image_url", image_url: { url: inputImage, }, }, ] : prompt, }, ], modalities: ["image", "text"], }), }) if (!response.ok) { const errorText = await response.text() let errorMessage = t("tools:generateImage.failedWithStatus", { status: response.status, statusText: response.statusText, }) try { const errorJson = JSON.parse(errorText) if (errorJson.error?.message) { errorMessage = t("tools:generateImage.failedWithMessage", { message: errorJson.error.message, }) } } catch { // Use default error message } return { success: false, error: errorMessage, } } const result: ImageGenerationResponse = await response.json() if (result.error) { return { success: false, error: t("tools:generateImage.failedWithMessage", { message: result.error.message, }), } } // Extract the generated image from the response const images = result.choices?.[0]?.message?.images if (!images || images.length === 0) { return { success: false, error: t("tools:generateImage.noImageGenerated"), } } const imageData = images[0]?.image_url?.url if (!imageData) { return { success: false, error: t("tools:generateImage.invalidImageData"), } } // Extract base64 data from data URL const base64Match = imageData.match(/^data:image\/(png|jpeg|jpg);base64,(.+)$/) if (!base64Match) { return { success: false, error: t("tools:generateImage.invalidImageFormat"), } } return { success: true, imageData: imageData, imageFormat: base64Match[1], } } catch (error) { return { success: false, error: error instanceof Error ? error.message : t("tools:generateImage.unknownError"), } } } /** * Generate an image using OpenAI's Images API (/v1/images/generations) * Supports BFL models (Flux) with provider-specific options for image editing */ export async function generateImageWithImagesApi(options: ImagesApiOptions): Promise { const { baseURL, authToken, model, prompt, inputImage, outputFormat = "png" } = options try { const url = `${baseURL}/images/generations` // Build the request body // For BFL models, inputImage is passed via providerOptions.blackForestLabs.inputImage const requestBody: Record = { model, prompt, n: 1, } // Add optional parameters if (options.size) { requestBody.size = options.size } if (options.quality) { requestBody.quality = options.quality } // For BFL (Black Forest Labs) models like flux-pro-1.1, use providerOptions if (model.startsWith("bfl/")) { requestBody.providerOptions = { blackForestLabs: { outputFormat: outputFormat, // inputImage: Base64 encoded image or URL of image to use as reference ...(inputImage && { inputImage }), }, } } else { // For other models, use standard output_format parameter requestBody.output_format = outputFormat } const fetchOptions: RequestInit = { method: "POST", headers: { Authorization: `Bearer ${authToken}`, "Content-Type": "application/json", "HTTP-Referer": "https://github.com/RooVetGit/Roo-Code", "X-Title": "Roo Code", }, body: JSON.stringify(requestBody), } const response = await fetch(url, fetchOptions) if (!response.ok) { const errorText = await response.text() let errorMessage = t("tools:generateImage.failedWithStatus", { status: response.status, statusText: response.statusText, }) try { const errorJson = JSON.parse(errorText) if (errorJson.error?.message) { errorMessage = t("tools:generateImage.failedWithMessage", { message: errorJson.error.message, }) } } catch { // Use default error message } return { success: false, error: errorMessage, } } const result: ImagesApiResponse = await response.json() if (result.error) { return { success: false, error: t("tools:generateImage.failedWithMessage", { message: result.error.message, }), } } // Extract the generated image from the response const images = result.data if (!images || images.length === 0) { return { success: false, error: t("tools:generateImage.noImageGenerated"), } } const imageItem = images[0] // Handle b64_json response (most common) if (imageItem?.b64_json) { // Convert base64 to data URL const dataUrl = `data:image/${outputFormat};base64,${imageItem.b64_json}` return { success: true, imageData: dataUrl, imageFormat: outputFormat, } } // Handle URL response (fallback) if (imageItem?.url) { // If it's already a data URL, use it directly if (imageItem.url.startsWith("data:image/")) { const formatMatch = imageItem.url.match(/^data:image\/(\w+);/) const format = formatMatch?.[1] || outputFormat return { success: true, imageData: imageItem.url, imageFormat: format, } } // For external URLs, return as-is (the caller will need to handle fetching) return { success: true, imageData: imageItem.url, imageFormat: outputFormat, } } return { success: false, error: t("tools:generateImage.invalidImageData"), } } catch (error) { return { success: false, error: error instanceof Error ? error.message : t("tools:generateImage.unknownError"), } } }