contaigents
Version:
Modular AI Content Ecosystem with Audio Generation
286 lines (277 loc) ⢠12.5 kB
JavaScript
import { BaseTool } from './BaseTool.js';
import { ImageGenerator } from '../imageGenerator.js';
import { LLMFactory } from '../llm/LLMFactory.js';
import path from 'path';
import fs from 'fs/promises';
export class ImageGenerationTool extends BaseTool {
constructor(baseDir) {
super();
this.baseDir = baseDir || process.cwd();
}
getName() {
return 'image_generation';
}
getDescription() {
return 'Generate stunning, high-quality images from detailed text prompts using AI providers (Gemini, OpenAI). IMPORTANT: Always create rich, descriptive, and creative prompts with specific details about style, lighting, mood, composition, colors, and artistic techniques for optimal results. Transform simple user requests into elaborate, professional-quality prompts.';
}
getAgentGuidance() {
return `
## šØ IMAGE GENERATION BEST PRACTICES
When using the image_generation tool, you MUST create RICH, DETAILED, and CREATIVE prompts for optimal results:
### ⨠PROMPT ENHANCEMENT REQUIREMENTS:
- **Be Highly Descriptive**: Use vivid adjectives, specific details, and sensory language
- **Set the Complete Scene**: Include environment, lighting, mood, atmosphere, and context
- **Specify Artistic Style**: Mention specific art styles, techniques, or visual aesthetics
- **Add Compositional Details**: Include camera angles, framing, perspective, and focal points
- **Use Quality Enhancers**: Add professional photography/art terms for better results
### š PROMPT TRANSFORMATION EXAMPLES:
ā **AVOID Simple Prompts**: "A cat sitting"
ā
**USE Rich Prompts**: "A majestic Maine Coon cat with piercing emerald eyes and silver-tipped fur, sitting regally on an ornate Victorian velvet cushion in deep burgundy, soft golden hour sunlight streaming through delicate lace curtains, creating warm shadows and highlights, captured in the style of a Renaissance portrait with rich textures and dramatic chiaroscuro lighting"
ā **AVOID Basic Requests**: "A mountain landscape"
ā
**USE Detailed Descriptions**: "A breathtaking alpine landscape at dawn, with ethereal mist rolling through ancient pine forests, a mirror-like glacial lake reflecting snow-capped peaks painted in shades of rose and gold, dramatic cumulus clouds catching the first light, captured with the precision and grandeur of Ansel Adams' photography, sharp detail throughout, perfect composition with leading lines"
### š STYLE ENHANCEMENT VOCABULARY:
- **Photography**: "professional photography, 4K HDR, studio lighting, bokeh effect, golden hour, dramatic shadows"
- **Art Movements**: "impressionist brushstrokes, Art Nouveau elegance, Baroque drama, minimalist composition"
- **Mood & Atmosphere**: "ethereal and dreamlike, dramatic and moody, serene and peaceful, vibrant and energetic"
- **Technical Terms**: "shallow depth of field, rule of thirds, leading lines, color grading, texture detail"
### š” AGENT INSTRUCTIONS:
1. **Always expand user requests** into detailed, creative prompts
2. **Add artistic context** and technical specifications
3. **Include mood and atmosphere** descriptions
4. **Specify lighting and composition** details
5. **Reference art styles or techniques** when appropriate
6. **Use sensory and emotional language** to enhance visual impact
Remember: The quality of the generated image directly correlates with the richness and creativity of your prompt!
`;
}
getParameters() {
return [
{
name: 'prompt',
type: 'string',
description: 'DETAILED, creative text prompt describing the image to generate. Must be rich and descriptive with specific details about subject, artistic style, lighting, mood, composition, colors, and techniques. Transform basic user requests into elaborate, professional-quality prompts for best results.',
required: true
},
{
name: 'output_path',
type: 'string',
description: 'Output file path (relative to project root)',
required: true
},
{
name: 'provider',
type: 'string',
description: 'AI provider for image generation (default: "gemini")',
required: false,
default: 'gemini'
},
{
name: 'model',
type: 'string',
description: 'Specific model to use (provider-dependent)',
required: false
},
{
name: 'width',
type: 'number',
description: 'Image width in pixels (optional)',
required: false
},
{
name: 'height',
type: 'number',
description: 'Image height in pixels (optional)',
required: false
},
{
name: 'style',
type: 'string',
description: 'Image style (e.g., "photographic", "digital_art", "anime")',
required: false
},
{
name: 'quality',
type: 'string',
description: 'Image quality (e.g., "standard", "hd")',
required: false
}
];
}
async execute(parameters) {
// Validate parameters
const validationError = this.validateParameters(parameters);
if (validationError) {
return validationError;
}
const { prompt, output_path, provider = 'gemini', model, width, height, style, quality } = parameters;
try {
// Validate prompt
if (!prompt || typeof prompt !== 'string' || !prompt.trim()) {
return {
success: false,
error: 'Prompt cannot be empty'
};
}
// Get configured LLM provider for API key
const llmProvider = await this.getLLMProviderForImage(provider);
if (!llmProvider) {
return {
success: false,
error: `No configured ${provider} provider found. Please configure the ${provider} provider using 'contaigents configure' command.`
};
}
// Validate and resolve output path
const pathValidation = await this.validateAndResolveOutputPath(output_path);
if (!pathValidation.success) {
return {
success: false,
error: pathValidation.error
};
}
// Initialize ImageGenerator with API key from LLM config
const imageGenerator = new ImageGenerator({
provider,
apiKey: llmProvider.apiKey,
model
});
// Validate model if specified
if (model && !imageGenerator.isModelAvailable(model)) {
const availableModels = imageGenerator.getAvailableModels();
return {
success: false,
error: `Model "${model}" is not available for provider "${provider}". Available models: ${availableModels.join(', ')}`
};
}
// Prepare generation options
const options = {};
if (width && height) {
options.width = width;
options.height = height;
}
if (style)
options.style = style;
if (quality)
options.quality = quality;
// Generate image
console.log(`šØ Generating image with ${provider} using model: ${imageGenerator.getModel()}`);
const imageBuffer = await imageGenerator.generateImage(prompt, options);
// Save image file
const savedPath = await imageGenerator.saveImageToFile(pathValidation.resolvedPath, imageBuffer);
// Get file stats
const fileStats = await fs.stat(savedPath);
const fileSize = fileStats.size;
return {
success: true,
data: {
output_file_path: path.relative(this.baseDir, savedPath),
absolute_path: savedPath,
file_size: fileSize,
provider,
model: imageGenerator.getModel(),
prompt_length: prompt.length,
mime_type: imageGenerator.getLastMimeType(),
dimensions: width && height ? `${width}x${height}` : 'auto',
style: style || 'default',
quality: quality || 'standard'
},
message: `Image generated successfully. Saved to: ${path.relative(this.baseDir, savedPath)} (${this.formatFileSize(fileSize)})`
};
}
catch (error) {
return {
success: false,
error: `Failed to generate image: ${error.message}`
};
}
}
/**
* Get configured LLM provider for image generation
*/
async getLLMProviderForImage(provider) {
try {
// Map image provider names to LLM provider names
const llmProviderName = this.mapImageProviderToLLM(provider);
// Get the configured LLM provider
const llmProvider = LLMFactory.getProvider(llmProviderName.toLowerCase());
// Check if it's configured
if (!(await llmProvider.isConfigured())) {
return null;
}
// Get the configuration
const config = llmProvider.getConfig();
// Return the API key
return {
apiKey: config.apiKey
};
}
catch (error) {
console.error(`Failed to get LLM provider for image: ${error}`);
return null;
}
}
/**
* Map image provider names to LLM provider names
*/
mapImageProviderToLLM(imageProvider) {
switch (imageProvider.toLowerCase()) {
case 'gemini':
case 'google':
return 'Google';
case 'openai':
return 'OpenAI';
default:
// Try to use the provider name as-is (capitalize first letter)
return imageProvider.charAt(0).toUpperCase() + imageProvider.slice(1).toLowerCase();
}
}
/**
* Validate and resolve output path with security checks
*/
async validateAndResolveOutputPath(outputPath) {
try {
// Normalize the path to prevent directory traversal
const normalizedPath = path.normalize(outputPath);
// Check for directory traversal attempts
if (normalizedPath.includes('..') || path.isAbsolute(normalizedPath)) {
return {
success: false,
error: 'Output path must be relative and cannot contain ".." or be absolute for security reasons'
};
}
// Resolve full path
const fullPath = path.join(this.baseDir, normalizedPath);
// Ensure the resolved path is still within the base directory
const relativePath = path.relative(this.baseDir, fullPath);
if (relativePath.startsWith('..')) {
return {
success: false,
error: 'Output path resolves outside of project directory'
};
}
// Create parent directories if they don't exist
const parentDir = path.dirname(fullPath);
await fs.mkdir(parentDir, { recursive: true });
return {
success: true,
resolvedPath: fullPath
};
}
catch (error) {
return {
success: false,
error: `Failed to resolve output path: ${error.message}`
};
}
}
/**
* Format file size in human-readable format
*/
formatFileSize(bytes) {
if (bytes === 0)
return '0 B';
const k = 1024;
const sizes = ['B', 'KB', 'MB', 'GB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + ' ' + sizes[i];
}
}