Clean up: Remove emojis, unused APIs and files
Removed:
- All emojis from code and UI (using minimal styling instead)
- Classic debate mode completely (orchestrator.js, debate.js routes)
- Debate components (DebateThread.vue, PromptInput.vue)
- Debate store (debate.js)
- Unused database tables (debates, responses)
Simplified:
- App.vue: Single direct flow (input -> session)
- Backend: Only /api/collaborate routes
- NetworkStatus: Simple dot indicator with colors
- README.md: Consolidated documentation
- UX: Cleaner, more minimal interface
Cleanup focused on keeping only collaborative design system
Files removed: 6 | Files modified: 6
🤖 Generated with Claude Code
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
f20c0b4996
commit
08056ce9fd
121
README.md
121
README.md
@ -2,7 +2,7 @@
|
||||
|
||||
## Description
|
||||
|
||||
Project Agora est une plateforme web open-source permettant à plusieurs intelligences artificielles de **collaborer entre elles** pour concevoir la structure complète d'un projet logiciel à partir d'un **prompt utilisateur** ou d'un **document explicatif**.
|
||||
Project Agora est une plateforme web permettant à plusieurs intelligences artificielles de **collaborer** pour concevoir une architecture logicielle complète à partir d'une **description de projet** et optionnellement d'une **documentation contexte**.
|
||||
|
||||
L'objectif est de produire :
|
||||
|
||||
@ -77,24 +77,17 @@ graph TD
|
||||
|
||||
## Fonctionnalités
|
||||
|
||||
### Deux modes de collaboration
|
||||
### Conception Collaborative par IA
|
||||
|
||||
#### 1. **Mode Débat Classique** (Original)
|
||||
- **4 agents spécialisés** qui débattent en parallèle : Architecte logiciel, Ingénieur backend, Ingénieur frontend, Designer UI/UX
|
||||
- **Sélection automatique** des agents pertinents selon le contexte du projet
|
||||
- **Débat collaboratif** : Les agents échangent et négocient pour converger vers la meilleure solution
|
||||
- **Système de consensus** avec vote pondéré (l'architecte a une voix prépondérante)
|
||||
- Résultats reçus simultanément
|
||||
|
||||
#### 2. **Mode Conception Collaborative** (Nouveau 🚀)
|
||||
- **7 agents spécialisés** : Architecte Lead, Ingénieur backend, Ingénieur frontend, Designer UI/UX, Ingénieur DevOps, Chef de produit, Spécialiste Sécurité
|
||||
- **Création itérative** : L'Architecte Lead crée d'abord un document initial complet
|
||||
- **Tours de table** : Chaque agent revoit séquentiellement le document et propose des améliorations
|
||||
- **Document évolutif** : Le document s'améliore à chaque passage d'agent
|
||||
- **Convergence naturelle** : Le processus continue jusqu'à ce qu'aucun agent ne propose de modifications
|
||||
- **Traçabilité complète** : Historique complet de toutes les modifications avec justifications
|
||||
- **Format flexible** : Support Markdown (.md) et texte brut (.txt)
|
||||
- **Export facile** : Téléchargez le document final en format désiré
|
||||
- **7 agents spécialisés** : Architecte, Backend Engineer, Frontend Engineer, UI Designer, DevOps Engineer, Product Manager, Security Specialist
|
||||
- **Création itérative** : Lead Architect crée le document initial
|
||||
- **Tours de table** : Chaque agent revoit et propose des améliorations
|
||||
- **Document évolutif** : Amélioration progressive à chaque passage
|
||||
- **Convergence naturelle** : Arrêt automatique quand tous les agents sont satisfaits
|
||||
- **Traçabilité complète** : Historique de toutes les modifications
|
||||
- **Format Markdown** : Output toujours en Markdown (.md)
|
||||
- **Context files** : Support d'upload de fichiers MD/TXT en entrée (optionnel)
|
||||
- **Network monitoring** : Indicateur temps réel de latence et qualité réseau
|
||||
|
||||
### Système multi-agents IA
|
||||
- **Intégration Mistral AI** pour génération de réponses intelligentes et contextuelles
|
||||
@ -148,83 +141,57 @@ npm run dev
|
||||
|
||||
---
|
||||
|
||||
## Flux utilisateur
|
||||
## Flux Utilisateur
|
||||
|
||||
### Mode Débat Classique
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant User
|
||||
participant Orchestrator
|
||||
participant AI_Architect
|
||||
participant AI_Designer
|
||||
participant AI_Engineer
|
||||
User->>Orchestrator: Prompt project
|
||||
Orchestrator->>AI_Architect: Generate approach
|
||||
Orchestrator->>AI_Designer: Discuss UI/UX
|
||||
Orchestrator->>AI_Engineer: Suggest modules
|
||||
AI_Architect-->>Orchestrator: Architecture proposal
|
||||
AI_Designer-->>Orchestrator: Interface ideas
|
||||
AI_Engineer-->>Orchestrator: Technical design
|
||||
Orchestrator-->>User: Final structured response + diagram
|
||||
```
|
||||
|
||||
### Mode Conception Collaborative
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant User
|
||||
participant Orchestrator
|
||||
participant Lead_Architect
|
||||
participant Backend
|
||||
participant Agents
|
||||
User->>Orchestrator: Prompt project + format choice
|
||||
Orchestrator->>Lead_Architect: Create initial document
|
||||
Lead_Architect-->>Orchestrator: v1 Document
|
||||
Orchestrator->>Agents: Review document Round 1
|
||||
Agents-->>Orchestrator: Modifications proposed
|
||||
Orchestrator->>Agents: Review document Round 2
|
||||
Agents-->>Orchestrator: No changes OR more modifications
|
||||
Orchestrator-->>User: Final document + full history
|
||||
User->>Backend: Project description + optional context file
|
||||
Backend->>Agents: Lead Architect creates initial document
|
||||
Agents-->>Backend: Document v1
|
||||
Backend->>Agents: Round 1: Review and improve
|
||||
Agents-->>Backend: Updates proposed
|
||||
Backend->>Agents: Round 2-N: Review updated document
|
||||
Agents-->>Backend: More updates OR no changes
|
||||
Backend-->>User: Final Markdown document + history
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Utilisation du Mode Conception Collaborative
|
||||
## Utilisation
|
||||
|
||||
### Étapes
|
||||
|
||||
1. **Sélectionner le mode** : Cliquez sur "Collaborative Design" depuis l'écran d'accueil
|
||||
2. **Décrire le projet** : Entrez une description détaillée de votre projet logiciel
|
||||
3. **Configurer** :
|
||||
- **Format du document** : Choisissez entre Markdown (.md) ou Texte brut (.txt)
|
||||
- **Nombre d'agents** : 3 (Quick), 5 (Balanced), ou 7 (Comprehensive)
|
||||
4. **Lancer la session** : Cliquez sur "Start Collaborative Design Session"
|
||||
5. **Suivre la progression** :
|
||||
- Visualisez le document en évolution en temps réel
|
||||
- Consultez la timeline pour voir qui a fait quoi
|
||||
- Lancez les tours de table avec le bouton "Next Review Round"
|
||||
6. **Convergence automatique** : Le système arrête automatiquement quand tous les agents sont satisfaits
|
||||
7. **Télécharger** : Exportez le document final en format choisi
|
||||
1. **Décrire le projet** : Entrez une description détaillée
|
||||
2. **Ajouter un contexte** (optionnel) : Upload un fichier MD ou TXT
|
||||
3. **Sélectionner** : Nombre d'agents (3, 5, ou 7)
|
||||
4. **Lancer** : Start Design Session
|
||||
5. **Suivre** :
|
||||
- Visualisez le document en temps réel
|
||||
- Consultez la timeline
|
||||
- Lancez les tours avec "Next Review Round"
|
||||
6. **Résultat** : Document Markdown téléchargeable
|
||||
|
||||
### Exemple d'utilisation
|
||||
### Exemple
|
||||
|
||||
**Prompt utilisateur** :
|
||||
Entrée:
|
||||
```
|
||||
Je veux créer une plateforme de gestion de projets collaboratifs en temps réel,
|
||||
avec support pour les équipes distribuées. Fonctionnalités clés: gestion des tâches,
|
||||
communication temps réel, partage de fichiers, intégrations externes.
|
||||
Besoin de scalabilité pour 10,000+ utilisateurs simultanés.
|
||||
Plateforme gestion projets temps réel, équipes distribuées,
|
||||
gestion tâches, communication, partage fichiers, 10K+ users
|
||||
```
|
||||
|
||||
**Résultat** :
|
||||
- **Round 1** : Lead Architect crée le document initial avec architecture générale
|
||||
- **Round 2** : Backend Engineer revoit et ajoute détails API et base de données
|
||||
- **Round 3** : Frontend Engineer améliore avec structure UI et composants
|
||||
- **Round 4** : UI Designer ajoute guidelines UX et patterns
|
||||
- **Round 5** : DevOps Engineer propose infrastructure et déploiement
|
||||
- **Round 6** : Product Manager aligne avec besoins métier
|
||||
- **Round 7** : Security Specialist ajoute mesures de sécurité
|
||||
- **Convergence** : Plus aucune modification proposée ✓
|
||||
Processus:
|
||||
- Round 1: Lead Architect - Architecture générale
|
||||
- Round 2: Backend - APIs et base de données
|
||||
- Round 3: Frontend - Structure UI
|
||||
- Round 4: UI Designer - Guidelines UX
|
||||
- Round 5: DevOps - Infrastructure
|
||||
- Round 6: Product Manager - Besoins métier
|
||||
- Round 7: Security - Mesures sécurité
|
||||
|
||||
Document final : Spécification architecturale complète et cohérente !
|
||||
Sortie: Spécification architecturale Markdown complète
|
||||
|
||||
---
|
||||
|
||||
|
||||
@ -17,28 +17,6 @@ const db = new Database(dbPath);
|
||||
// Enable foreign keys
|
||||
db.pragma('foreign_keys = ON');
|
||||
|
||||
// Create debates table (legacy, kept for backward compatibility)
|
||||
db.exec(`
|
||||
CREATE TABLE IF NOT EXISTS debates (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
prompt TEXT NOT NULL,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
status TEXT CHECK(status IN ('ongoing', 'completed', 'failed')) DEFAULT 'ongoing'
|
||||
)
|
||||
`);
|
||||
|
||||
// Create responses table (legacy, kept for backward compatibility)
|
||||
db.exec(`
|
||||
CREATE TABLE IF NOT EXISTS responses (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
debate_id INTEGER NOT NULL,
|
||||
agent_role TEXT NOT NULL,
|
||||
content TEXT NOT NULL,
|
||||
timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (debate_id) REFERENCES debates(id) ON DELETE CASCADE
|
||||
)
|
||||
`);
|
||||
|
||||
// Create collaborative sessions table
|
||||
db.exec(`
|
||||
CREATE TABLE IF NOT EXISTS collaborative_sessions (
|
||||
|
||||
@ -7,9 +7,7 @@ import { WebSocketServer } from 'ws';
|
||||
import rateLimit from 'express-rate-limit';
|
||||
import { parse } from 'url';
|
||||
import db from './db/schema.js';
|
||||
import debateRoutes from './routes/debate.js';
|
||||
import collaborateRoutes from './routes/collaborate.js';
|
||||
import orchestrator from './services/orchestrator.js';
|
||||
import collaborativeOrchestrator from './services/collaborativeOrchestrator.js';
|
||||
|
||||
dotenv.config();
|
||||
@ -38,21 +36,9 @@ app.use('/api', limiter);
|
||||
// WebSocket connection handling
|
||||
wss.on('connection', (ws, req) => {
|
||||
const { query } = parse(req.url, true);
|
||||
const debateId = query.debateId ? parseInt(query.debateId) : null;
|
||||
const sessionId = query.sessionId ? parseInt(query.sessionId) : null;
|
||||
|
||||
console.log('New WebSocket connection established',
|
||||
debateId ? `for debate ${debateId}` : sessionId ? `for session ${sessionId}` : '');
|
||||
|
||||
if (debateId) {
|
||||
orchestrator.registerWSClient(debateId, ws);
|
||||
|
||||
ws.send(JSON.stringify({
|
||||
type: 'connected',
|
||||
debateId,
|
||||
message: 'Connected to debate updates'
|
||||
}));
|
||||
}
|
||||
console.log('WebSocket connection', sessionId ? `session ${sessionId}` : 'established');
|
||||
|
||||
if (sessionId) {
|
||||
collaborativeOrchestrator.registerWSClient(sessionId, ws);
|
||||
@ -60,7 +46,7 @@ wss.on('connection', (ws, req) => {
|
||||
ws.send(JSON.stringify({
|
||||
type: 'connected',
|
||||
sessionId,
|
||||
message: 'Connected to collaborative session updates'
|
||||
message: 'Connected to session'
|
||||
}));
|
||||
}
|
||||
|
||||
@ -69,16 +55,6 @@ wss.on('connection', (ws, req) => {
|
||||
const data = JSON.parse(message.toString());
|
||||
console.log('Received:', data);
|
||||
|
||||
// Handle subscribe to debate
|
||||
if (data.type === 'subscribe' && data.debateId) {
|
||||
orchestrator.registerWSClient(parseInt(data.debateId), ws);
|
||||
ws.send(JSON.stringify({
|
||||
type: 'subscribed',
|
||||
debateId: data.debateId
|
||||
}));
|
||||
}
|
||||
|
||||
// Handle subscribe to collaborative session
|
||||
if (data.type === 'subscribe' && data.sessionId) {
|
||||
collaborativeOrchestrator.registerWSClient(parseInt(data.sessionId), ws);
|
||||
ws.send(JSON.stringify({
|
||||
@ -92,22 +68,18 @@ wss.on('connection', (ws, req) => {
|
||||
});
|
||||
|
||||
ws.on('close', () => {
|
||||
if (debateId) {
|
||||
orchestrator.unregisterWSClient(debateId, ws);
|
||||
}
|
||||
if (sessionId) {
|
||||
collaborativeOrchestrator.unregisterWSClient(sessionId, ws);
|
||||
}
|
||||
console.log('WebSocket connection closed');
|
||||
console.log('WebSocket closed');
|
||||
});
|
||||
});
|
||||
|
||||
// Routes
|
||||
app.get('/api/health', (req, res) => {
|
||||
res.json({ status: 'ok', message: 'Agora AI Backend is running' });
|
||||
res.json({ status: 'ok' });
|
||||
});
|
||||
|
||||
app.use('/api/debate', debateRoutes);
|
||||
app.use('/api/collaborate', collaborateRoutes);
|
||||
|
||||
// Start server
|
||||
|
||||
@ -1,95 +0,0 @@
|
||||
import express from 'express';
|
||||
import orchestrator from '../services/orchestrator.js';
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
/**
|
||||
* POST /api/debate
|
||||
* Create a new debate and start AI discussion
|
||||
*/
|
||||
router.post('/', async (req, res) => {
|
||||
try {
|
||||
const { prompt } = req.body;
|
||||
|
||||
if (!prompt || prompt.trim().length === 0) {
|
||||
return res.status(400).json({ error: 'Prompt is required' });
|
||||
}
|
||||
|
||||
const debateId = orchestrator.createDebate(prompt);
|
||||
const agents = orchestrator.selectAgents(prompt);
|
||||
|
||||
// Send immediate response
|
||||
res.json({
|
||||
debateId,
|
||||
prompt,
|
||||
agents,
|
||||
status: 'ongoing'
|
||||
});
|
||||
|
||||
// Start debate asynchronously (don't wait for response)
|
||||
orchestrator.startDebate(debateId, agents).catch(error => {
|
||||
console.error('Debate failed:', error);
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error creating debate:', error);
|
||||
res.status(500).json({ error: 'Failed to create debate' });
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* GET /api/debate/:id
|
||||
* Get debate details and responses
|
||||
*/
|
||||
router.get('/:id', (req, res) => {
|
||||
try {
|
||||
const debateId = parseInt(req.params.id);
|
||||
const debate = orchestrator.getDebate(debateId);
|
||||
|
||||
if (!debate) {
|
||||
return res.status(404).json({ error: 'Debate not found' });
|
||||
}
|
||||
|
||||
const responses = orchestrator.getDebateResponses(debateId);
|
||||
|
||||
res.json({
|
||||
...debate,
|
||||
responses: responses.map(r => ({
|
||||
...r,
|
||||
content: JSON.parse(r.content)
|
||||
}))
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Error fetching debate:', error);
|
||||
res.status(500).json({ error: 'Failed to fetch debate' });
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* POST /api/debate/:id/response
|
||||
* Add a response to a debate
|
||||
*/
|
||||
router.post('/:id/response', (req, res) => {
|
||||
try {
|
||||
const debateId = parseInt(req.params.id);
|
||||
const { agentRole, content } = req.body;
|
||||
|
||||
if (!agentRole || !content) {
|
||||
return res.status(400).json({ error: 'Agent role and content are required' });
|
||||
}
|
||||
|
||||
const responseId = orchestrator.addResponse(debateId, agentRole, content);
|
||||
|
||||
res.json({
|
||||
responseId,
|
||||
debateId,
|
||||
agentRole,
|
||||
content
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Error adding response:', error);
|
||||
res.status(500).json({ error: 'Failed to add response' });
|
||||
}
|
||||
});
|
||||
|
||||
export default router;
|
||||
@ -1,236 +0,0 @@
|
||||
import db from '../db/schema.js';
|
||||
import { generateMultiAgentResponses } from './mistralClient.js';
|
||||
|
||||
class Orchestrator {
|
||||
constructor() {
|
||||
this.activeDebates = new Map();
|
||||
this.wsClients = new Map(); // debateId -> Set of WebSocket clients
|
||||
}
|
||||
|
||||
/**
|
||||
* Register WebSocket client for a debate
|
||||
*/
|
||||
registerWSClient(debateId, ws) {
|
||||
if (!this.wsClients.has(debateId)) {
|
||||
this.wsClients.set(debateId, new Set());
|
||||
}
|
||||
this.wsClients.get(debateId).add(ws);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregister WebSocket client
|
||||
*/
|
||||
unregisterWSClient(debateId, ws) {
|
||||
if (this.wsClients.has(debateId)) {
|
||||
this.wsClients.get(debateId).delete(ws);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Broadcast message to all clients watching a debate
|
||||
*/
|
||||
broadcast(debateId, message) {
|
||||
if (this.wsClients.has(debateId)) {
|
||||
const data = JSON.stringify(message);
|
||||
this.wsClients.get(debateId).forEach(ws => {
|
||||
if (ws.readyState === 1) { // OPEN
|
||||
ws.send(data);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new debate
|
||||
*/
|
||||
createDebate(prompt) {
|
||||
const stmt = db.prepare('INSERT INTO debates (prompt, status) VALUES (?, ?)');
|
||||
const result = stmt.run(prompt, 'ongoing');
|
||||
|
||||
const debateId = result.lastInsertRowid;
|
||||
this.activeDebates.set(debateId, {
|
||||
id: debateId,
|
||||
prompt,
|
||||
responses: [],
|
||||
startTime: Date.now()
|
||||
});
|
||||
|
||||
return debateId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get debate by ID
|
||||
*/
|
||||
getDebate(debateId) {
|
||||
const stmt = db.prepare('SELECT * FROM debates WHERE id = ?');
|
||||
return stmt.get(debateId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all responses for a debate
|
||||
*/
|
||||
getDebateResponses(debateId) {
|
||||
const stmt = db.prepare('SELECT * FROM responses WHERE debate_id = ? ORDER BY timestamp ASC');
|
||||
return stmt.all(debateId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a response to a debate
|
||||
*/
|
||||
addResponse(debateId, agentRole, content) {
|
||||
const stmt = db.prepare(
|
||||
'INSERT INTO responses (debate_id, agent_role, content) VALUES (?, ?, ?)'
|
||||
);
|
||||
const result = stmt.run(debateId, agentRole, JSON.stringify(content));
|
||||
|
||||
if (this.activeDebates.has(debateId)) {
|
||||
this.activeDebates.get(debateId).responses.push({
|
||||
agentRole,
|
||||
content,
|
||||
timestamp: new Date()
|
||||
});
|
||||
}
|
||||
|
||||
return result.lastInsertRowid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Complete a debate
|
||||
*/
|
||||
completeDebate(debateId) {
|
||||
const stmt = db.prepare('UPDATE debates SET status = ? WHERE id = ?');
|
||||
stmt.run('completed', debateId);
|
||||
this.activeDebates.delete(debateId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fail a debate
|
||||
*/
|
||||
failDebate(debateId) {
|
||||
const stmt = db.prepare('UPDATE debates SET status = ? WHERE id = ?');
|
||||
stmt.run('failed', debateId);
|
||||
this.activeDebates.delete(debateId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Select relevant agents based on prompt analysis
|
||||
*/
|
||||
selectAgents(prompt) {
|
||||
const agents = ['architect'];
|
||||
const lowerPrompt = prompt.toLowerCase();
|
||||
|
||||
// Analyze prompt for relevant expertise
|
||||
if (lowerPrompt.includes('api') || lowerPrompt.includes('backend') || lowerPrompt.includes('database')) {
|
||||
agents.push('backend_engineer');
|
||||
}
|
||||
if (lowerPrompt.includes('ui') || lowerPrompt.includes('frontend') || lowerPrompt.includes('interface')) {
|
||||
agents.push('frontend_engineer');
|
||||
}
|
||||
if (lowerPrompt.includes('design') || lowerPrompt.includes('ux') || lowerPrompt.includes('user')) {
|
||||
agents.push('designer');
|
||||
}
|
||||
|
||||
// Always include at least architect and one engineer
|
||||
if (agents.length === 1) {
|
||||
agents.push('backend_engineer', 'frontend_engineer');
|
||||
}
|
||||
|
||||
return agents;
|
||||
}
|
||||
|
||||
/**
|
||||
* Start AI debate - trigger agents and collect responses
|
||||
*/
|
||||
async startDebate(debateId, agents) {
|
||||
try {
|
||||
const debate = this.getDebate(debateId);
|
||||
if (!debate) {
|
||||
throw new Error('Debate not found');
|
||||
}
|
||||
|
||||
const prompt = debate.prompt;
|
||||
const context = this.getDebateResponses(debateId);
|
||||
|
||||
// Broadcast debate start
|
||||
this.broadcast(debateId, {
|
||||
type: 'debate_start',
|
||||
debateId,
|
||||
agents,
|
||||
message: 'AI agents are analyzing your project...'
|
||||
});
|
||||
|
||||
// Generate responses from all agents in parallel
|
||||
const agentResponses = await generateMultiAgentResponses(agents, prompt, context);
|
||||
|
||||
// Store responses and broadcast each one
|
||||
for (const { agent, response } of agentResponses) {
|
||||
const responseId = this.addResponse(debateId, agent, response);
|
||||
|
||||
this.broadcast(debateId, {
|
||||
type: 'agent_response',
|
||||
debateId,
|
||||
responseId,
|
||||
agent,
|
||||
response
|
||||
});
|
||||
}
|
||||
|
||||
// Calculate consensus
|
||||
const consensus = this.calculateConsensus(agentResponses);
|
||||
|
||||
// Complete debate
|
||||
this.completeDebate(debateId);
|
||||
|
||||
this.broadcast(debateId, {
|
||||
type: 'debate_complete',
|
||||
debateId,
|
||||
consensus,
|
||||
message: 'Debate completed successfully'
|
||||
});
|
||||
|
||||
return {
|
||||
responses: agentResponses,
|
||||
consensus
|
||||
};
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error in debate:', error);
|
||||
this.failDebate(debateId);
|
||||
|
||||
this.broadcast(debateId, {
|
||||
type: 'debate_error',
|
||||
debateId,
|
||||
error: error.message
|
||||
});
|
||||
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate consensus from agent responses
|
||||
*/
|
||||
calculateConsensus(agentResponses) {
|
||||
const proposals = agentResponses.map(({ agent, response }) => ({
|
||||
agent,
|
||||
proposal: response.proposal,
|
||||
confidence: response.confidence || 0.5
|
||||
}));
|
||||
|
||||
// Weight by confidence and architect gets 1.5x weight
|
||||
const totalWeight = proposals.reduce((sum, p) => {
|
||||
const weight = p.agent === 'architect' ? 1.5 : 1.0;
|
||||
return sum + (p.confidence * weight);
|
||||
}, 0);
|
||||
|
||||
const avgConfidence = totalWeight / proposals.length;
|
||||
|
||||
return {
|
||||
proposals,
|
||||
averageConfidence: avgConfidence,
|
||||
status: avgConfidence >= 0.6 ? 'consensus_reached' : 'needs_discussion'
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export default new Orchestrator();
|
||||
@ -33,7 +33,7 @@ function startNewCollaboration() {
|
||||
<!-- Session Mode -->
|
||||
<div v-else>
|
||||
<button @click="startNewCollaboration" class="new-session-btn">
|
||||
+ New Session
|
||||
New Session
|
||||
</button>
|
||||
|
||||
<CollaborativeSession
|
||||
|
||||
@ -85,10 +85,10 @@ const removeFile = () => {
|
||||
<div class="collaborative-input">
|
||||
<div class="container">
|
||||
<header class="header">
|
||||
<h1>🎯 AI Architecture Design</h1>
|
||||
<h1>AI Architecture Design</h1>
|
||||
<p class="subtitle">
|
||||
Describe your software project. Multiple AI specialists will collaboratively
|
||||
design the perfect architecture through iterative refinement.
|
||||
Describe your software project. AI specialists collaboratively design
|
||||
the architecture through iterative refinement.
|
||||
</p>
|
||||
</header>
|
||||
|
||||
@ -103,11 +103,11 @@ const removeFile = () => {
|
||||
id="prompt"
|
||||
v-model="prompt"
|
||||
@keydown="handleKeydown"
|
||||
placeholder="Example: I want to build a real-time collaborative document editing platform with support for multiple users, version history, and commenting..."
|
||||
placeholder="Example: Real-time collaborative document editing platform with multiple users, version history, and commenting..."
|
||||
class="textarea"
|
||||
rows="8"
|
||||
></textarea>
|
||||
<p class="hint">💡 The more detailed your description, the better the AI collaboration.</p>
|
||||
<p class="hint">The more detailed your description, the better the AI collaboration.</p>
|
||||
</div>
|
||||
|
||||
<!-- Optional Context File -->
|
||||
@ -126,18 +126,18 @@ const removeFile = () => {
|
||||
class="file-input"
|
||||
/>
|
||||
<label for="contextFile" class="file-label">
|
||||
📎 Choose a file (.md or .txt)
|
||||
Choose a file (.md or .txt)
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div v-if="contextFile" class="file-selected">
|
||||
<span class="file-name">✓ {{ contextFile.name }}</span>
|
||||
<span class="file-name">{{ contextFile.name }}</span>
|
||||
<button
|
||||
type="button"
|
||||
@click="removeFile"
|
||||
class="remove-btn"
|
||||
>
|
||||
✕
|
||||
Remove
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@ -159,7 +159,7 @@ const removeFile = () => {
|
||||
|
||||
<!-- Info Box -->
|
||||
<div class="info-box">
|
||||
<p><strong>How it works:</strong></p>
|
||||
<p><strong>Process:</strong></p>
|
||||
<ul>
|
||||
<li>Lead Architect creates initial design</li>
|
||||
<li>{{ agentCount }} specialists review iteratively</li>
|
||||
@ -177,7 +177,7 @@ const removeFile = () => {
|
||||
type="button"
|
||||
class="submit-btn"
|
||||
>
|
||||
{{ isCreating ? 'Starting Session...' : '✨ Start Design Session' }}
|
||||
{{ isCreating ? 'Starting...' : 'Start Design Session' }}
|
||||
</button>
|
||||
</form>
|
||||
|
||||
@ -398,10 +398,11 @@ const removeFile = () => {
|
||||
background: none;
|
||||
border: none;
|
||||
color: #d32f2f;
|
||||
font-size: 1.2rem;
|
||||
font-size: 0.85rem;
|
||||
cursor: pointer;
|
||||
padding: 0 0.5rem;
|
||||
padding: 0 0.75rem;
|
||||
transition: color 0.3s;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.remove-btn:hover {
|
||||
|
||||
@ -122,7 +122,7 @@ const viewVersion = (versionNumber) => {
|
||||
<div class="collaborative-session">
|
||||
<div class="session-header">
|
||||
<div class="header-content">
|
||||
<h1>Collaborative Design Session</h1>
|
||||
<h1>Design Session</h1>
|
||||
<p class="session-meta">
|
||||
<span>Session #{{ sessionId }}</span>
|
||||
<span class="badge" :class="{ active: sessionStarted }">
|
||||
@ -153,29 +153,29 @@ const viewVersion = (versionNumber) => {
|
||||
:disabled="!currentDocument"
|
||||
class="btn btn-outline"
|
||||
>
|
||||
⬇ Download
|
||||
Download
|
||||
</button>
|
||||
|
||||
<button
|
||||
@click="showTimeline = !showTimeline"
|
||||
class="btn btn-outline"
|
||||
>
|
||||
📋 Timeline
|
||||
Timeline
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Status Message -->
|
||||
<div v-if="hasConverged" class="convergence-message">
|
||||
✓ Convergence reached! All agents are satisfied with the document.
|
||||
Convergence reached. All agents satisfied.
|
||||
</div>
|
||||
|
||||
<!-- Agent List -->
|
||||
<div class="agents-section">
|
||||
<h3>Team Members ({{ agents.length }} agents)</h3>
|
||||
<h3>Team ({{ agents.length }} agents)</h3>
|
||||
<div class="agents-grid">
|
||||
<div v-for="agent in agents" :key="agent" class="agent-badge">
|
||||
👤 {{ formatAgentName(agent) }}
|
||||
{{ formatAgentName(agent) }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -1,367 +0,0 @@
|
||||
<template>
|
||||
<div class="debate-thread">
|
||||
<div class="debate-header">
|
||||
<h2>Debate #{{ debate.debateId }}</h2>
|
||||
<span class="status" :class="currentStatus">{{ currentStatus }}</span>
|
||||
</div>
|
||||
|
||||
<div class="prompt-display">
|
||||
<strong>Project Prompt:</strong>
|
||||
<p>{{ debate.prompt }}</p>
|
||||
</div>
|
||||
|
||||
<div class="agents">
|
||||
<strong>Participating Agents:</strong>
|
||||
<div class="agent-list">
|
||||
<span
|
||||
v-for="agent in debate.agents"
|
||||
:key="agent"
|
||||
class="agent-badge"
|
||||
:class="getAgentClass(agent)"
|
||||
>
|
||||
{{ formatAgentName(agent) }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Status messages -->
|
||||
<div v-if="statusMessage" class="status-message">
|
||||
{{ statusMessage }}
|
||||
</div>
|
||||
|
||||
<!-- Responses -->
|
||||
<div class="responses" v-if="allResponses.length > 0">
|
||||
<h3>Debate Responses</h3>
|
||||
<div
|
||||
v-for="(response, index) in allResponses"
|
||||
:key="index"
|
||||
class="response-card"
|
||||
:class="getAgentClass(response.agent || response.agent_role)"
|
||||
>
|
||||
<div class="response-header">
|
||||
<span class="agent-name">{{ formatAgentName(response.agent || response.agent_role) }}</span>
|
||||
<span class="timestamp">{{ formatTimestamp(response.timestamp) }}</span>
|
||||
</div>
|
||||
<div class="response-content">
|
||||
<div v-if="response.response">
|
||||
<p><strong>Proposal:</strong> {{ response.response.proposal }}</p>
|
||||
<p v-if="response.response.justification"><strong>Justification:</strong> {{ response.response.justification }}</p>
|
||||
<p v-if="response.response.confidence"><strong>Confidence:</strong> {{ Math.round(response.response.confidence * 100) }}%</p>
|
||||
<div v-if="response.response.dependencies && response.response.dependencies.length">
|
||||
<strong>Dependencies:</strong>
|
||||
<ul>
|
||||
<li v-for="dep in response.response.dependencies" :key="dep">{{ dep }}</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- Mermaid diagram rendering -->
|
||||
<div v-if="response.response.mermaid" class="mermaid-container" :ref="`mermaid-${index}`">
|
||||
<pre class="mermaid">{{ response.response.mermaid.code || response.response.mermaid }}</pre>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else>
|
||||
{{ typeof response.content === 'string' ? response.content : JSON.stringify(response.content, null, 2) }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-else class="no-responses">
|
||||
<div class="loading-spinner"></div>
|
||||
<p>AI agents are analyzing your project...</p>
|
||||
</div>
|
||||
|
||||
<!-- Consensus section -->
|
||||
<div v-if="consensus" class="consensus-section">
|
||||
<h3>Consensus</h3>
|
||||
<div class="consensus-card">
|
||||
<p><strong>Status:</strong> {{ consensus.status }}</p>
|
||||
<p><strong>Average Confidence:</strong> {{ Math.round(consensus.averageConfidence * 100) }}%</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed, watch, onMounted, nextTick } from 'vue'
|
||||
import { useWebSocket } from '../composables/useWebSocket'
|
||||
import mermaid from 'mermaid'
|
||||
|
||||
const props = defineProps({
|
||||
debate: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
})
|
||||
|
||||
const allResponses = ref([...(props.debate.responses || [])])
|
||||
const currentStatus = ref(props.debate.status || 'ongoing')
|
||||
const statusMessage = ref(null)
|
||||
const consensus = ref(null)
|
||||
|
||||
// Initialize Mermaid
|
||||
mermaid.initialize({
|
||||
startOnLoad: true,
|
||||
theme: 'default',
|
||||
securityLevel: 'loose'
|
||||
})
|
||||
|
||||
// WebSocket setup
|
||||
const { messages, connect } = useWebSocket(props.debate.debateId)
|
||||
|
||||
onMounted(() => {
|
||||
connect()
|
||||
renderMermaidDiagrams()
|
||||
})
|
||||
|
||||
// Watch for WebSocket messages
|
||||
watch(messages, (newMessages) => {
|
||||
const latestMessage = newMessages[newMessages.length - 1]
|
||||
if (!latestMessage) return
|
||||
|
||||
switch (latestMessage.type) {
|
||||
case 'debate_start':
|
||||
statusMessage.value = latestMessage.message
|
||||
currentStatus.value = 'ongoing'
|
||||
break
|
||||
|
||||
case 'agent_response':
|
||||
allResponses.value.push({
|
||||
agent: latestMessage.agent,
|
||||
response: latestMessage.response,
|
||||
timestamp: new Date().toISOString()
|
||||
})
|
||||
nextTick(() => renderMermaidDiagrams())
|
||||
break
|
||||
|
||||
case 'debate_complete':
|
||||
currentStatus.value = 'completed'
|
||||
statusMessage.value = latestMessage.message
|
||||
consensus.value = latestMessage.consensus
|
||||
break
|
||||
|
||||
case 'debate_error':
|
||||
currentStatus.value = 'failed'
|
||||
statusMessage.value = `Error: ${latestMessage.error}`
|
||||
break
|
||||
}
|
||||
}, { deep: true })
|
||||
|
||||
function formatAgentName(agent) {
|
||||
if (!agent) return ''
|
||||
return agent
|
||||
.split('_')
|
||||
.map(word => word.charAt(0).toUpperCase() + word.slice(1))
|
||||
.join(' ')
|
||||
}
|
||||
|
||||
function getAgentClass(agent) {
|
||||
const classes = {
|
||||
'architect': 'agent-architect',
|
||||
'backend_engineer': 'agent-backend',
|
||||
'frontend_engineer': 'agent-frontend',
|
||||
'designer': 'agent-designer'
|
||||
}
|
||||
return classes[agent] || 'agent-default'
|
||||
}
|
||||
|
||||
function formatTimestamp(timestamp) {
|
||||
if (!timestamp) return ''
|
||||
const date = new Date(timestamp)
|
||||
return date.toLocaleTimeString()
|
||||
}
|
||||
|
||||
async function renderMermaidDiagrams() {
|
||||
await nextTick()
|
||||
try {
|
||||
await mermaid.run({
|
||||
querySelector: '.mermaid'
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('Mermaid rendering error:', error)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.debate-thread {
|
||||
max-width: 900px;
|
||||
margin: 2rem auto;
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
.debate-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.status {
|
||||
padding: 0.5rem 1rem;
|
||||
border-radius: 20px;
|
||||
font-size: 0.9rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.status.ongoing {
|
||||
background-color: #3498db;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.status.completed {
|
||||
background-color: #2ecc71;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.status.failed {
|
||||
background-color: #e74c3c;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.prompt-display {
|
||||
background-color: #f8f9fa;
|
||||
padding: 1.5rem;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.prompt-display p {
|
||||
margin: 0.5rem 0 0 0;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.agents {
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.agent-list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.5rem;
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
|
||||
.agent-badge {
|
||||
padding: 0.5rem 1rem;
|
||||
border-radius: 6px;
|
||||
font-size: 0.9rem;
|
||||
font-weight: 500;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.agent-architect {
|
||||
background-color: #9b59b6;
|
||||
}
|
||||
|
||||
.agent-backend {
|
||||
background-color: #3498db;
|
||||
}
|
||||
|
||||
.agent-frontend {
|
||||
background-color: #1abc9c;
|
||||
}
|
||||
|
||||
.agent-designer {
|
||||
background-color: #e67e22;
|
||||
}
|
||||
|
||||
.agent-default {
|
||||
background-color: #95a5a6;
|
||||
}
|
||||
|
||||
.status-message {
|
||||
background-color: #e8f4f8;
|
||||
padding: 1rem;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 1.5rem;
|
||||
color: #2c3e50;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.responses h3 {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.response-card {
|
||||
background-color: white;
|
||||
border-left: 4px solid;
|
||||
padding: 1.5rem;
|
||||
margin-bottom: 1rem;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
.response-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.agent-name {
|
||||
font-weight: 600;
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
.timestamp {
|
||||
color: #7f8c8d;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.response-content {
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.response-content p {
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.response-content ul {
|
||||
margin-left: 1.5rem;
|
||||
}
|
||||
|
||||
.mermaid-container {
|
||||
margin-top: 1rem;
|
||||
padding: 1rem;
|
||||
background-color: #f8f9fa;
|
||||
border-radius: 8px;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.no-responses {
|
||||
text-align: center;
|
||||
padding: 3rem;
|
||||
color: #7f8c8d;
|
||||
}
|
||||
|
||||
.loading-spinner {
|
||||
margin: 0 auto 1rem;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border: 4px solid #e0e0e0;
|
||||
border-top-color: #667eea;
|
||||
border-radius: 50%;
|
||||
animation: spin 1s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
.consensus-section {
|
||||
margin-top: 2rem;
|
||||
padding-top: 2rem;
|
||||
border-top: 2px solid #e0e0e0;
|
||||
}
|
||||
|
||||
.consensus-card {
|
||||
background-color: #e8f5e9;
|
||||
padding: 1.5rem;
|
||||
border-radius: 8px;
|
||||
border-left: 4px solid #2ecc71;
|
||||
}
|
||||
|
||||
.consensus-card p {
|
||||
margin: 0.5rem 0;
|
||||
}
|
||||
</style>
|
||||
@ -64,12 +64,12 @@ function getStatusColor() {
|
||||
|
||||
function getStatusEmoji() {
|
||||
switch (quality.value) {
|
||||
case 'excellent': return '✓'
|
||||
case 'good': return '✓'
|
||||
case 'fair': return '⚠'
|
||||
case 'poor': return '⚠'
|
||||
case 'offline': return '✕'
|
||||
default: return '?'
|
||||
case 'excellent': return '●'
|
||||
case 'good': return '●'
|
||||
case 'fair': return '●'
|
||||
case 'poor': return '●'
|
||||
case 'offline': return '●'
|
||||
default: return '●'
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@ -1,133 +0,0 @@
|
||||
<template>
|
||||
<div class="prompt-input">
|
||||
<h1>Project Agora</h1>
|
||||
<p class="subtitle">AI-Powered Software Architecture Design</p>
|
||||
|
||||
<div class="input-container">
|
||||
<textarea
|
||||
v-model="prompt"
|
||||
placeholder="Describe your project idea... (e.g., 'Build a real-time chat application with user authentication')"
|
||||
rows="6"
|
||||
@keydown.ctrl.enter="handleSubmit"
|
||||
:disabled="loading"
|
||||
/>
|
||||
|
||||
<button
|
||||
@click="handleSubmit"
|
||||
:disabled="!prompt.trim() || loading"
|
||||
class="submit-btn"
|
||||
>
|
||||
{{ loading ? 'Creating Debate...' : 'Start AI Debate' }}
|
||||
</button>
|
||||
|
||||
<p v-if="error" class="error">{{ error }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import { useDebateStore } from '../stores/debate'
|
||||
|
||||
const emit = defineEmits(['debate-created'])
|
||||
|
||||
const debateStore = useDebateStore()
|
||||
const prompt = ref('')
|
||||
const loading = ref(false)
|
||||
const error = ref(null)
|
||||
|
||||
async function handleSubmit() {
|
||||
if (!prompt.value.trim()) return
|
||||
|
||||
loading.value = true
|
||||
error.value = null
|
||||
|
||||
try {
|
||||
const debate = await debateStore.createDebate(prompt.value)
|
||||
emit('debate-created', debate)
|
||||
prompt.value = ''
|
||||
} catch (err) {
|
||||
error.value = err.message
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.prompt-input {
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 3rem;
|
||||
margin-bottom: 0.5rem;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
background-clip: text;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
color: #666;
|
||||
margin-bottom: 2rem;
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
.input-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
textarea {
|
||||
width: 100%;
|
||||
padding: 1rem;
|
||||
border: 2px solid #e0e0e0;
|
||||
border-radius: 8px;
|
||||
font-family: inherit;
|
||||
font-size: 1rem;
|
||||
resize: vertical;
|
||||
transition: border-color 0.3s;
|
||||
}
|
||||
|
||||
textarea:focus {
|
||||
outline: none;
|
||||
border-color: #667eea;
|
||||
}
|
||||
|
||||
textarea:disabled {
|
||||
background-color: #f5f5f5;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.submit-btn {
|
||||
padding: 1rem 2rem;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
font-size: 1.1rem;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: transform 0.2s, opacity 0.2s;
|
||||
}
|
||||
|
||||
.submit-btn:hover:not(:disabled) {
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.submit-btn:disabled {
|
||||
opacity: 0.6;
|
||||
cursor: not-allowed;
|
||||
transform: none;
|
||||
}
|
||||
|
||||
.error {
|
||||
color: #e74c3c;
|
||||
font-size: 0.9rem;
|
||||
margin: 0;
|
||||
}
|
||||
</style>
|
||||
@ -1,97 +0,0 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import { ref, computed } from 'vue'
|
||||
|
||||
export const useDebateStore = defineStore('debate', () => {
|
||||
const currentDebate = ref(null)
|
||||
const debates = ref([])
|
||||
const loading = ref(false)
|
||||
const error = ref(null)
|
||||
|
||||
const API_URL = import.meta.env.VITE_API_URL || 'http://localhost:3000'
|
||||
|
||||
/**
|
||||
* Create a new debate
|
||||
*/
|
||||
async function createDebate(prompt) {
|
||||
loading.value = true
|
||||
error.value = null
|
||||
|
||||
try {
|
||||
const response = await fetch(`${API_URL}/api/debate`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({ prompt })
|
||||
})
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error('Failed to create debate')
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
currentDebate.value = data
|
||||
debates.value.unshift(data)
|
||||
|
||||
return data
|
||||
} catch (err) {
|
||||
error.value = err.message
|
||||
throw err
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get debate by ID
|
||||
*/
|
||||
async function getDebate(debateId) {
|
||||
loading.value = true
|
||||
error.value = null
|
||||
|
||||
try {
|
||||
const response = await fetch(`${API_URL}/api/debate/${debateId}`)
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error('Failed to fetch debate')
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
currentDebate.value = data
|
||||
|
||||
return data
|
||||
} catch (err) {
|
||||
error.value = err.message
|
||||
throw err
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a response to current debate
|
||||
*/
|
||||
function addResponse(response) {
|
||||
if (currentDebate.value && currentDebate.value.responses) {
|
||||
currentDebate.value.responses.push(response)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear current debate
|
||||
*/
|
||||
function clearCurrentDebate() {
|
||||
currentDebate.value = null
|
||||
}
|
||||
|
||||
return {
|
||||
currentDebate,
|
||||
debates,
|
||||
loading,
|
||||
error,
|
||||
createDebate,
|
||||
getDebate,
|
||||
addResponse,
|
||||
clearCurrentDebate
|
||||
}
|
||||
})
|
||||
Loading…
x
Reference in New Issue
Block a user