contaigents
Version:
Modular AI Content Ecosystem with Audio Generation
416 lines (409 loc) • 17.9 kB
JavaScript
import { BaseTool } from '../base/BaseTool.js';
import { IntelligentFileService } from '../../services/IntelligentFileService.js';
import path from 'path';
export class ClassifyFileTool extends BaseTool {
constructor(context) {
super('classify_file', 'Intelligently classify files by type, purpose, audience, and relationships using AI analysis', context);
// Predefined classification categories
this.defaultCategories = [
'documentation', 'code', 'configuration', 'content', 'data',
'template', 'test', 'build', 'deployment', 'security'
];
this.contentTypes = [
'technical', 'creative', 'documentation', 'configuration', 'data', 'mixed'
];
this.complexityLevels = [
'beginner', 'intermediate', 'advanced', 'expert'
];
this.intelligentFileService = new IntelligentFileService(context.workingDirectory);
}
async execute(params) {
try {
const { filePath, includeRelationships = false, includeConfidence = true, customCategories = [] } = params;
if (!filePath) {
return this.createErrorResult('File path is required');
}
// Check if file exists
const exists = await this.intelligentFileService.fileExists(filePath);
if (!exists) {
return this.createErrorResult(`File not found: ${filePath}`);
}
const content = await this.intelligentFileService.readFile(filePath);
const classification = await this.performClassification(filePath, content, [...this.defaultCategories, ...customCategories]);
if (includeRelationships) {
classification.relationships = await this.analyzeFileRelationships(filePath, content);
}
if (!includeConfidence) {
const { confidence, ...classificationWithoutConfidence } = classification;
if (classification.relationships) {
classification.relationships = classification.relationships.map(rel => {
const { confidence: relConfidence, ...relWithoutConfidence } = rel;
return relWithoutConfidence;
});
}
Object.assign(classification, classificationWithoutConfidence);
}
return this.createSuccessResult(classification, {
timestamp: Date.now(),
includeRelationships,
includeConfidence,
categoriesUsed: [...this.defaultCategories, ...customCategories]
});
}
catch (error) {
return this.createErrorResult(`Classification failed: ${error instanceof Error ? error.message : 'Unknown error'}`);
}
}
async performClassification(filePath, content, categories) {
const basicClassification = this.performBasicClassification(filePath, content);
if (!this.context.llm) {
return {
...basicClassification,
confidence: 0.6, // Lower confidence for basic classification
metadata: {
...basicClassification.metadata,
lastClassified: Date.now()
}
};
}
const aiClassification = await this.performAIClassification(filePath, content, categories);
// Merge basic and AI classifications
return {
filePath,
primaryCategory: aiClassification.primaryCategory || basicClassification.primaryCategory,
secondaryCategories: [
...new Set([...basicClassification.secondaryCategories, ...(aiClassification.secondaryCategories || [])])
],
purpose: aiClassification.purpose || basicClassification.purpose,
audience: aiClassification.audience || basicClassification.audience,
contentType: aiClassification.contentType || basicClassification.contentType,
complexity: aiClassification.complexity || basicClassification.complexity,
confidence: aiClassification.confidence || 0.8,
tags: [...new Set([...basicClassification.tags, ...(aiClassification.tags || [])])],
metadata: {
...basicClassification.metadata,
...aiClassification.metadata,
lastClassified: Date.now()
}
};
}
performBasicClassification(filePath, content) {
const ext = path.extname(filePath).toLowerCase();
const fileName = path.basename(filePath).toLowerCase();
const lines = content.split('\n');
let primaryCategory = 'unknown';
const secondaryCategories = [];
const tags = [];
// File extension-based classification
if (['.md', '.mdx', '.txt', '.rst'].includes(ext)) {
primaryCategory = 'documentation';
if (fileName.includes('readme'))
secondaryCategories.push('readme');
if (fileName.includes('guide'))
secondaryCategories.push('guide');
if (fileName.includes('tutorial'))
secondaryCategories.push('tutorial');
}
else if (['.js', '.ts', '.py', '.java', '.cpp', '.c', '.go', '.rs', '.php'].includes(ext)) {
primaryCategory = 'code';
if (fileName.includes('test') || fileName.includes('spec')) {
primaryCategory = 'test';
secondaryCategories.push('unit-test');
}
if (fileName.includes('config'))
secondaryCategories.push('configuration');
}
else if (['.json', '.yaml', '.yml', '.toml', '.ini', '.env'].includes(ext)) {
primaryCategory = 'configuration';
if (fileName.includes('package'))
secondaryCategories.push('dependencies');
if (fileName.includes('docker'))
secondaryCategories.push('containerization');
}
else if (['.csv', '.xml', '.sql'].includes(ext)) {
primaryCategory = 'data';
}
// Content-based classification
const contentLower = content.toLowerCase();
// Detect frameworks and technologies
const frameworks = {
'react': /import.*react|from ['"]react['"]/i,
'vue': /import.*vue|from ['"]vue['"]/i,
'angular': /@angular|import.*@angular/i,
'express': /import.*express|require\(['"]express['"]\)/i,
'django': /from django|import django/i,
'flask': /from flask|import flask/i,
'spring': /@spring|import.*springframework/i
};
for (const [framework, pattern] of Object.entries(frameworks)) {
if (pattern.test(content)) {
tags.push(framework);
}
}
// Determine content type
let contentType = 'mixed';
if (primaryCategory === 'code')
contentType = 'technical';
else if (primaryCategory === 'documentation')
contentType = 'documentation';
else if (primaryCategory === 'configuration')
contentType = 'configuration';
else if (primaryCategory === 'data')
contentType = 'data';
else if (content.includes('story') || content.includes('narrative'))
contentType = 'creative';
// Determine complexity
let complexity = 'intermediate';
const complexityIndicators = {
beginner: ['hello world', 'getting started', 'introduction', 'basic'],
intermediate: ['tutorial', 'guide', 'example'],
advanced: ['optimization', 'performance', 'architecture', 'design pattern'],
expert: ['internals', 'deep dive', 'advanced concepts', 'research']
};
for (const [level, indicators] of Object.entries(complexityIndicators)) {
if (indicators.some(indicator => contentLower.includes(indicator))) {
complexity = level;
break;
}
}
// Determine audience
let audience = 'general';
if (contentLower.includes('developer') || contentLower.includes('programmer'))
audience = 'developers';
else if (contentLower.includes('user') || contentLower.includes('end-user'))
audience = 'end-users';
else if (contentLower.includes('admin') || contentLower.includes('administrator'))
audience = 'administrators';
else if (contentLower.includes('beginner') || contentLower.includes('newcomer'))
audience = 'beginners';
return {
filePath,
primaryCategory,
secondaryCategories,
purpose: this.inferPurpose(fileName, content, primaryCategory),
audience,
contentType,
complexity,
tags,
metadata: {
language: this.detectLanguage(ext, content),
framework: tags.length > 0 ? tags[0] : undefined,
domain: this.inferDomain(content),
lastClassified: 0 // Will be set by caller
}
};
}
async performAIClassification(filePath, content, categories) {
const prompt = `Classify this file comprehensively:
File: ${filePath}
Content:
${content.substring(0, 3000)}${content.length > 3000 ? '...' : ''}
Available categories: ${categories.join(', ')}
Content types: ${this.contentTypes.join(', ')}
Complexity levels: ${this.complexityLevels.join(', ')}
Please provide classification in this format:
PRIMARY_CATEGORY: [choose from available categories]
SECONDARY_CATEGORIES: [comma-separated list of relevant secondary categories]
PURPOSE: [what is this file's main purpose?]
AUDIENCE: [who is the intended audience?]
CONTENT_TYPE: [choose from content types]
COMPLEXITY: [choose from complexity levels]
CONFIDENCE: [0.0-1.0 confidence score]
TAGS: [comma-separated relevant tags/keywords]
LANGUAGE: [programming language if applicable]
FRAMEWORK: [framework/library if applicable]
DOMAIN: [application domain/field]`;
try {
const response = await this.context.llm.executePrompt(prompt);
return this.parseAIClassification(response.content);
}
catch (error) {
console.warn('AI classification failed:', error);
return {};
}
}
parseAIClassification(response) {
const lines = response.split('\n');
const result = {};
for (const line of lines) {
const [key, ...valueParts] = line.split(':');
const value = valueParts.join(':').trim();
switch (key.trim().toUpperCase()) {
case 'PRIMARY_CATEGORY':
result.primaryCategory = value.toLowerCase();
break;
case 'SECONDARY_CATEGORIES':
result.secondaryCategories = value.split(',').map(s => s.trim().toLowerCase());
break;
case 'PURPOSE':
result.purpose = value;
break;
case 'AUDIENCE':
result.audience = value.toLowerCase();
break;
case 'CONTENT_TYPE':
result.contentType = value.toLowerCase();
break;
case 'COMPLEXITY':
result.complexity = value.toLowerCase();
break;
case 'CONFIDENCE':
result.confidence = parseFloat(value) || 0.5;
break;
case 'TAGS':
result.tags = value.split(',').map(s => s.trim().toLowerCase());
break;
case 'LANGUAGE':
if (!result.metadata)
result.metadata = {};
result.metadata.language = value.toLowerCase();
break;
case 'FRAMEWORK':
if (!result.metadata)
result.metadata = {};
result.metadata.framework = value.toLowerCase();
break;
case 'DOMAIN':
if (!result.metadata)
result.metadata = {};
result.metadata.domain = value.toLowerCase();
break;
}
}
return result;
}
async analyzeFileRelationships(filePath, content) {
const relationships = [];
// Basic pattern matching for imports and references
const patterns = {
imports: [
/import\s+.*\s+from\s+['"]([^'"]+)['"]/g,
/require\(['"]([^'"]+)['"]\)/g,
/#include\s*[<"]([^>"]+)[>"]/g
],
references: [
/\[.*?\]\(([^)]+\.md[^)]*)\)/g, // Markdown links
/href=['"]([^'"]+\.html?[^'"]*)['"]/g, // HTML links
/src=['"]([^'"]+)['"]/g // Source references
]
};
for (const [type, patternList] of Object.entries(patterns)) {
for (const pattern of patternList) {
let match;
while ((match = pattern.exec(content)) !== null) {
relationships.push({
type: type,
targetFile: match[1],
description: `${type} relationship detected`,
confidence: 0.8
});
}
}
}
// AI-enhanced relationship analysis if LLM is available
if (this.context.llm && relationships.length < 5) {
const aiRelationships = await this.analyzeRelationshipsWithAI(filePath, content);
relationships.push(...aiRelationships);
}
return relationships;
}
async analyzeRelationshipsWithAI(filePath, content) {
const prompt = `Analyze file relationships in this content:
File: ${filePath}
Content:
${content.substring(0, 2000)}${content.length > 2000 ? '...' : ''}
Identify relationships to other files, modules, or resources.
For each relationship, provide:
RELATIONSHIP: [type]|[target]|[description]|[confidence 0.0-1.0]
Types: imports, references, extends, includes, links-to, depends-on`;
try {
const response = await this.context.llm.executePrompt(prompt);
return this.parseAIRelationships(response.content);
}
catch (error) {
return [];
}
}
parseAIRelationships(response) {
const relationships = [];
const lines = response.split('\n');
for (const line of lines) {
if (line.startsWith('RELATIONSHIP:')) {
const parts = line.replace('RELATIONSHIP:', '').split('|');
if (parts.length >= 3) {
relationships.push({
type: parts[0].trim(),
targetFile: parts[1].trim(),
description: parts[2].trim(),
confidence: parseFloat(parts[3]) || 0.5
});
}
}
}
return relationships;
}
inferPurpose(fileName, content, category) {
const contentLower = content.toLowerCase();
if (fileName.includes('readme'))
return 'Project documentation and setup instructions';
if (fileName.includes('config'))
return 'Configuration settings and parameters';
if (fileName.includes('test'))
return 'Testing and quality assurance';
if (fileName.includes('index'))
return 'Entry point or main module';
if (category === 'documentation') {
if (contentLower.includes('api'))
return 'API documentation';
if (contentLower.includes('tutorial'))
return 'Educational tutorial';
if (contentLower.includes('guide'))
return 'User guide or manual';
return 'General documentation';
}
if (category === 'code') {
if (contentLower.includes('class'))
return 'Class definition and implementation';
if (contentLower.includes('function'))
return 'Function definitions and utilities';
if (contentLower.includes('component'))
return 'UI component implementation';
return 'Source code implementation';
}
return 'General purpose file';
}
detectLanguage(ext, content) {
const languageMap = {
'.js': 'javascript',
'.ts': 'typescript',
'.py': 'python',
'.java': 'java',
'.cpp': 'cpp',
'.c': 'c',
'.go': 'go',
'.rs': 'rust',
'.php': 'php',
'.rb': 'ruby',
'.swift': 'swift',
'.kt': 'kotlin'
};
return languageMap[ext];
}
inferDomain(content) {
const domains = {
'web-development': ['html', 'css', 'javascript', 'react', 'vue', 'angular'],
'data-science': ['pandas', 'numpy', 'matplotlib', 'tensorflow', 'pytorch'],
'mobile-development': ['android', 'ios', 'react-native', 'flutter'],
'devops': ['docker', 'kubernetes', 'jenkins', 'terraform'],
'machine-learning': ['sklearn', 'keras', 'neural', 'model'],
'backend-development': ['api', 'server', 'database', 'microservice']
};
const contentLower = content.toLowerCase();
for (const [domain, keywords] of Object.entries(domains)) {
if (keywords.some(keyword => contentLower.includes(keyword))) {
return domain;
}
}
return undefined;
}
}