UNPKG

claude-gemini-multimodal-bridge

Version:

Enterprise-grade AI integration bridge connecting Claude Code, Gemini CLI, and Google AI Studio with intelligent routing and advanced multimodal processing capabilities

531 lines (530 loc) 20.8 kB
import { MultimodalProcess } from '../tools/multimodalProcess.js'; import { logger } from '../utils/logger.js'; import { safeExecute } from '../utils/errorHandler.js'; import { BaseWorkflow } from './BaseWorkflow.js'; import path from 'path'; export class ConversionWorkflow extends BaseWorkflow { multimodalProcess; SUPPORTED_CONVERSIONS = { document: { from: ['.pdf', '.doc', '.docx', '.txt', '.md', '.rtf', '.odt'], to: ['.pdf', '.docx', '.txt', '.md', '.html'], }, image: { from: ['.png', '.jpg', '.jpeg', '.gif', '.bmp', '.webp', '.tiff'], to: ['.png', '.jpg', '.jpeg', '.gif', '.bmp', '.webp', '.svg'], }, audio: { from: ['.mp3', '.wav', '.m4a', '.flac', '.aac', '.ogg'], to: ['.mp3', '.wav', '.m4a', '.flac'], }, data: { from: ['.csv', '.xlsx', '.json', '.xml', '.yaml'], to: ['.csv', '.xlsx', '.json', '.xml', '.yaml'], }, presentation: { from: ['.ppt', '.pptx', '.odp'], to: ['.pptx', '.pdf', '.html'], }, }; constructor(id) { super('conversion', 600000, id); this.multimodalProcess = new MultimodalProcess(); } async executeDocumentConversion(files, targetFormat, options) { return safeExecute(async () => { logger.info('Starting document conversion workflow', { fileCount: files.length, targetFormat, workflowId: this.id, }); this.validateConversion('document', files, targetFormat); const workflow = this.createDocumentConversionWorkflow(files, targetFormat, options); return await this.orchestrator.executeWorkflow(workflow); }, { operationName: 'document-conversion-workflow', layer: 'claude', timeout: this.timeout, }); } async executeImageConversion(files, targetFormat, options) { return safeExecute(async () => { logger.info('Starting image conversion workflow', { fileCount: files.length, targetFormat, workflowId: this.id, }); this.validateConversion('image', files, targetFormat); const workflow = this.createImageConversionWorkflow(files, targetFormat, options); return await this.orchestrator.executeWorkflow(workflow); }, { operationName: 'image-conversion-workflow', layer: 'claude', timeout: this.timeout, }); } async executeAudioConversion(files, targetFormat, options) { return safeExecute(async () => { logger.info('Starting audio conversion workflow', { fileCount: files.length, targetFormat, workflowId: this.id, }); this.validateConversion('audio', files, targetFormat); const workflow = this.createAudioConversionWorkflow(files, targetFormat, options); return await this.orchestrator.executeWorkflow(workflow); }, { operationName: 'audio-conversion-workflow', layer: 'claude', timeout: this.timeout, }); } async executeDataConversion(files, targetFormat, options) { return safeExecute(async () => { logger.info('Starting data conversion workflow', { fileCount: files.length, targetFormat, workflowId: this.id, }); this.validateConversion('data', files, targetFormat); const workflow = this.createDataConversionWorkflow(files, targetFormat, options); return await this.orchestrator.executeWorkflow(workflow); }, { operationName: 'data-conversion-workflow', layer: 'claude', timeout: this.timeout, }); } async executeBatchConversion(conversions) { return safeExecute(async () => { logger.info('Starting batch conversion workflow', { conversionCount: conversions.length, workflowId: this.id, }); const workflow = this.createBatchConversionWorkflow(conversions); return await this.orchestrator.executeWorkflow(workflow); }, { operationName: 'batch-conversion-workflow', layer: 'claude', timeout: this.timeout * conversions.length, }); } async executeContentExtractionConversion(files, extractionType, targetFormat, options) { return safeExecute(async () => { logger.info('Starting content extraction conversion workflow', { fileCount: files.length, extractionType, targetFormat, workflowId: this.id, }); const workflow = this.createContentExtractionConversionWorkflow(files, extractionType, targetFormat, options); return await this.orchestrator.executeWorkflow(workflow); }, { operationName: 'content-extraction-conversion-workflow', layer: 'claude', timeout: this.timeout, }); } validateInputs(inputs) { if (!inputs.files || !Array.isArray(inputs.files) || inputs.files.length === 0) { return false; } if (!inputs.targetFormat || typeof inputs.targetFormat !== 'string') { return false; } for (const file of inputs.files) { if (!file.path || typeof file.path !== 'string') { return false; } const ext = path.extname(file.path).toLowerCase(); if (!this.isSupportedSourceFormat(ext)) { return false; } } return true; } estimateResourceRequirements(inputs) { const fileCount = inputs.files?.length || 0; const totalSize = inputs.files?.reduce((sum, file) => sum + (file.size || 0), 0) || 0; const complexity = this.assessConversionComplexity(inputs.files, inputs.options); const memory = 1024; const cpu = 1.5; const duration = 120000; const cost = 0.02; const sizeMultiplier = Math.min(totalSize / (10 * 1024 * 1024), 10); const countMultiplier = Math.min(fileCount * 0.5, 5); const complexityMultipliers = { low: { estimated_tokens: 1, complexity_score: 1, estimated_duration: 1, estimated_cost: 1 }, medium: { estimated_tokens: 1.5, complexity_score: 1.3, estimated_duration: 1.5, estimated_cost: 1.3 }, high: { estimated_tokens: 2.5, complexity_score: 2, estimated_duration: 2.5, estimated_cost: 2 }, }; const multiplier = complexityMultipliers[complexity]; return { estimated_tokens: memory * Math.max(sizeMultiplier, countMultiplier) * multiplier.estimated_tokens, complexity_score: cpu * multiplier.complexity_score, estimated_duration: duration * Math.max(sizeMultiplier, countMultiplier) * multiplier.estimated_duration, recommended_execution_mode: 'adaptive', required_capabilities: ['claude', 'gemini', 'aistudio'], estimated_cost: cost * multiplier.estimated_cost, }; } validateConversion(conversionType, files, targetFormat) { const supported = this.SUPPORTED_CONVERSIONS[conversionType]; let normalizedTarget = targetFormat; if (!normalizedTarget.startsWith('.')) { normalizedTarget = '.' + normalizedTarget; } if (!supported.to.includes(normalizedTarget)) { throw new Error(`Unsupported target format for ${conversionType}: ${targetFormat}`); } for (const file of files) { const ext = path.extname(file.path).toLowerCase(); if (!supported.from.includes(ext)) { throw new Error(`Unsupported source format for ${conversionType}: ${file.path}`); } } } createDocumentConversionWorkflow(files, targetFormat, options) { return { id: `document_conversion_${Date.now()}`, steps: [ { id: 'prepare_documents', layer: 'aistudio', action: 'document_analysis', input: { files, instructions: 'Analyze document structure and content for conversion', }, dependsOn: [], }, { id: 'convert_documents', layer: 'aistudio', action: 'convert_file', input: { files, outputFormat: targetFormat, options: { preserveFormatting: options?.preserveFormatting ?? true, extractImages: options?.extractImages ?? false, quality: options?.quality ?? 'high', }, }, dependsOn: ['prepare_documents'], }, { id: 'verify_conversion', layer: 'claude', action: 'synthesize_response', input: { request: 'Verify conversion quality and generate conversion report', inputs: { originalAnalysis: '{{prepare_documents}}', conversionResult: '{{convert_documents}}', }, }, dependsOn: ['convert_documents'], }, ], continueOnError: false, timeout: this.timeout, }; } createImageConversionWorkflow(files, targetFormat, options) { return { id: `image_conversion_${Date.now()}`, steps: [ { id: 'analyze_images', layer: 'aistudio', action: 'analyze_image', input: { files, instructions: 'Analyze image properties for optimal conversion', }, dependsOn: [], }, { id: 'convert_images', layer: 'aistudio', action: 'convert_file', input: { files, outputFormat: targetFormat, options: { resize: options?.resize, quality: options?.quality ?? 90, compress: options?.compress ?? false, }, }, dependsOn: ['analyze_images'], }, { id: 'verify_image_quality', layer: 'claude', action: 'synthesize_response', input: { request: 'Assess image conversion quality and provide optimization recommendations', inputs: { originalAnalysis: '{{analyze_images}}', conversionResult: '{{convert_images}}', }, }, dependsOn: ['convert_images'], }, ], continueOnError: true, timeout: this.timeout, }; } createAudioConversionWorkflow(files, targetFormat, options) { return { id: `audio_conversion_${Date.now()}`, steps: [ { id: 'analyze_audio', layer: 'aistudio', action: 'transcribe_audio', input: { files, instructions: 'Analyze audio properties and quality', }, dependsOn: [], }, { id: 'convert_audio', layer: 'aistudio', action: 'convert_file', input: { files, outputFormat: targetFormat, options: { bitrate: options?.bitrate, sampleRate: options?.sampleRate, channels: options?.channels, }, }, dependsOn: ['analyze_audio'], }, { id: 'verify_audio_quality', layer: 'claude', action: 'synthesize_response', input: { request: 'Verify audio conversion quality and generate quality report', inputs: { originalAnalysis: '{{analyze_audio}}', conversionResult: '{{convert_audio}}', }, }, dependsOn: ['convert_audio'], }, ], continueOnError: true, timeout: this.timeout, }; } createDataConversionWorkflow(files, targetFormat, options) { return { id: `data_conversion_${Date.now()}`, steps: [ { id: 'analyze_data_structure', layer: 'aistudio', action: 'document_analysis', input: { files, instructions: 'Analyze data structure and schema for conversion', }, dependsOn: [], }, { id: 'convert_data_format', layer: 'aistudio', action: 'convert_file', input: { files, outputFormat: targetFormat, options: { preserveStructure: options?.preserveStructure ?? true, includeMetadata: options?.includeMetadata ?? true, encoding: options?.encoding ?? 'utf-8', }, }, dependsOn: ['analyze_data_structure'], }, { id: 'validate_data_integrity', layer: 'claude', action: 'complex_reasoning', input: { prompt: 'Validate data integrity and structure preservation after conversion', context: 'Original: {{analyze_data_structure}}, Converted: {{convert_data_format}}', depth: 'medium', }, dependsOn: ['convert_data_format'], }, ], continueOnError: false, timeout: this.timeout, }; } createBatchConversionWorkflow(conversions) { const steps = []; conversions.forEach((conversion, index) => { steps.push({ id: `batch_conversion_${index}`, layer: 'aistudio', action: 'convert_file', input: { files: conversion.files, outputFormat: conversion.targetFormat, options: conversion.options || {}, }, dependsOn: [], }); }); steps.push({ id: 'summarize_batch_conversion', layer: 'claude', action: 'synthesize_response', input: { request: 'Summarize batch conversion results and provide quality assessment', inputs: Object.fromEntries(conversions.map((_, index) => [`conversion_${index}`, `{{batch_conversion_${index}}}`])), }, dependsOn: conversions.map((_, index) => `batch_conversion_${index}`), }); return { id: `batch_conversion_${Date.now()}`, steps, continueOnError: true, timeout: this.timeout * conversions.length, }; } createContentExtractionConversionWorkflow(files, extractionType, targetFormat, options) { return { id: `content_extraction_conversion_${Date.now()}`, steps: [ { id: 'extract_content', layer: 'aistudio', action: 'multimodal_processing', input: { files, instructions: `Extract ${extractionType} content from files`, options: { extractionType }, }, dependsOn: [], }, { id: 'convert_extracted_content', layer: 'aistudio', action: 'convert_file', input: { content: '{{extract_content}}', outputFormat: targetFormat, options: options || {}, }, dependsOn: ['extract_content'], }, { id: 'organize_converted_content', layer: 'claude', action: 'synthesize_response', input: { request: 'Organize and structure the converted content with metadata', inputs: { extractedContent: '{{extract_content}}', convertedContent: '{{convert_extracted_content}}', }, }, dependsOn: ['convert_extracted_content'], }, ], continueOnError: false, timeout: this.timeout, }; } assessConversionComplexity(files, options) { let complexityScore = 0; if (files.length > 20) { complexityScore += 3; } else if (files.length > 10) { complexityScore += 2; } else if (files.length > 5) { complexityScore += 1; } const totalSize = files.reduce((sum, file) => sum + (file.size || 0), 0); if (totalSize > 500 * 1024 * 1024) { complexityScore += 3; } else if (totalSize > 100 * 1024 * 1024) { complexityScore += 2; } else if (totalSize > 10 * 1024 * 1024) { complexityScore += 1; } if (options?.quality === 'high') { complexityScore += 1; } if (options?.preserveFormatting) { complexityScore += 1; } if (options?.extractImages) { complexityScore += 1; } if (options?.resize) { complexityScore += 1; } if (complexityScore >= 6) { return 'high'; } if (complexityScore >= 3) { return 'medium'; } return 'low'; } estimateConversionDuration(files, complexity) { const baseTime = 30000; const fileMultiplier = Math.min(files.length * 0.5, 10); const complexityMultipliers = { low: 1, medium: 2, high: 4, }; return baseTime * fileMultiplier * complexityMultipliers[complexity]; } estimateConversionCost(files, complexity) { const baseCost = 0.005; const fileCount = files.length; const complexityMultipliers = { low: 1, medium: 1.5, high: 2.5, }; return baseCost * fileCount * complexityMultipliers[complexity]; } isSupportedSourceFormat(extension) { return Object.values(this.SUPPORTED_CONVERSIONS) .some(conv => conv.from.includes(extension)); } getSupportedConversions() { return this.SUPPORTED_CONVERSIONS; } getAvailableConversionTypes() { return Object.keys(this.SUPPORTED_CONVERSIONS); } getCapabilities() { return [ 'document_conversion', 'image_conversion', 'audio_conversion', 'data_conversion', 'batch_conversion', 'content_extraction_conversion', ]; } }