levisnkyyyy-images-mcp
Version:
Model Context Protocol server for AI image and video generation using LiteLLM and fal.ai
1,101 lines (1,100 loc) • 67.1 kB
JavaScript
#!/usr/bin/env node
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const index_js_1 = require("@modelcontextprotocol/sdk/server/index.js");
const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js");
const types_js_1 = require("@modelcontextprotocol/sdk/types.js");
const promises_1 = __importDefault(require("fs/promises"));
const path_1 = __importDefault(require("path"));
const client_1 = require("@fal-ai/client");
const LITELLM_URL = process.env.LITELLM_URL || "http://litellm:4000";
const LITELLM_KEY = process.env.LITELLM_KEY || "";
const FAL_API_KEY = process.env.FAL_API_KEY || "";
const FS_PREFIX = process.env.FS_PREFIX || "";
// Helper function to remove file extension from image/video name
function removeFileExtension(filename) {
// Remove common image and video file extensions
return filename.replace(/\.(png|jpg|jpeg|gif|bmp|webp|svg|tiff|tif|ico|mp4|avi|mov|wmv|flv|mkv|webm|m4v|mpg|mpeg)$/i, '');
}
// Configure fal client with API key
if (FAL_API_KEY) {
client_1.fal.config({
credentials: FAL_API_KEY,
});
}
const server = new index_js_1.Server({
name: "images-mcp",
version: "1.0.0",
}, {
capabilities: {
tools: {},
},
});
server.setRequestHandler(types_js_1.ListToolsRequestSchema, async () => {
return {
tools: [
{
name: "openai_image_generation",
description: "Generate an image using OpenAI's DALL-E model",
inputSchema: {
type: "object",
properties: {
prompt: {
type: "string",
description: "The text prompt to generate an image from",
},
project_folder: {
type: "string",
description: "Path to the folder where generated images will be saved",
},
image_name: {
type: "string",
description: "Base filename for the generated image(s) (without extension)",
},
size: {
type: "string",
description: "The size of the generated image",
enum: ["256x256", "512x512", "1024x1024"],
default: "1024x1024",
},
n: {
type: "number",
description: "Number of images to generate",
minimum: 1,
maximum: 4,
default: 1,
},
},
required: ["prompt", "project_folder", "image_name"],
},
},
{
name: "openai_image_edit",
description: "Edit an existing image based on a prompt using OpenAI's DALL-E model",
inputSchema: {
type: "object",
properties: {
image_path: {
type: "string",
description: "Full path to the image file to edit",
},
mask: {
type: "string",
description: "Base64 encoded mask where edits should be applied",
},
prompt: {
type: "string",
description: "The text prompt describing how to edit the image",
},
project_folder: {
type: "string",
description: "Path to the folder where edited images will be saved",
},
image_name: {
type: "string",
description: "Base filename for the edited image(s) (without extension)",
},
size: {
type: "string",
description: "The size of the edited image",
enum: ["256x256", "512x512", "1024x1024"],
default: "1024x1024",
},
n: {
type: "number",
description: "Number of edited images to generate",
minimum: 1,
maximum: 4,
default: 1,
},
},
required: ["image_path", "prompt", "project_folder", "image_name"],
},
},
{
name: "imagen_image_generation",
description: "Generate an image using Google's Imagen 3.0 model",
inputSchema: {
type: "object",
properties: {
prompt: {
type: "string",
description: "The text prompt to generate an image from",
},
project_folder: {
type: "string",
description: "Path to the folder where generated images will be saved",
},
image_name: {
type: "string",
description: "Base filename for the generated image(s) (without extension)",
},
size: {
type: "string",
description: "The size of the generated image",
enum: ["1024x1024", "1536x1536"],
default: "1024x1024",
},
n: {
type: "number",
description: "Number of images to generate",
minimum: 1,
maximum: 4,
default: 1,
},
},
required: ["prompt", "project_folder", "image_name"],
},
},
{
name: "imagen4_image_generation",
description: "Generate an image using Google's Imagen 4.0 model",
inputSchema: {
type: "object",
properties: {
prompt: {
type: "string",
description: "The text prompt to generate an image from",
},
project_folder: {
type: "string",
description: "Path to the folder where generated images will be saved",
},
image_name: {
type: "string",
description: "Base filename for the generated image(s) (without extension)",
},
size: {
type: "string",
description: "The size of the generated image",
enum: ["1024x1024", "1536x1536", "2048x2048"],
default: "1024x1024",
},
n: {
type: "number",
description: "Number of images to generate",
minimum: 1,
maximum: 4,
default: 1,
},
},
required: ["prompt", "project_folder", "image_name"],
},
},
{
name: "imagen4_ultra_image_generation",
description: "Generate an image using Google's Imagen 4.0 Ultra model",
inputSchema: {
type: "object",
properties: {
prompt: {
type: "string",
description: "The text prompt to generate an image from",
},
project_folder: {
type: "string",
description: "Path to the folder where generated images will be saved",
},
image_name: {
type: "string",
description: "Base filename for the generated image(s) (without extension)",
},
size: {
type: "string",
description: "The size of the generated image",
enum: ["1024x1024", "1536x1536", "2048x2048", "4096x4096"],
default: "2048x2048",
},
n: {
type: "number",
description: "Number of images to generate",
minimum: 1,
maximum: 4,
default: 1,
},
},
required: ["prompt", "project_folder", "image_name"],
},
},
{
name: "flux_pro_image_generation",
description: "Generate an image using fal.ai's Flux Pro model",
inputSchema: {
type: "object",
properties: {
prompt: {
type: "string",
description: "The text prompt to generate an image from",
},
project_folder: {
type: "string",
description: "Path to the folder where generated images will be saved",
},
image_name: {
type: "string",
description: "Base filename for the generated image(s) (without extension)",
},
width: {
type: "number",
description: "Width of the generated image",
minimum: 256,
maximum: 2048,
default: 1024,
},
height: {
type: "number",
description: "Height of the generated image",
minimum: 256,
maximum: 2048,
default: 1024,
},
num_inference_steps: {
type: "number",
description: "Number of inference steps",
minimum: 1,
maximum: 100,
default: 25,
},
guidance_scale: {
type: "number",
description: "Guidance scale for generation",
minimum: 1,
maximum: 20,
default: 7.5,
},
},
required: ["prompt", "project_folder", "image_name"],
},
},
{
name: "flux_max_image_generation",
description: "Generate an image using fal.ai's Flux Max model",
inputSchema: {
type: "object",
properties: {
prompt: {
type: "string",
description: "The text prompt to generate an image from",
},
project_folder: {
type: "string",
description: "Path to the folder where generated images will be saved",
},
image_name: {
type: "string",
description: "Base filename for the generated image(s) (without extension)",
},
guidance_scale: {
type: "number",
description: "Guidance scale for generation",
minimum: 1,
maximum: 20,
default: 3.5,
},
aspect_ratio: {
type: "string",
description: "Aspect ratio of the generated image",
enum: ["1:1", "16:9", "9:16", "4:3", "3:4"],
default: "1:1",
},
safety_tolerance: {
type: "string",
description: "Safety tolerance level",
enum: ["1", "2", "3", "4", "5"],
default: "2",
},
},
required: ["prompt", "project_folder", "image_name"],
},
},
{
name: "flux_pro_image_edit",
description: "Edit an existing image using fal.ai's Flux Pro image-to-image model",
inputSchema: {
type: "object",
properties: {
image_path: {
type: "string",
description: "Full path to the image file to edit",
},
prompt: {
type: "string",
description: "The text prompt describing how to edit the image",
},
project_folder: {
type: "string",
description: "Path to the folder where edited images will be saved",
},
image_name: {
type: "string",
description: "Base filename for the edited image(s) (without extension)",
},
guidance_scale: {
type: "number",
description: "Guidance scale for generation",
minimum: 1,
maximum: 20,
default: 3.5,
},
safety_tolerance: {
type: "string",
description: "Safety tolerance level",
enum: ["1", "2", "3", "4", "5"],
default: "2",
},
},
required: ["image_path", "prompt", "project_folder", "image_name"],
},
},
{
name: "veo3_video_generation",
description: "Generate a video using fal.ai's Veo3 model",
inputSchema: {
type: "object",
properties: {
prompt: {
type: "string",
description: "The text prompt to generate a video from",
},
project_folder: {
type: "string",
description: "Path to the folder where generated video will be saved",
},
video_name: {
type: "string",
description: "Base filename for the generated video (without extension)",
},
duration: {
type: "number",
description: "Duration of the video in seconds",
minimum: 1,
maximum: 10,
default: 8,
},
aspect_ratio: {
type: "string",
description: "Aspect ratio of the video",
enum: ["16:9", "9:16", "1:1"],
default: "16:9",
},
enhance_prompt: {
type: "boolean",
description: "Whether to enhance the prompt",
default: true,
},
generate_audio: {
type: "boolean",
description: "Whether to generate audio for the video",
default: true,
},
},
required: ["prompt", "project_folder", "video_name"],
},
},
{
name: "kling_video_image_to_video",
description: "Generate a video from an image using Kling Video v2 model",
inputSchema: {
type: "object",
properties: {
image_path: {
type: "string",
description: "Full path to the image file to convert to video",
},
prompt: {
type: "string",
description: "The text prompt describing the video generation from the image",
},
project_folder: {
type: "string",
description: "Path to the folder where generated video will be saved",
},
video_name: {
type: "string",
description: "Base filename for the generated video (without extension)",
},
duration: {
type: "string",
description: "Duration of the video in seconds",
enum: ["5", "10"],
default: "5",
},
aspect_ratio: {
type: "string",
description: "Aspect ratio of the video",
enum: ["16:9", "9:16", "1:1"],
default: "16:9",
},
negative_prompt: {
type: "string",
description: "What to avoid in the video generation",
},
cfg_scale: {
type: "number",
description: "Classifier-free guidance scale",
minimum: 0,
maximum: 1,
default: 0.5,
},
},
required: ["image_path", "prompt", "project_folder", "video_name"],
},
},
{
name: "seedance_video_generation",
description: "Generate a video using ByteDance's SeedDance v1 Lite text-to-video model",
inputSchema: {
type: "object",
properties: {
prompt: {
type: "string",
description: "The text prompt to generate a video from",
},
project_folder: {
type: "string",
description: "Path to the folder where generated video will be saved",
},
video_name: {
type: "string",
description: "Base filename for the generated video (without extension)",
},
aspect_ratio: {
type: "string",
description: "Aspect ratio of the video",
enum: ["16:9", "4:3", "1:1", "9:21"],
default: "16:9",
},
resolution: {
type: "string",
description: "Resolution of the video",
enum: ["480p", "720p"],
default: "720p",
},
duration: {
type: "string",
description: "Duration of the video in seconds",
enum: ["5", "10"],
default: "5",
},
},
required: ["prompt", "project_folder", "video_name"],
},
},
{
name: "seedance_image_to_video",
description: "Generate a video from an image using ByteDance's SeedDance v1 Lite image-to-video model",
inputSchema: {
type: "object",
properties: {
image_path: {
type: "string",
description: "Full path to the image file to convert to video",
},
prompt: {
type: "string",
description: "The text prompt describing the video generation from the image",
},
project_folder: {
type: "string",
description: "Path to the folder where generated video will be saved",
},
video_name: {
type: "string",
description: "Base filename for the generated video (without extension)",
},
resolution: {
type: "string",
description: "Resolution of the video",
enum: ["480p", "720p"],
default: "720p",
},
duration: {
type: "string",
description: "Duration of the video in seconds",
enum: ["5", "10"],
default: "5",
},
},
required: ["image_path", "prompt", "project_folder", "video_name"],
},
},
],
};
});
server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
const toolName = request.params.name;
if (toolName === "openai_image_generation") {
const { prompt, project_folder, image_name: rawImageName, size = "1024x1024", n = 1 } = request.params.arguments;
const image_name = removeFileExtension(rawImageName);
try {
const response = await fetch(`${LITELLM_URL}/v1/images/generations`, {
method: "POST",
headers: {
"Content-Type": "application/json",
...(LITELLM_KEY && { "Authorization": `Bearer ${LITELLM_KEY}` }),
},
body: JSON.stringify({
model: "gpt-image-1-openai",
prompt,
size,
n,
response_format: "b64_json",
}),
});
if (!response.ok) {
const errorText = await response.text();
throw new Error(`API request failed: ${response.status} - ${errorText}`);
}
const data = await response.json();
// Ensure the project folder exists
// await fs.mkdir(project_folder, { recursive: true });
// Save each generated image
const savedImages = [];
for (let i = 0; i < data.data.length; i++) {
const imageData = data.data[i];
// Extract base64 data from URL or b64_json
let base64Data;
console.log(`Received image data: ${Object.keys(imageData)}`);
if (imageData.b64_json) {
base64Data = imageData.b64_json;
}
else if (imageData.url && imageData.url.startsWith('data:image')) {
// Extract base64 from data URL
base64Data = imageData.url.split(',')[1];
}
else if (imageData.url) {
// If it's a regular URL, fetch the image
const imgResponse = await fetch(imageData.url);
const buffer = await imgResponse.arrayBuffer();
base64Data = Buffer.from(buffer).toString('base64');
}
else {
throw new Error('No image data found in response');
}
// Determine filename
const filename = n > 1 ? `${image_name}_${i + 1}.png` : `${image_name}.png`;
const folder = FS_PREFIX ? path_1.default.join(FS_PREFIX, project_folder) : project_folder;
const filepath = path_1.default.join(folder, filename);
await promises_1.default.access(folder, promises_1.default.constants.W_OK);
// Save the image
await promises_1.default.writeFile(filepath, Buffer.from(base64Data, 'base64'));
savedImages.push(filename);
}
return {
content: [
{
type: "text",
text: `Successfully generated and saved ${n} image(s) with prompt: "${prompt}"`,
},
{
type: "text",
text: `Saved to: ${savedImages.join(', ')}`,
},
],
};
}
catch (error) {
return {
content: [
{
type: "text",
text: `Error generating image: ${error instanceof Error ? error.message : String(error)}`,
},
{
type: "text",
text: `Stack trace: ${error instanceof Error ? error.stack : 'No stack trace available'}`,
},
],
isError: true,
};
}
}
else if (toolName === "openai_image_edit") {
const { image_path, mask, prompt, project_folder, image_name: rawImageName, size = "1024x1024", n = 1 } = request.params.arguments;
const image_name = removeFileExtension(rawImageName);
try {
// Read the image file and convert to base64
const fullImagePath = FS_PREFIX ? path_1.default.join(FS_PREFIX, image_path) : image_path;
const imageBuffer = await promises_1.default.readFile(fullImagePath);
const imageBase64 = imageBuffer.toString('base64');
const response = await fetch(`${LITELLM_URL}/v1/images/edits`, {
method: "POST",
headers: {
"Content-Type": "application/json",
...(LITELLM_KEY && { "Authorization": `Bearer ${LITELLM_KEY}` }),
},
body: JSON.stringify({
model: "gpt-image-1-openai",
image: imageBase64,
mask,
prompt,
size,
n,
response_format: "b64_json",
}),
});
if (!response.ok) {
const errorText = await response.text();
throw new Error(`API request failed: ${response.status} - ${errorText}`);
}
const data = await response.json();
// Ensure the project folder exists
const folder = FS_PREFIX ? path_1.default.join(FS_PREFIX, project_folder) : project_folder;
await promises_1.default.mkdir(folder, { recursive: true });
// Save each edited image
const savedImages = [];
for (let i = 0; i < data.data.length; i++) {
const imageData = data.data[i];
// Extract base64 data from URL or b64_json
let base64Data;
if (imageData.b64_json) {
base64Data = imageData.b64_json;
}
else if (imageData.url && imageData.url.startsWith('data:image')) {
// Extract base64 from data URL
base64Data = imageData.url.split(',')[1];
}
else if (imageData.url) {
// If it's a regular URL, fetch the image
const imgResponse = await fetch(imageData.url);
const buffer = await imgResponse.arrayBuffer();
base64Data = Buffer.from(buffer).toString('base64');
}
else {
throw new Error('No image data found in response');
}
// Determine filename
const filename = n > 1 ? `${image_name}_${i + 1}.png` : `${image_name}.png`;
const filepath = path_1.default.join(folder, filename);
// Save the image
await promises_1.default.writeFile(filepath, Buffer.from(base64Data, 'base64'));
savedImages.push(filename);
}
return {
content: [
{
type: "text",
text: `Successfully edited and saved ${n} image(s) with prompt: "${prompt}"`,
},
{
type: "text",
text: `Saved to: ${savedImages.join(', ')}`,
},
],
};
}
catch (error) {
return {
content: [
{
type: "text",
text: `Error editing image: ${error instanceof Error ? error.message : String(error)}`,
},
{
type: "text",
text: `Stack trace: ${error instanceof Error ? error.stack : 'No stack trace available'}`,
},
],
isError: true,
};
}
}
else if (toolName === "imagen_image_generation") {
const { prompt, project_folder, image_name: rawImageName, size = "1024x1024", n = 1 } = request.params.arguments;
const image_name = removeFileExtension(rawImageName);
try {
const response = await fetch(`${LITELLM_URL}/v1/images/generations`, {
method: "POST",
headers: {
"Content-Type": "application/json",
...(LITELLM_KEY && { "Authorization": `Bearer ${LITELLM_KEY}` }),
},
body: JSON.stringify({
model: "imagen-3.0-generate-002",
prompt,
size,
n,
response_format: "b64_json",
}),
});
if (!response.ok) {
const errorText = await response.text();
throw new Error(`API request failed: ${response.status} - ${errorText}`);
}
const data = await response.json();
// Save each generated image
const savedImages = [];
for (let i = 0; i < data.data.length; i++) {
const imageData = data.data[i];
// Extract base64 data from URL or b64_json
let base64Data;
console.log(`Received image data: ${Object.keys(imageData)}`);
if (imageData.b64_json) {
base64Data = imageData.b64_json;
}
else if (imageData.url && imageData.url.startsWith('data:image')) {
// Extract base64 from data URL
base64Data = imageData.url.split(',')[1];
}
else if (imageData.url) {
// If it's a regular URL, fetch the image
const imgResponse = await fetch(imageData.url);
const buffer = await imgResponse.arrayBuffer();
base64Data = Buffer.from(buffer).toString('base64');
}
else {
throw new Error('No image data found in response');
}
// Determine filename
const filename = n > 1 ? `${image_name}_${i + 1}.png` : `${image_name}.png`;
const folder = FS_PREFIX ? path_1.default.join(FS_PREFIX, project_folder) : project_folder;
const filepath = path_1.default.join(folder, filename);
await promises_1.default.access(folder, promises_1.default.constants.W_OK);
// Save the image
await promises_1.default.writeFile(filepath, Buffer.from(base64Data, 'base64'));
savedImages.push(filename);
}
return {
content: [
{
type: "text",
text: `Successfully generated and saved ${n} image(s) with Imagen 3.0 using prompt: "${prompt}"`,
},
{
type: "text",
text: `Saved to: ${savedImages.join(', ')}`,
},
],
};
}
catch (error) {
return {
content: [
{
type: "text",
text: `Error generating image with Imagen: ${error instanceof Error ? error.message : String(error)}`,
},
{
type: "text",
text: `Stack trace: ${error instanceof Error ? error.stack : 'No stack trace available'}`,
},
],
isError: true,
};
}
}
else if (toolName === "imagen4_image_generation") {
const { prompt, project_folder, image_name: rawImageName, size = "1024x1024", n = 1 } = request.params.arguments;
const image_name = removeFileExtension(rawImageName);
try {
const response = await fetch(`${LITELLM_URL}/v1/images/generations`, {
method: "POST",
headers: {
"Content-Type": "application/json",
...(LITELLM_KEY && { "Authorization": `Bearer ${LITELLM_KEY}` }),
},
body: JSON.stringify({
model: "imagen-4.0-generate",
prompt,
size,
n,
response_format: "b64_json",
}),
});
if (!response.ok) {
const errorText = await response.text();
throw new Error(`API request failed: ${response.status} - ${errorText}`);
}
const data = await response.json();
// Save each generated image
const savedImages = [];
for (let i = 0; i < data.data.length; i++) {
const imageData = data.data[i];
// Extract base64 data from URL or b64_json
let base64Data;
console.log(`Received image data: ${Object.keys(imageData)}`);
if (imageData.b64_json) {
base64Data = imageData.b64_json;
}
else if (imageData.url && imageData.url.startsWith('data:image')) {
// Extract base64 from data URL
base64Data = imageData.url.split(',')[1];
}
else if (imageData.url) {
// If it's a regular URL, fetch the image
const imgResponse = await fetch(imageData.url);
const buffer = await imgResponse.arrayBuffer();
base64Data = Buffer.from(buffer).toString('base64');
}
else {
throw new Error('No image data found in response');
}
// Determine filename
const filename = n > 1 ? `${image_name}_${i + 1}.png` : `${image_name}.png`;
const folder = FS_PREFIX ? path_1.default.join(FS_PREFIX, project_folder) : project_folder;
const filepath = path_1.default.join(folder, filename);
await promises_1.default.access(folder, promises_1.default.constants.W_OK);
// Save the image
await promises_1.default.writeFile(filepath, Buffer.from(base64Data, 'base64'));
savedImages.push(filename);
}
return {
content: [
{
type: "text",
text: `Successfully generated and saved ${n} image(s) with Imagen 4.0 using prompt: "${prompt}"`,
},
{
type: "text",
text: `Saved to: ${savedImages.join(', ')}`,
},
],
};
}
catch (error) {
return {
content: [
{
type: "text",
text: `Error generating image with Imagen 4.0: ${error instanceof Error ? error.message : String(error)}`,
},
{
type: "text",
text: `Stack trace: ${error instanceof Error ? error.stack : 'No stack trace available'}`,
},
],
isError: true,
};
}
}
else if (toolName === "imagen4_ultra_image_generation") {
const { prompt, project_folder, image_name: rawImageName, size = "2048x2048", n = 1 } = request.params.arguments;
const image_name = removeFileExtension(rawImageName);
try {
const response = await fetch(`${LITELLM_URL}/v1/images/generations`, {
method: "POST",
headers: {
"Content-Type": "application/json",
...(LITELLM_KEY && { "Authorization": `Bearer ${LITELLM_KEY}` }),
},
body: JSON.stringify({
model: "imagen-4.0-ultra-generate",
prompt,
size,
n,
response_format: "b64_json",
}),
});
if (!response.ok) {
const errorText = await response.text();
throw new Error(`API request failed: ${response.status} - ${errorText}`);
}
const data = await response.json();
// Save each generated image
const savedImages = [];
for (let i = 0; i < data.data.length; i++) {
const imageData = data.data[i];
// Extract base64 data from URL or b64_json
let base64Data;
console.log(`Received image data: ${Object.keys(imageData)}`);
if (imageData.b64_json) {
base64Data = imageData.b64_json;
}
else if (imageData.url && imageData.url.startsWith('data:image')) {
// Extract base64 from data URL
base64Data = imageData.url.split(',')[1];
}
else if (imageData.url) {
// If it's a regular URL, fetch the image
const imgResponse = await fetch(imageData.url);
const buffer = await imgResponse.arrayBuffer();
base64Data = Buffer.from(buffer).toString('base64');
}
else {
throw new Error('No image data found in response');
}
// Determine filename
const filename = n > 1 ? `${image_name}_${i + 1}.png` : `${image_name}.png`;
const folder = FS_PREFIX ? path_1.default.join(FS_PREFIX, project_folder) : project_folder;
const filepath = path_1.default.join(folder, filename);
await promises_1.default.access(folder, promises_1.default.constants.W_OK);
// Save the image
await promises_1.default.writeFile(filepath, Buffer.from(base64Data, 'base64'));
savedImages.push(filename);
}
return {
content: [
{
type: "text",
text: `Successfully generated and saved ${n} image(s) with Imagen 4.0 Ultra using prompt: "${prompt}"`,
},
{
type: "text",
text: `Saved to: ${savedImages.join(', ')}`,
},
],
};
}
catch (error) {
return {
content: [
{
type: "text",
text: `Error generating image with Imagen 4.0 Ultra: ${error instanceof Error ? error.message : String(error)}`,
},
{
type: "text",
text: `Stack trace: ${error instanceof Error ? error.stack : 'No stack trace available'}`,
},
],
isError: true,
};
}
}
else if (toolName === "flux_pro_image_generation") {
const { prompt, project_folder, image_name: rawImageName, width = 1024, height = 1024, num_inference_steps = 25, guidance_scale = 7.5 } = request.params.arguments;
const image_name = removeFileExtension(rawImageName);
try {
if (!FAL_API_KEY) {
throw new Error("FAL_API_KEY environment variable is not set. Please set it to use fal.ai models.");
}
console.log('entered flux');
const result = await client_1.fal.run("fal-ai/flux-pro", {
input: {
prompt,
image_size: {
width,
height,
},
num_inference_steps,
guidance_scale,
},
});
console.log('received data', JSON.stringify(result, null, 2));
// Extract image data from result - handle fal.ai response structure
const resultData = result.data || result;
const images = resultData.images || (resultData.image ? [resultData.image] : [resultData]);
// Save each generated image
const savedImages = [];
for (let i = 0; i < images.length; i++) {
const imageData = images[i];
console.log('Processing image data:', JSON.stringify(imageData, null, 2));
// Get the URL - handle different response structures
let imageUrl;
if (typeof imageData === 'string') {
imageUrl = imageData;
}
else if (imageData.url) {
imageUrl = imageData.url;
}
else if (resultData.url) {
imageUrl = resultData.url;
}
else {
throw new Error(`No URL found in image data: ${JSON.stringify(result)}`);
}
// Fetch the image from URL
const imgResponse = await fetch(imageUrl);
const buffer = await imgResponse.arrayBuffer();
// Determine filename
const filename = images.length > 1 ? `${image_name}_${i + 1}.png` : `${image_name}.png`;
const folder = FS_PREFIX ? path_1.default.join(FS_PREFIX, project_folder) : project_folder;
const filepath = path_1.default.join(folder, filename);
await promises_1.default.access(folder, promises_1.default.constants.W_OK);
// Save the image
await promises_1.default.writeFile(filepath, Buffer.from(buffer));
savedImages.push(filename);
}
return {
content: [
{
type: "text",
text: `Successfully generated and saved ${images.length} image(s) with Flux Pro using prompt: "${prompt}"`,
},
{
type: "text",
text: `Saved to: ${savedImages.join(', ')}`,
},
],
};
}
catch (error) {
return {
content: [
{
type: "text",
text: `Error generating image with Flux Pro: ${error instanceof Error ? error.message : String(error)}`,
},
{
type: "text",
text: `Stack trace: ${error instanceof Error ? error.stack : 'No stack trace available'}`,
},
],
isError: true,
};
}
}
else if (toolName === "flux_max_image_generation") {
const { prompt, project_folder, image_name: rawImageName, guidance_scale = 3.5, aspect_ratio = "1:1", safety_tolerance = "2" } = request.params.arguments;
const image_name = removeFileExtension(rawImageName);
try {
if (!FAL_API_KEY) {
throw new Error("FAL_API_KEY environment variable is not set. Please set it to use fal.ai models.");
}
const result = await client_1.fal.run("fal-ai/flux-pro/kontext/max/text-to-image", {
input: {
prompt,
guidance_scale,
num_images: 1,
safety_tolerance,
output_format: "jpeg",
aspect_ratio,
},
});
console.log('Flux Max result:', JSON.stringify(result, null, 2));
// Extract image data from result - handle different response structures
let images = [];
if (result.data?.images) {
images = result.data.images;
}
else if (result.images) {
images = result.images;
}
else if (result.image) {
images = [result.image];
}
else if (result.url) {
images = [{ url: result.url }];
}
else {
console.error('Unexpected result structure:', JSON.stringify(result, null, 2));
throw new Error(`Unexpected response structure from Flux Max: ${JSON.stringify(result)}`);
}
// Save each generated image
const savedImages = [];
for (let i = 0; i < images.length; i++) {
const imageData = images[i];
// Get the URL - handle different response structures
let imageUrl;
if (typeof imageData === 'string') {
imageUrl = imageData;
}
else if (imageData.url) {
imageUrl = imageData.url;
}
else {
throw new Error(`No URL found in image data at index ${i}: ${JSON.stringify(imageData)}`);
}
// Fetch the image from URL
const imgResponse = await fetch(imageUrl);
const buffer = await imgResponse.arrayBuffer();
// Determine filename
const filename = images.length > 1 ? `${image_name}_${i + 1}.jpg` : `${image_name}.jpg`;
const folder = FS_PREFIX ? path_1.default.join(FS_PREFIX, project_folder) : project_folder;
const filepath = path_1.default.join(folder, filename);
await promises_1.default.access(folder, promises_1.default.constants.W_OK);
// Save the image
await promises_1.default.writeFile(filepath, Buffer.from(buffer));