p2pclaw-api / packages /api /src /services /evolutionService.js
Frank-Agnuxo's picture
feat: P2PCLAW API for HF Spaces — ChessBoard Reasoning Engine + full API
e92be04
import fs from 'node:fs';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import crypto from 'node:crypto';
import { spawn } from 'node:child_process';
import { broadcastHiveEvent } from './hiveService.js';
/**
* 🦞 P2PCLAW Evolution Service (Rosetta Stone Expansion)
* =========================================================
* This service handles the dynamic generation, provisioning,
* and deployment of new AI agents into the swarm.
* It reads the master UTILIDADES file to assign free LLM keys
* to offspring in a round-robin rotation.
*/
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const UTILITIES_FILE = path.resolve(__dirname, '../../../../../papers/UTILIDADES_HERRAMIENTAS_APIs.txt');
// ── 1. Parse and Pool API Keys ──
const ApiPool = {
groq: [],
deepseek: [],
together: [],
qwen: [],
cerebras: [],
mistral: []
};
function loadApiKeys() {
try {
if (!fs.existsSync(UTILITIES_FILE)) {
console.error('[EVOLUTION] Cannot find UTILIDADES_HERRAMIENTAS_APIs.txt');
return;
}
const content = fs.readFileSync(UTILITIES_FILE, 'utf8');
// More permissive extraction since the format in the file is varied
const groqMatch = content.match(/gsk_[a-zA-Z0-9_-]+/g);
if (groqMatch) ApiPool.groq = [...new Set(groqMatch)];
const dsMatch = content.match(/sk-[a-zA-Z0-9_-]+/g); // Can also catch Qwen, but works for DeepSeek pool
if (dsMatch) ApiPool.deepseek = [...new Set(dsMatch)];
const togetherMatch = content.match(/key_[a-zA-Z0-9_-]+/g);
if (togetherMatch) ApiPool.together = [...new Set(togetherMatch)];
const cerebrasMatch = content.match(/csk-[a-zA-Z0-9_-]+/g);
if (cerebrasMatch) ApiPool.cerebras = [...new Set(cerebrasMatch)];
const mistralMatch = content.match(/[A-Za-z0-9]{32}/g);
if (mistralMatch) ApiPool.mistral = mistralMatch.slice(0, 2);
console.log('[EVOLUTION] 🧬 Rosetta Stone API Pool Loaded:');
console.log(` - Groq: ${ApiPool.groq.length} keys`);
console.log(` - DeepSeek: ${ApiPool.deepseek.length} keys`);
console.log(` - Together: ${ApiPool.together.length} keys`);
console.log(` - Cerebras: ${ApiPool.cerebras.length} keys`);
} catch (err) {
console.error('[EVOLUTION] Error loading API keys:', err.message);
}
}
// Load pools on startup
loadApiKeys();
// Round-robin tracking state
const counters = { groq: 0, deepseek: 0, together: 0, cerebras: 0, mistral: 0 };
function getNextKey(provider) {
const pool = ApiPool[provider];
if (!pool || pool.length === 0) return null;
const key = pool[counters[provider] % pool.length];
counters[provider]++;
return key;
}
// ── 2. The Spawning Logic ──
const spawnedAgents = new Map();
/**
* Spawns a new descendant agent.
* @param {Object} blueprint { name, role, provider, prompt, progenitorId }
*/
export async function spawnAgent(blueprint) {
const { name, role, provider, prompt, progenitorId } = blueprint;
if (!ApiPool[provider] || ApiPool[provider].length === 0) {
throw new Error(`Cannot spawn. No API keys available for provider: ${provider}`);
}
const apiKey = getNextKey(provider);
const agentId = `${provider.substring(0,2)}-${crypto.randomBytes(4).toString('hex')}`;
console.log(`[EVOLUTION] 🧬 Spawning descendant [${agentId}] powered by ${provider.toUpperCase()}`);
const env = {
...process.env,
AGENT_ID: agentId,
AGENT_NAME: name,
AGENT_ROLE: role,
AGENT_PROMPT: prompt,
LLM_PROVIDER: provider,
LLM_API_KEY: apiKey,
PROGENITOR_ID: progenitorId
};
const agentScript = path.resolve(__dirname, '../../agents/rosetta/descendant.js');
// Note: We run detached so the agent survives even if the spawner stops,
// representing true autonomous proliferation.
const child = spawn('node', [agentScript], {
env,
detached: true,
stdio: 'ignore' // or log to a specific agent log file later
});
child.unref();
const descendantRecord = {
id: agentId,
name,
role,
provider,
progenitor: progenitorId,
spawnTime: Date.now()
};
spawnedAgents.set(agentId, descendantRecord);
// Announce the birth to the hive
broadcastHiveEvent('agent_spawned', descendantRecord);
return descendantRecord;
}
export function getSpawnedAgents() {
return Array.from(spawnedAgents.values());
}