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
|
## 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 :
|
L'objectif est de produire :
|
||||||
|
|
||||||
@ -77,24 +77,17 @@ graph TD
|
|||||||
|
|
||||||
## Fonctionnalités
|
## Fonctionnalités
|
||||||
|
|
||||||
### Deux modes de collaboration
|
### Conception Collaborative par IA
|
||||||
|
|
||||||
#### 1. **Mode Débat Classique** (Original)
|
- **7 agents spécialisés** : Architecte, Backend Engineer, Frontend Engineer, UI Designer, DevOps Engineer, Product Manager, Security Specialist
|
||||||
- **4 agents spécialisés** qui débattent en parallèle : Architecte logiciel, Ingénieur backend, Ingénieur frontend, Designer UI/UX
|
- **Création itérative** : Lead Architect crée le document initial
|
||||||
- **Sélection automatique** des agents pertinents selon le contexte du projet
|
- **Tours de table** : Chaque agent revoit et propose des améliorations
|
||||||
- **Débat collaboratif** : Les agents échangent et négocient pour converger vers la meilleure solution
|
- **Document évolutif** : Amélioration progressive à chaque passage
|
||||||
- **Système de consensus** avec vote pondéré (l'architecte a une voix prépondérante)
|
- **Convergence naturelle** : Arrêt automatique quand tous les agents sont satisfaits
|
||||||
- Résultats reçus simultanément
|
- **Traçabilité complète** : Historique de toutes les modifications
|
||||||
|
- **Format Markdown** : Output toujours en Markdown (.md)
|
||||||
#### 2. **Mode Conception Collaborative** (Nouveau 🚀)
|
- **Context files** : Support d'upload de fichiers MD/TXT en entrée (optionnel)
|
||||||
- **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é
|
- **Network monitoring** : Indicateur temps réel de latence et qualité réseau
|
||||||
- **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é
|
|
||||||
|
|
||||||
### Système multi-agents IA
|
### Système multi-agents IA
|
||||||
- **Intégration Mistral AI** pour génération de réponses intelligentes et contextuelles
|
- **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
|
```mermaid
|
||||||
sequenceDiagram
|
sequenceDiagram
|
||||||
participant User
|
participant User
|
||||||
participant Orchestrator
|
participant Backend
|
||||||
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 Agents
|
participant Agents
|
||||||
User->>Orchestrator: Prompt project + format choice
|
User->>Backend: Project description + optional context file
|
||||||
Orchestrator->>Lead_Architect: Create initial document
|
Backend->>Agents: Lead Architect creates initial document
|
||||||
Lead_Architect-->>Orchestrator: v1 Document
|
Agents-->>Backend: Document v1
|
||||||
Orchestrator->>Agents: Review document Round 1
|
Backend->>Agents: Round 1: Review and improve
|
||||||
Agents-->>Orchestrator: Modifications proposed
|
Agents-->>Backend: Updates proposed
|
||||||
Orchestrator->>Agents: Review document Round 2
|
Backend->>Agents: Round 2-N: Review updated document
|
||||||
Agents-->>Orchestrator: No changes OR more modifications
|
Agents-->>Backend: More updates OR no changes
|
||||||
Orchestrator-->>User: Final document + full history
|
Backend-->>User: Final Markdown document + history
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Utilisation du Mode Conception Collaborative
|
## Utilisation
|
||||||
|
|
||||||
### Étapes
|
### Étapes
|
||||||
|
|
||||||
1. **Sélectionner le mode** : Cliquez sur "Collaborative Design" depuis l'écran d'accueil
|
1. **Décrire le projet** : Entrez une description détaillée
|
||||||
2. **Décrire le projet** : Entrez une description détaillée de votre projet logiciel
|
2. **Ajouter un contexte** (optionnel) : Upload un fichier MD ou TXT
|
||||||
3. **Configurer** :
|
3. **Sélectionner** : Nombre d'agents (3, 5, ou 7)
|
||||||
- **Format du document** : Choisissez entre Markdown (.md) ou Texte brut (.txt)
|
4. **Lancer** : Start Design Session
|
||||||
- **Nombre d'agents** : 3 (Quick), 5 (Balanced), ou 7 (Comprehensive)
|
5. **Suivre** :
|
||||||
4. **Lancer la session** : Cliquez sur "Start Collaborative Design Session"
|
- Visualisez le document en temps réel
|
||||||
5. **Suivre la progression** :
|
- Consultez la timeline
|
||||||
- Visualisez le document en évolution en temps réel
|
- Lancez les tours avec "Next Review Round"
|
||||||
- Consultez la timeline pour voir qui a fait quoi
|
6. **Résultat** : Document Markdown téléchargeable
|
||||||
- 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
|
|
||||||
|
|
||||||
### Exemple d'utilisation
|
### Exemple
|
||||||
|
|
||||||
**Prompt utilisateur** :
|
Entrée:
|
||||||
```
|
```
|
||||||
Je veux créer une plateforme de gestion de projets collaboratifs en temps réel,
|
Plateforme gestion projets temps réel, équipes distribuées,
|
||||||
avec support pour les équipes distribuées. Fonctionnalités clés: gestion des tâches,
|
gestion tâches, communication, partage fichiers, 10K+ users
|
||||||
communication temps réel, partage de fichiers, intégrations externes.
|
|
||||||
Besoin de scalabilité pour 10,000+ utilisateurs simultanés.
|
|
||||||
```
|
```
|
||||||
|
|
||||||
**Résultat** :
|
Processus:
|
||||||
- **Round 1** : Lead Architect crée le document initial avec architecture générale
|
- Round 1: Lead Architect - Architecture générale
|
||||||
- **Round 2** : Backend Engineer revoit et ajoute détails API et base de données
|
- Round 2: Backend - APIs et base de données
|
||||||
- **Round 3** : Frontend Engineer améliore avec structure UI et composants
|
- Round 3: Frontend - Structure UI
|
||||||
- **Round 4** : UI Designer ajoute guidelines UX et patterns
|
- Round 4: UI Designer - Guidelines UX
|
||||||
- **Round 5** : DevOps Engineer propose infrastructure et déploiement
|
- Round 5: DevOps - Infrastructure
|
||||||
- **Round 6** : Product Manager aligne avec besoins métier
|
- Round 6: Product Manager - Besoins métier
|
||||||
- **Round 7** : Security Specialist ajoute mesures de sécurité
|
- Round 7: Security - Mesures sécurité
|
||||||
- **Convergence** : Plus aucune modification proposée ✓
|
|
||||||
|
|
||||||
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
|
// Enable foreign keys
|
||||||
db.pragma('foreign_keys = ON');
|
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
|
// Create collaborative sessions table
|
||||||
db.exec(`
|
db.exec(`
|
||||||
CREATE TABLE IF NOT EXISTS collaborative_sessions (
|
CREATE TABLE IF NOT EXISTS collaborative_sessions (
|
||||||
|
|||||||
@ -7,9 +7,7 @@ import { WebSocketServer } from 'ws';
|
|||||||
import rateLimit from 'express-rate-limit';
|
import rateLimit from 'express-rate-limit';
|
||||||
import { parse } from 'url';
|
import { parse } from 'url';
|
||||||
import db from './db/schema.js';
|
import db from './db/schema.js';
|
||||||
import debateRoutes from './routes/debate.js';
|
|
||||||
import collaborateRoutes from './routes/collaborate.js';
|
import collaborateRoutes from './routes/collaborate.js';
|
||||||
import orchestrator from './services/orchestrator.js';
|
|
||||||
import collaborativeOrchestrator from './services/collaborativeOrchestrator.js';
|
import collaborativeOrchestrator from './services/collaborativeOrchestrator.js';
|
||||||
|
|
||||||
dotenv.config();
|
dotenv.config();
|
||||||
@ -38,21 +36,9 @@ app.use('/api', limiter);
|
|||||||
// WebSocket connection handling
|
// WebSocket connection handling
|
||||||
wss.on('connection', (ws, req) => {
|
wss.on('connection', (ws, req) => {
|
||||||
const { query } = parse(req.url, true);
|
const { query } = parse(req.url, true);
|
||||||
const debateId = query.debateId ? parseInt(query.debateId) : null;
|
|
||||||
const sessionId = query.sessionId ? parseInt(query.sessionId) : null;
|
const sessionId = query.sessionId ? parseInt(query.sessionId) : null;
|
||||||
|
|
||||||
console.log('New WebSocket connection established',
|
console.log('WebSocket connection', sessionId ? `session ${sessionId}` : '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'
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sessionId) {
|
if (sessionId) {
|
||||||
collaborativeOrchestrator.registerWSClient(sessionId, ws);
|
collaborativeOrchestrator.registerWSClient(sessionId, ws);
|
||||||
@ -60,7 +46,7 @@ wss.on('connection', (ws, req) => {
|
|||||||
ws.send(JSON.stringify({
|
ws.send(JSON.stringify({
|
||||||
type: 'connected',
|
type: 'connected',
|
||||||
sessionId,
|
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());
|
const data = JSON.parse(message.toString());
|
||||||
console.log('Received:', data);
|
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) {
|
if (data.type === 'subscribe' && data.sessionId) {
|
||||||
collaborativeOrchestrator.registerWSClient(parseInt(data.sessionId), ws);
|
collaborativeOrchestrator.registerWSClient(parseInt(data.sessionId), ws);
|
||||||
ws.send(JSON.stringify({
|
ws.send(JSON.stringify({
|
||||||
@ -92,22 +68,18 @@ wss.on('connection', (ws, req) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
ws.on('close', () => {
|
ws.on('close', () => {
|
||||||
if (debateId) {
|
|
||||||
orchestrator.unregisterWSClient(debateId, ws);
|
|
||||||
}
|
|
||||||
if (sessionId) {
|
if (sessionId) {
|
||||||
collaborativeOrchestrator.unregisterWSClient(sessionId, ws);
|
collaborativeOrchestrator.unregisterWSClient(sessionId, ws);
|
||||||
}
|
}
|
||||||
console.log('WebSocket connection closed');
|
console.log('WebSocket closed');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// Routes
|
// Routes
|
||||||
app.get('/api/health', (req, res) => {
|
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);
|
app.use('/api/collaborate', collaborateRoutes);
|
||||||
|
|
||||||
// Start server
|
// 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 -->
|
<!-- Session Mode -->
|
||||||
<div v-else>
|
<div v-else>
|
||||||
<button @click="startNewCollaboration" class="new-session-btn">
|
<button @click="startNewCollaboration" class="new-session-btn">
|
||||||
+ New Session
|
New Session
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<CollaborativeSession
|
<CollaborativeSession
|
||||||
|
|||||||
@ -85,10 +85,10 @@ const removeFile = () => {
|
|||||||
<div class="collaborative-input">
|
<div class="collaborative-input">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<header class="header">
|
<header class="header">
|
||||||
<h1>🎯 AI Architecture Design</h1>
|
<h1>AI Architecture Design</h1>
|
||||||
<p class="subtitle">
|
<p class="subtitle">
|
||||||
Describe your software project. Multiple AI specialists will collaboratively
|
Describe your software project. AI specialists collaboratively design
|
||||||
design the perfect architecture through iterative refinement.
|
the architecture through iterative refinement.
|
||||||
</p>
|
</p>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
@ -103,11 +103,11 @@ const removeFile = () => {
|
|||||||
id="prompt"
|
id="prompt"
|
||||||
v-model="prompt"
|
v-model="prompt"
|
||||||
@keydown="handleKeydown"
|
@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"
|
class="textarea"
|
||||||
rows="8"
|
rows="8"
|
||||||
></textarea>
|
></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>
|
</div>
|
||||||
|
|
||||||
<!-- Optional Context File -->
|
<!-- Optional Context File -->
|
||||||
@ -126,18 +126,18 @@ const removeFile = () => {
|
|||||||
class="file-input"
|
class="file-input"
|
||||||
/>
|
/>
|
||||||
<label for="contextFile" class="file-label">
|
<label for="contextFile" class="file-label">
|
||||||
📎 Choose a file (.md or .txt)
|
Choose a file (.md or .txt)
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="contextFile" class="file-selected">
|
<div v-if="contextFile" class="file-selected">
|
||||||
<span class="file-name">✓ {{ contextFile.name }}</span>
|
<span class="file-name">{{ contextFile.name }}</span>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
@click="removeFile"
|
@click="removeFile"
|
||||||
class="remove-btn"
|
class="remove-btn"
|
||||||
>
|
>
|
||||||
✕
|
Remove
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -159,7 +159,7 @@ const removeFile = () => {
|
|||||||
|
|
||||||
<!-- Info Box -->
|
<!-- Info Box -->
|
||||||
<div class="info-box">
|
<div class="info-box">
|
||||||
<p><strong>How it works:</strong></p>
|
<p><strong>Process:</strong></p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>Lead Architect creates initial design</li>
|
<li>Lead Architect creates initial design</li>
|
||||||
<li>{{ agentCount }} specialists review iteratively</li>
|
<li>{{ agentCount }} specialists review iteratively</li>
|
||||||
@ -177,7 +177,7 @@ const removeFile = () => {
|
|||||||
type="button"
|
type="button"
|
||||||
class="submit-btn"
|
class="submit-btn"
|
||||||
>
|
>
|
||||||
{{ isCreating ? 'Starting Session...' : '✨ Start Design Session' }}
|
{{ isCreating ? 'Starting...' : 'Start Design Session' }}
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
@ -398,10 +398,11 @@ const removeFile = () => {
|
|||||||
background: none;
|
background: none;
|
||||||
border: none;
|
border: none;
|
||||||
color: #d32f2f;
|
color: #d32f2f;
|
||||||
font-size: 1.2rem;
|
font-size: 0.85rem;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
padding: 0 0.5rem;
|
padding: 0 0.75rem;
|
||||||
transition: color 0.3s;
|
transition: color 0.3s;
|
||||||
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
|
|
||||||
.remove-btn:hover {
|
.remove-btn:hover {
|
||||||
|
|||||||
@ -122,7 +122,7 @@ const viewVersion = (versionNumber) => {
|
|||||||
<div class="collaborative-session">
|
<div class="collaborative-session">
|
||||||
<div class="session-header">
|
<div class="session-header">
|
||||||
<div class="header-content">
|
<div class="header-content">
|
||||||
<h1>Collaborative Design Session</h1>
|
<h1>Design Session</h1>
|
||||||
<p class="session-meta">
|
<p class="session-meta">
|
||||||
<span>Session #{{ sessionId }}</span>
|
<span>Session #{{ sessionId }}</span>
|
||||||
<span class="badge" :class="{ active: sessionStarted }">
|
<span class="badge" :class="{ active: sessionStarted }">
|
||||||
@ -153,29 +153,29 @@ const viewVersion = (versionNumber) => {
|
|||||||
:disabled="!currentDocument"
|
:disabled="!currentDocument"
|
||||||
class="btn btn-outline"
|
class="btn btn-outline"
|
||||||
>
|
>
|
||||||
⬇ Download
|
Download
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
@click="showTimeline = !showTimeline"
|
@click="showTimeline = !showTimeline"
|
||||||
class="btn btn-outline"
|
class="btn btn-outline"
|
||||||
>
|
>
|
||||||
📋 Timeline
|
Timeline
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Status Message -->
|
<!-- Status Message -->
|
||||||
<div v-if="hasConverged" class="convergence-message">
|
<div v-if="hasConverged" class="convergence-message">
|
||||||
✓ Convergence reached! All agents are satisfied with the document.
|
Convergence reached. All agents satisfied.
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Agent List -->
|
<!-- Agent List -->
|
||||||
<div class="agents-section">
|
<div class="agents-section">
|
||||||
<h3>Team Members ({{ agents.length }} agents)</h3>
|
<h3>Team ({{ agents.length }} agents)</h3>
|
||||||
<div class="agents-grid">
|
<div class="agents-grid">
|
||||||
<div v-for="agent in agents" :key="agent" class="agent-badge">
|
<div v-for="agent in agents" :key="agent" class="agent-badge">
|
||||||
👤 {{ formatAgentName(agent) }}
|
{{ formatAgentName(agent) }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</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() {
|
function getStatusEmoji() {
|
||||||
switch (quality.value) {
|
switch (quality.value) {
|
||||||
case 'excellent': return '✓'
|
case 'excellent': return '●'
|
||||||
case 'good': return '✓'
|
case 'good': return '●'
|
||||||
case 'fair': return '⚠'
|
case 'fair': return '●'
|
||||||
case 'poor': return '⚠'
|
case 'poor': return '●'
|
||||||
case 'offline': return '✕'
|
case 'offline': return '●'
|
||||||
default: return '?'
|
default: return '●'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</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