/** * AAMOS Swarm Orchestrator V2 * Parallell exekvering med dynamisk lastbalansering */ import { MODELS, kimiCreate, kimiStream, qwenCreate, qwenStream, claudeChat, groqChat } from './model-router.mjs'; // ── Provider-hälsa ──────────────────────────────────────────────────────── const _providerHealth = new Map(); function checkHealth(provider) { const lastCheck = _providerHealth.get(provider); if (!lastCheck) return true; return Date.now() - lastCheck.lastFailure > 30000; // 30s cooldown } function markFailure(provider) { _providerHealth.set(provider, { lastFailure: Date.now(), failures: (_providerHealth.get(provider)?.failures || 0) + 1 }); } function markSuccess(provider) { _providerHealth.set(provider, { lastFailure: 0, failures: 0, lastSuccess: Date.now() }); } // ── Viktad routing ──────────────────────────────────────────────────────── function selectProvider(task, message) { const providers = [ { name: 'kimi', models: [MODELS.KIMI_K2_6, MODELS.KIMI_K2_7_CODE, MODELS.KIMI_K2_7_HIGHSPEED], weight: 0.5 }, { name: 'bedrock', models: [MODELS.QWEN_235B, MODELS.QWEN_32B, MODELS.QWEN_CODER], weight: 0.3 }, { name: 'groq', models: ['llama-3.3-70b-versatile', 'openai/gpt-oss-120b'], weight: 0.2 }, ]; const available = providers.filter(p => checkHealth(p.name)); const totalWeight = available.reduce((a, p) => a + p.weight, 0); let random = Math.random() * totalWeight; for (const provider of available) { random -= provider.weight; if (random <= 0) return provider; } return available[0] || providers[0]; // fallback } // ── Swarm exekvering ────────────────────────────────────────────────────── export async function swarmExecute(tasks, options = {}) { const { maxConcurrency = 4, sharedContext = {}, timeout = 60000, onProgress = () => {} } = options; const startTime = Date.now(); const results = []; const executing = new Set(); async function executeTask(task, index) { const taskStart = Date.now(); const provider = selectProvider(task.type, task.message); onProgress({ type: 'start', task: index, provider: provider.name, model: task.model }); try { let result; // Välj rätt execute-funktion baserat på provider switch(provider.name) { case 'kimi': result = await kimiCreate(task.model, sharedContext.system, [ ...sharedContext.history || [], { role: 'user', content: task.message } ], task.maxTokens || 4096); break; case 'bedrock': result = await qwenCreate(task.model, sharedContext.system, [ ...sharedContext.history || [], { role: 'user', content: task.message } ], task.maxTokens || 4096); break; case 'groq': result = await groqChat(task.model, sharedContext.system, [ ...sharedContext.history || [], { role: 'user', content: task.message } ]); break; default: throw new Error(`Okänd provider: ${provider.name}`); } markSuccess(provider.name); const duration = Date.now() - taskStart; onProgress({ type: 'complete', task: index, duration, provider: provider.name }); return { status: 'fulfilled', value: { task: task.type, model: task.model, provider: provider.name, duration, result: typeof result === 'string' ? result : result.text, timestamp: new Date().toISOString(), } }; } catch (error) { markFailure(provider.name); const duration = Date.now() - taskStart; onProgress({ type: 'error', task: index, duration, error: error.message }); return { status: 'rejected', reason: { task: task.type, model: task.model, provider: provider.name, duration, error: error.message, timestamp: new Date().toISOString(), } }; } } // Kör med begränsad concurrency for (let i = 0; i < tasks.length; i++) { while (executing.size >= maxConcurrency) { await Promise.race(executing); } const promise = executeTask(tasks[i], i).then(result => { executing.delete(promise); results[i] = result; return result; }); executing.add(promise); } // Vänta på alla await Promise.all(executing); const totalTime = Date.now() - startTime; // Sammanställ const successful = results.filter(r => r.status === 'fulfilled').map(r => r.value); const failed = results.filter(r => r.status === 'rejected').map(r => r.reason); return { successful, failed, totalTime, summary: { total: tasks.length, success: successful.length, failed: failed.length, avgDuration: successful.reduce((a, r) => a + r.duration, 0) / successful.length || 0, providers: [...new Set(successful.map(r => r.provider))], } }; } // ── Agent-koordinering ──────────────────────────────────────────────────── export async function coordinateAgents(agents, sharedGoal, options = {}) { console.log(`🎯 Koordinerar ${agents.length} agenter för: ${sharedGoal}`); // Fas 1: Planering (Bernt-nivå) const plan = await swarmExecute([{ type: 'planning', model: MODELS.KIMI_K2_6, message: `Planera uppgifter för: ${sharedGoal}. Tillgängliga agenter: ${agents.map(a => a.name).join(', ')}`, maxTokens: 2048, }], { sharedContext: { system: 'Du är en expert på att bryta ner komplexa uppgifter i deluppgifter.' } }); // Fas 2: Exekvering (parallell) const tasks = agents.map(agent => ({ type: agent.specialization[0], model: agent.model, message: `${agent.role}: ${sharedGoal}`, maxTokens: 4096, })); const results = await swarmExecute(tasks, { maxConcurrency: options.maxConcurrency || agents.length, sharedContext: { system: `Delad kontext för: ${sharedGoal}`, history: [{ role: 'assistant', content: plan.successful[0]?.result || '' }] }, onProgress: (event) => { if (event.type === 'start') console.log(` 🚀 ${event.provider} startar task ${event.task}`); if (event.type === 'complete') console.log(` ✅ Task ${event.task} klar (${event.duration}ms)`); if (event.type === 'error') console.log(` ❌ Task ${event.task} misslyckades: ${event.error}`); } }); // Fas 3: Sammanställning const synthesis = await swarmExecute([{ type: 'synthesis', model: MODELS.KIMI_K2_6, message: `Sammanställ resultaten för: ${sharedGoal}\n\n${results.successful.map(r => `## ${r.task}\n${r.result}`).join('\n\n')}`, maxTokens: 4096, }], { sharedContext: { system: 'Du är en expert på att syntetisera resultat från flera källor.' } }); return { plan: plan.successful[0]?.result, results, synthesis: synthesis.successful[0]?.result, }; } export default { swarmExecute, coordinateAgents };