danielrosehill's picture
updated
f6ba329
const fs = require('fs');
const path = require('path');
// Configuration
const BATCH_SIZE = 50; // Process 50 files at a time
// Paths
const configsDir = path.join(__dirname, 'configs');
const imagesDir = path.join(__dirname, 'images/avatar-photos');
const outputDir = path.join(__dirname, 'generated-configs');
const outputListFile = path.join(__dirname, 'configs-list.json');
// Command line arguments
const args = process.argv.slice(2);
const startIndex = parseInt(args[0]) || 0;
const batchSize = parseInt(args[1]) || BATCH_SIZE;
// Create output directory if it doesn't exist
if (!fs.existsSync(outputDir)) {
fs.mkdirSync(outputDir, { recursive: true });
}
// Get all markdown files and sort them alphabetically
const configFiles = fs.readdirSync(configsDir)
.filter(file => file.endsWith('.md'))
.sort((a, b) => a.localeCompare(b));
// Get all image files
const imageFiles = fs.readdirSync(imagesDir)
.filter(file => file.endsWith('.webp'));
// Determine which files to process in this batch
const endIndex = Math.min(startIndex + batchSize, configFiles.length);
const filesToProcess = configFiles.slice(startIndex, endIndex);
console.log(`Processing files ${startIndex + 1} to ${endIndex} of ${configFiles.length}`);
console.log(`Found ${imageFiles.length} image files.`);
// Load existing configs list if it exists
let configsList = [];
if (fs.existsSync(outputListFile)) {
try {
configsList = JSON.parse(fs.readFileSync(outputListFile, 'utf8'));
console.log(`Loaded ${configsList.length} existing configurations from configs-list.json`);
} catch (error) {
console.error('Error loading existing configs-list.json:', error);
}
}
// Process each configuration file in this batch
filesToProcess.forEach(file => {
try {
const filePath = path.join(configsDir, file);
const content = fs.readFileSync(filePath, 'utf8');
// Parse the markdown content
const name = extractSection(content, '# Name', '# Description').trim();
const description = extractSection(content, '# Description', '# System Prompt').trim();
const systemPrompt = extractSection(content, '# System Prompt', null).trim();
if (!name || !description || !systemPrompt) {
console.warn(`Skipping ${file}: Missing required sections`);
return;
}
// Find a matching avatar image
const avatar = findMatchingAvatar(file, name, imageFiles);
// Create JSON data for this configuration
const configData = {
name,
description,
systemPrompt,
avatar: avatar ? `images/avatar-photos/${avatar}` : null
};
// Save individual JSON file
const jsonFilename = file.replace('.md', '.json');
fs.writeFileSync(path.join(outputDir, jsonFilename), JSON.stringify(configData, null, 2));
// Check if this configuration is already in the list
const existingIndex = configsList.findIndex(c => c.filename === jsonFilename);
if (existingIndex !== -1) {
// Update existing entry
configsList[existingIndex] = { ...configData, filename: jsonFilename };
} else {
// Add to the list
configsList.push({
name,
description,
systemPrompt,
filename: jsonFilename,
avatar: avatar ? `images/avatar-photos/${avatar}` : null
});
}
console.log(`Processed: ${file} (${configsList.length} total)`);
} catch (error) {
console.error(`Error processing ${file}:`, error);
}
});
// Sort the list alphabetically by name
configsList.sort((a, b) => a.name.localeCompare(b.name));
// Save the updated list
fs.writeFileSync(outputListFile, JSON.stringify(configsList, null, 2));
console.log(`Updated configs-list.json with ${configsList.length} configurations.`);
// Print progress information
if (endIndex < configFiles.length) {
console.log(`\nTo process the next batch, run:`);
const nextCommand = `node build.js ${endIndex} ${batchSize}`;
console.log(nextCommand);
// Write to output file for build-all.sh
fs.appendFileSync('build-output.txt', `To process the next batch, run:\n${nextCommand}\n`);
} else {
console.log(`\nAll files processed successfully!`);
// Write completion to output file
fs.appendFileSync('build-output.txt', `All files processed successfully!\n`);
}
// Helper function to extract a section from markdown
function extractSection(content, startMarker, endMarker) {
const startIndex = content.indexOf(startMarker);
if (startIndex === -1) return '';
const startContentIndex = startIndex + startMarker.length;
let endContentIndex;
if (endMarker) {
const endIndex = content.indexOf(endMarker, startContentIndex);
endContentIndex = endIndex !== -1 ? endIndex : content.length;
} else {
endContentIndex = content.length;
}
return content.substring(startContentIndex, endContentIndex);
}
// Helper function to find a matching avatar image
function findMatchingAvatar(filename, name, imageFiles) {
// Remove extension from filename
const baseFilename = path.basename(filename, '.md');
// Try to find exact match by filename
const exactMatch = imageFiles.find(img => {
const imgBase = path.basename(img, '.webp');
return imgBase.toLowerCase() === baseFilename.toLowerCase();
});
if (exactMatch) return exactMatch;
// Try to find partial matches
const partialMatches = imageFiles.filter(img => {
const imgBase = path.basename(img, '.webp').toLowerCase();
const filenameWords = baseFilename.toLowerCase().split(/[-_\s]+/);
// Check if any word from the filename is in the image name
return filenameWords.some(word =>
word.length > 3 && imgBase.includes(word)
);
});
if (partialMatches.length > 0) {
// Return the first match
return partialMatches[0];
}
// If no match found, try to find a thematically appropriate image
// This is a simple approach - in a real system, you might use more sophisticated matching
// Keywords to image mappings
const keywordMappings = [
{ keywords: ['sloth', 'slow', 'lazy'], images: imageFiles.filter(img => img.includes('sloth')) },
{ keywords: ['tech', 'code', 'developer', 'programming'], images: imageFiles.filter(img => img.includes('laptop') || img.includes('computer')) },
{ keywords: ['android', 'robot', 'ai', 'assistant'], images: imageFiles.filter(img => img.includes('android') || img.includes('bot')) },
{ keywords: ['business', 'corporate', 'company'], images: imageFiles.filter(img => img.includes('business') || img.includes('corporate')) },
{ keywords: ['document', 'text', 'writing'], images: imageFiles.filter(img => img.includes('doc') || img.includes('text')) }
];
for (const mapping of keywordMappings) {
const matchesKeyword = mapping.keywords.some(keyword =>
baseFilename.toLowerCase().includes(keyword) ||
name.toLowerCase().includes(keyword)
);
if (matchesKeyword && mapping.images.length > 0) {
// Return a random image from the matching category
return mapping.images[Math.floor(Math.random() * mapping.images.length)];
}
}
// If still no match, return a random image
return imageFiles[Math.floor(Math.random() * imageFiles.length)];
}