Compare commits

...

3 Commits

Author SHA1 Message Date
33e2434e71 Update README documentation for current architecture
- Update objective to reflect N agents (3-50) instead of fixed 7 roles
- Update AI layer to reference mistral-large-2411 model
- Add section-based editing and convergence details
- Remove outdated 'network monitoring' feature
- Add session discovery features
- Update features list to include: dynamic agents, random names, raised hand animation, real-time visualization
- Update usage guide to reflect new session discovery UI
- Update example to use dynamic agents with random names
- Clarify automatic process and consensus convergence
- Add info about accessing previous sessions
2025-10-18 23:53:02 +02:00
1dde7dfc5b Improve session discovery UI with intuitive navigation
- Add stats bar showing completed/in-progress/total sessions count
- Add status filter tabs (All, Completed, In Progress)
- Add horizontal carousel display for recent sessions
- Add 'View All Sessions' expandable button
- Add visual divider between recent sessions and new session form
- Improve session cards with better information display
- Make sessions more discoverable and easier to access
- Add filtering and sorting capabilities for better UX
2025-10-18 23:52:07 +02:00
56cf1a3efd Clean up dead code and unused imports
- Remove unused Readable import from mistralClient.js
- Remove unused generateAgentResponse streaming function
- Remove unused parseStreamResponse helper function
- Remove unused updateDocumentFromMessage from collaboration store
- Remove unused documentVersions state from store
- Remove unused getRandomName function from nameGenerator.js
- Clean up store exports and clearCurrentSession function
2025-10-18 23:50:39 +02:00
5 changed files with 380 additions and 151 deletions

View File

@ -19,17 +19,13 @@ Aucune génération de code direct n'est effectuée : seules des **explications,
## Objectif ## Objectif
Reproduire une **table ronde d'IA spécialistes**, chaque agent IA représentant un rôle spécifique : Reproduire une **table ronde d'IA spécialistes**, avec un nombre d'agents flexible et configurable :
- Lead Architect (Architecte logiciel) - **Agents dynamiques** (3-50) avec des noms aléatoires pour la diversité
- Backend Engineer (Développeur backend) - Chaque agent à une perspective unique sur l'architecture logicielle
- Frontend Engineer (Développeur frontend) - Les agents se spécialisent naturellement dans différents domaines
- UI Designer (Designer UI/UX)
- DevOps Engineer (Ingénieur DevOps)
- Product Manager (Chef de projet)
- Security Specialist (Spécialiste sécurité)
L'aspect le plus crucial de ces IA réside dans leur capacité à **collaborer itérativement** pour améliorer collectivement la spécification architecturale jusqu'à convergence naturelle (quand plus aucune amélioration n'est proposée). L'aspect le plus crucial de ces IA réside dans leur capacité à **collaborer itérativement** pour améliorer collectivement la spécification architecturale. Le processus s'arrête naturellement quand **tous les agents convergent** (plus aucune amélioration n'est proposée sur N tours consécutifs).
--- ---
@ -57,9 +53,10 @@ Elles discutent pour converger vers un consensus, puis produisent une **fiche co
- **Frontend**: Vue.js 3 + Vite - **Frontend**: Vue.js 3 + Vite
- **Backend**: Node.js + Express - **Backend**: Node.js + Express
- **AI Layer**: Multi-agent système avec Mistral API - **AI Layer**: Multi-agent système avec Mistral API (mistral-large-2411)
- **Storage**: SQLite + JSON backup - **Storage**: SQLite pour persistance complète
- **Diagrammes**: Mermaid.js - **Real-time**: WebSocket pour streaming temps réel des modifications
- **Design**: Glassmorphism avec animations fluides
### Architecture globale ### Architecture globale
@ -79,28 +76,36 @@ graph TD
### Conception Collaborative par IA ### Conception Collaborative par IA
- **7 agents spécialisés** : Architecte, Backend Engineer, Frontend Engineer, UI Designer, DevOps Engineer, Product Manager, Security Specialist - **N agents dynamiques** (3-50) : Nombre configurable pour chaque session
- **Création itérative** : Lead Architect crée le document initial - **Noms aléatoires** : Chaque agent a un nom unique pour meilleure clarté
- **Tours de table** : Chaque agent revoit et propose des améliorations - **Création itérative** : SYSTEM crée la structure de base
- **Tours de table automatiques** : Chaque agent revoit et peut modifier une section
- **Document évolutif** : Amélioration progressive à chaque passage - **Document évolutif** : Amélioration progressive à chaque passage
- **Convergence naturelle** : Arrêt automatique quand tous les agents sont satisfaits - **Opérations supportées** : Modification, création, ou suppression de sections
- **Traçabilité complète** : Historique de toutes les modifications - **Convergence naturelle** : Arrêt automatique quand N agents de suite ne proposent aucun changement
- **Traçabilité complète** : Historique de toutes les modifications avec agent, raison, et round
- **Format Markdown** : Output toujours en Markdown (.md) - **Format Markdown** : Output toujours en Markdown (.md)
- **Context files** : Support d'upload de fichiers MD/TXT en entrée (optionnel) - **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 - **Session discovery** : Accès facile aux sessions précédentes avec filtres et stats
- **Real-time visualization** : Observation des changements en direct via WebSocket
### 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** (mistral-large-2411) pour génération intelligente et contextualisée
- **System prompts spécialisés** pour chaque rôle d'agent - **System prompts intelligents** : Prompt générique paramétré par agent
- **Contexte intelligent** : Les agents voient l'historique et le document actuel - **Contexte intelligent** : Les agents voient le document actuel et sa version antérieure
- **Section-based editing** : Les agents modifient une section à la fois pour cohérence
### Interface et visualisation ### Interface et visualisation
- **Saisie de prompt** décrivant le projet souhaité - **Saisie de prompt** décrivant le projet souhaité
- **Upload optionnel** de fichiers contexte (MD/TXT) - **Upload optionnel** de fichiers contexte (MD/TXT)
- **Sélection dynamique** du nombre d'agents (3-50)
- **Visualisation en temps réel** des modifications via WebSocket - **Visualisation en temps réel** des modifications via WebSocket
- **Timeline d'évolution** du document avec historique complet - **Timeline interactive** montrant chaque round et les agents ayant participé
- **Affichage interactif** du document Markdown final - **Raised hand animation** montrant l'agent actuel qui réfléchit/modifie
- **Indicateurs de progression** et statuts du session - **Affichage interactif** du document Markdown avec syntaxe colorée
- **Session discovery** avec stats (complétées, en cours, total)
- **Filtres par statut** pour accéder facilement aux sessions
- **Historique des sessions** accessible depuis la page d'accueil
### Architecture et stockage ### Architecture et stockage
- **API REST** pour gestion des sessions collaboratives - **API REST** pour gestion des sessions collaboratives
@ -165,15 +170,28 @@ sequenceDiagram
### Étapes ### Étapes
1. **Décrire le projet** : Entrez une description détaillée 1. **Accédez à la page d'accueil** : Consultez les sessions précédentes
2. **Ajouter un contexte** (optionnel) : Upload un fichier MD ou TXT - Visualisez les stats (complétées, en cours, total)
3. **Sélectionner** : Nombre d'agents (3, 5, ou 7) - Filtrez par statut si nécessaire
4. **Lancer** : Start Design Session - Cliquez sur une session pour la reprendre
5. **Suivre** : 2. **Ou créez une nouvelle session** :
- Décrivez le projet en détail
- Ajouter un contexte (optionnel) : Upload un fichier MD ou TXT
- Sélectionnez le nombre d'agents (3-50)
- Lancez "Start Design Session"
3. **Observez la collaboration** :
- Visualisez le document en temps réel - Visualisez le document en temps réel
- Consultez la timeline - Consultez la timeline avec les rounds et les agents ayant participé
- Lancez les tours avec "Next Review Round" - Voir l'animation "main levée" pour l'agent actuel
6. **Résultat** : Document Markdown téléchargeable 4. **Processus automatique** :
- Chaque agent revoit le document
- Peut modifier, créer, ou supprimer une section
- Les rounds se font automatiquement
- Le processus s'arrête quand consensus est atteint
5. **Résultat** :
- Document Markdown téléchargeable
- Historique complet des modifications
- Session reste accessible pour consultation
### Exemple ### Exemple
@ -183,16 +201,12 @@ Plateforme gestion projets temps réel, équipes distribuées,
gestion tâches, communication, partage fichiers, 10K+ users gestion tâches, communication, partage fichiers, 10K+ users
``` ```
Processus: Processus (avec 7 agents aux noms aléatoires):
- Round 1: Lead Architect - Architecture générale - Round 1: SYSTEM crée la structure de base
- Round 2: Backend - APIs et base de données - Round 2: Chaque agent revoit et propose des améliorations
- Round 3: Frontend - Structure UI - Round 3+: Itération jusqu'à convergence
- Round 4: UI Designer - Guidelines UX
- Round 5: DevOps - Infrastructure
- Round 6: Product Manager - Besoins métier
- Round 7: Security - Mesures sécurité
Sortie: Spécification architecturale Markdown complète Résultat: Spécification architecturale Markdown complète avec traçabilité des changements
--- ---

View File

@ -1,5 +1,4 @@
import dotenv from 'dotenv' import dotenv from 'dotenv'
import { Readable } from 'stream'
dotenv.config() dotenv.config()
@ -69,69 +68,6 @@ async function callMistralAPI(messages, options = {}) {
return response return response
} }
/**
* Parse streaming response
*/
async function* parseStreamResponse(reader) {
const decoder = new TextDecoder()
let buffer = ''
while (true) {
const { done, value } = await reader.read()
if (done) break
buffer += decoder.decode(value, { stream: true })
const lines = buffer.split('\n')
buffer = lines.pop() || ''
for (const line of lines) {
if (line.startsWith('data: ')) {
const data = line.slice(6)
if (data === '[DONE]') continue
try {
const json = JSON.parse(data)
if (json.choices?.[0]?.delta?.content) {
yield json.choices[0].delta.content
}
} catch (e) {
// Skip invalid JSON
}
}
}
}
}
/**
* Generate agent response with streaming thoughts
*/
export async function* generateAgentResponse(agentName, prompt, currentDocument = '', onThought = null) {
const systemPrompt = getAgentPrompt(agentName)
const messages = [
{ role: 'system', content: systemPrompt },
{ role: 'user', content: `Project description: ${prompt}\n\nCurrent document:\n${currentDocument}` }
]
try {
const response = await callMistralAPI(messages, { stream: true })
const reader = response.body.getReader()
let fullContent = ''
for await (const chunk of parseStreamResponse(reader)) {
fullContent += chunk
if (onThought) {
onThought(chunk)
}
yield chunk
}
return fullContent
} catch (error) {
console.error(`Error generating response from ${agentName}:`, error)
return `Error: ${error.message}`
}
}
/** /**
* Generate agent response (non-streaming version for simpler integration) * Generate agent response (non-streaming version for simpler integration)
*/ */

View File

@ -31,7 +31,3 @@ export function getRandomNames(count) {
const shuffled = [...allNames].sort(() => Math.random() - 0.5) const shuffled = [...allNames].sort(() => Math.random() - 0.5)
return shuffled.slice(0, count) return shuffled.slice(0, count)
} }
export function getRandomName() {
return allNames[Math.floor(Math.random() * allNames.length)]
}

View File

@ -12,6 +12,8 @@ const agentCount = ref(7)
const isCreating = ref(false) const isCreating = ref(false)
const previousSessions = ref([]) const previousSessions = ref([])
const loadingPreviousSessions = ref(false) const loadingPreviousSessions = ref(false)
const showAllSessions = ref(false)
const sessionStatusFilter = ref('all') // 'all', 'completed', 'ongoing', 'created'
onMounted(async () => { onMounted(async () => {
loadingPreviousSessions.value = true loadingPreviousSessions.value = true
@ -37,6 +39,28 @@ const agentOptions = computed(() => {
})) }))
}) })
const filteredSessions = computed(() => {
let filtered = previousSessions.value
if (sessionStatusFilter.value !== 'all') {
filtered = filtered.filter(s => s.status === sessionStatusFilter.value)
}
return filtered
})
const displayedSessions = computed(() => {
return showAllSessions.value ? filteredSessions.value : filteredSessions.value.slice(0, 6)
})
const completedCount = computed(() => {
return previousSessions.value.filter(s => s.status === 'completed').length
})
const ongoingCount = computed(() => {
return previousSessions.value.filter(s => s.status === 'ongoing').length
})
const handleFileSelect = (event) => { const handleFileSelect = (event) => {
const file = event.target.files?.[0] const file = event.target.files?.[0]
if (file) { if (file) {
@ -112,6 +136,88 @@ const removeFile = () => {
</p> </p>
</header> </header>
<!-- Quick Access Section -->
<div v-if="previousSessions.length > 0" class="quick-access-section">
<div class="stats-bar">
<div class="stat">
<span class="stat-label">Completed</span>
<span class="stat-value">{{ completedCount }}</span>
</div>
<div class="stat">
<span class="stat-label">In Progress</span>
<span class="stat-value">{{ ongoingCount }}</span>
</div>
<div class="stat">
<span class="stat-label">Total</span>
<span class="stat-value">{{ previousSessions.length }}</span>
</div>
</div>
<!-- Status Filter -->
<div class="filter-tabs">
<button
@click="sessionStatusFilter = 'all'"
class="filter-btn"
:class="{ active: sessionStatusFilter === 'all' }"
>
All Sessions
</button>
<button
@click="sessionStatusFilter = 'completed'"
class="filter-btn"
:class="{ active: sessionStatusFilter === 'completed' }"
>
Completed
</button>
<button
@click="sessionStatusFilter = 'ongoing'"
class="filter-btn"
:class="{ active: sessionStatusFilter === 'ongoing' }"
>
In Progress
</button>
</div>
<!-- Recent Sessions Grid -->
<div class="recent-sessions-scroll">
<div class="sessions-carousel">
<div
v-for="session in displayedSessions"
:key="session.sessionId"
@click="handleOpenSession(session.sessionId)"
class="session-card-horizontal"
:class="{ 'status-' + session.status }"
:title="session.prompt"
>
<div class="card-left">
<div class="session-id">#{{ session.sessionId }}</div>
</div>
<div class="card-main">
<p class="session-prompt-short">{{ session.prompt.substring(0, 75) }}{{ session.prompt.length > 75 ? '...' : '' }}</p>
<p class="session-meta">{{ new Date(session.createdAt).toLocaleDateString() }}</p>
</div>
<div class="card-right">
<span class="status-badge">{{ session.status }}</span>
</div>
</div>
</div>
</div>
<div v-if="filteredSessions.length > 6" class="view-all-container">
<button
@click="showAllSessions = !showAllSessions"
class="view-all-btn"
>
{{ showAllSessions ? 'Show Less' : `View All (${filteredSessions.length} sessions)` }}
</button>
</div>
</div>
<!-- Divider -->
<div v-if="previousSessions.length > 0" class="divider">
<span>Or start a new session</span>
</div>
<form @submit.prevent="handleCreateSession" class="form"> <form @submit.prevent="handleCreateSession" class="form">
<!-- Project Description --> <!-- Project Description -->
<div class="form-section"> <div class="form-section">
@ -201,27 +307,6 @@ const removeFile = () => {
</button> </button>
</form> </form>
<!-- Previous Sessions -->
<div v-if="previousSessions.length > 0" class="previous-sessions-section">
<h2>Recent Sessions</h2>
<div class="sessions-grid">
<div
v-for="session in previousSessions.slice(0, 6)"
:key="session.sessionId"
@click="handleOpenSession(session.sessionId)"
class="session-card"
:class="{ 'status-completed': session.status === 'completed' }"
>
<div class="session-header">
<span class="session-id">#{{ session.sessionId }}</span>
<span class="session-status">{{ session.status }}</span>
</div>
<p class="session-prompt">{{ session.prompt.substring(0, 100) }}...</p>
<p class="session-date">{{ new Date(session.createdAt).toLocaleDateString() }}</p>
</div>
</div>
</div>
<footer class="footer"> <footer class="footer">
<p>Typical session: 2-5 rounds for complete consensus. Output format: Markdown</p> <p>Typical session: 2-5 rounds for complete consensus. Output format: Markdown</p>
</footer> </footer>
@ -617,6 +702,225 @@ const removeFile = () => {
font-size: 0.85rem; font-size: 0.85rem;
} }
/* Quick Access Section */
.quick-access-section {
margin-bottom: 2rem;
}
.stats-bar {
display: flex;
gap: 1.5rem;
margin-bottom: 1.5rem;
padding: 1rem 1.5rem;
background: rgba(102, 126, 234, 0.08);
border: 1px solid rgba(102, 126, 234, 0.2);
border-radius: 12px;
backdrop-filter: blur(10px);
}
.stat {
display: flex;
flex-direction: column;
align-items: center;
}
.stat-label {
font-size: 0.8rem;
color: rgba(255, 255, 255, 0.5);
text-transform: uppercase;
letter-spacing: 0.5px;
font-weight: 600;
}
.stat-value {
font-size: 1.8rem;
color: rgba(102, 126, 234, 0.95);
font-weight: 700;
margin-top: 0.25rem;
}
.filter-tabs {
display: flex;
gap: 0.75rem;
margin-bottom: 1.5rem;
flex-wrap: wrap;
}
.filter-btn {
padding: 0.6rem 1.2rem;
background: rgba(255, 255, 255, 0.05);
border: 1px solid rgba(255, 255, 255, 0.1);
color: rgba(255, 255, 255, 0.6);
border-radius: 8px;
cursor: pointer;
font-size: 0.9rem;
font-weight: 500;
transition: all 0.3s ease;
backdrop-filter: blur(5px);
}
.filter-btn:hover {
background: rgba(102, 126, 234, 0.2);
border-color: rgba(102, 126, 234, 0.4);
color: rgba(255, 255, 255, 0.8);
}
.filter-btn.active {
background: rgba(102, 126, 234, 0.3);
border-color: rgba(102, 126, 234, 0.6);
color: rgba(255, 255, 255, 0.95);
box-shadow: 0 0 15px rgba(102, 126, 234, 0.2);
}
.recent-sessions-scroll {
overflow-x: auto;
margin-bottom: 1rem;
padding-bottom: 0.5rem;
}
.sessions-carousel {
display: flex;
gap: 1rem;
min-width: min-content;
}
.session-card-horizontal {
display: flex;
align-items: center;
gap: 1rem;
min-width: 350px;
padding: 1rem;
background: rgba(102, 126, 234, 0.1);
border: 1px solid rgba(102, 126, 234, 0.3);
border-radius: 12px;
cursor: pointer;
transition: all 0.3s ease;
backdrop-filter: blur(10px);
}
.session-card-horizontal:hover {
background: rgba(102, 126, 234, 0.15);
border-color: rgba(102, 126, 234, 0.5);
transform: translateY(-2px);
box-shadow: 0 8px 20px rgba(102, 126, 234, 0.2);
}
.session-card-horizontal.status-completed {
border-color: rgba(76, 175, 80, 0.3);
background: rgba(76, 175, 80, 0.05);
}
.session-card-horizontal.status-completed:hover {
background: rgba(76, 175, 80, 0.12);
border-color: rgba(76, 175, 80, 0.5);
box-shadow: 0 8px 20px rgba(76, 175, 80, 0.15);
}
.card-left {
flex-shrink: 0;
}
.card-main {
flex: 1;
min-width: 0;
}
.session-prompt-short {
margin: 0;
color: rgba(255, 255, 255, 0.8);
font-size: 0.95rem;
font-weight: 500;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.session-meta {
margin: 0.25rem 0 0 0;
color: rgba(255, 255, 255, 0.4);
font-size: 0.8rem;
}
.card-right {
flex-shrink: 0;
}
.status-badge {
display: inline-flex;
align-items: center;
padding: 0.4rem 0.8rem;
background: rgba(76, 175, 80, 0.2);
color: rgba(76, 175, 80, 0.9);
border-radius: 20px;
font-size: 0.75rem;
font-weight: 600;
text-transform: capitalize;
white-space: nowrap;
}
.session-card-horizontal.status-ongoing .status-badge {
background: rgba(102, 126, 234, 0.2);
color: rgba(102, 126, 234, 0.9);
}
.view-all-container {
text-align: center;
margin-top: 1rem;
}
.view-all-btn {
padding: 0.7rem 1.5rem;
background: rgba(102, 126, 234, 0.15);
border: 1px solid rgba(102, 126, 234, 0.3);
color: rgba(102, 126, 234, 0.9);
border-radius: 8px;
cursor: pointer;
font-size: 0.9rem;
font-weight: 600;
transition: all 0.3s ease;
backdrop-filter: blur(5px);
}
.view-all-btn:hover {
background: rgba(102, 126, 234, 0.25);
border-color: rgba(102, 126, 234, 0.5);
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.15);
}
.divider {
text-align: center;
margin: 2rem 0;
position: relative;
}
.divider span {
background: linear-gradient(
-45deg,
#0f0c29 0%,
#302b63 25%,
#24243e 50%,
#302b63 75%,
#0f0c29 100%
);
padding: 0 1rem;
color: rgba(255, 255, 255, 0.4);
font-size: 0.9rem;
font-weight: 500;
position: relative;
z-index: 1;
}
.divider::before {
content: '';
position: absolute;
top: 50%;
left: 0;
right: 0;
height: 1px;
background: linear-gradient(to right, transparent, rgba(102, 126, 234, 0.2), transparent);
z-index: 0;
}
@media (max-width: 768px) { @media (max-width: 768px) {
.collaborative-input { .collaborative-input {
padding: 1rem; padding: 1rem;

View File

@ -7,7 +7,6 @@ export const useCollaborationStore = defineStore('collaboration', () => {
const loading = ref(false) const loading = ref(false)
const error = ref(null) const error = ref(null)
const currentDocument = ref('') const currentDocument = ref('')
const documentVersions = ref([])
const currentRound = ref(0) const currentRound = ref(0)
const conversationHistory = ref([]) const conversationHistory = ref([])
@ -138,23 +137,6 @@ export const useCollaborationStore = defineStore('collaboration', () => {
} }
} }
/**
* Update document from WebSocket message
*/
function updateDocumentFromMessage(message) {
if (message.type === 'initial_document_created') {
currentDocument.value = message.content
currentRound.value = 1
} else if (message.type === 'document_modified') {
currentDocument.value = message.content
} else if (message.type === 'round_complete') {
conversationHistory.value.push({
roundNumber: message.roundNumber,
agentsMadeChanges: message.agentsMadeChanges
})
}
}
/** /**
* Complete a session * Complete a session
*/ */
@ -184,7 +166,6 @@ export const useCollaborationStore = defineStore('collaboration', () => {
function clearCurrentSession() { function clearCurrentSession() {
currentSession.value = null currentSession.value = null
currentDocument.value = '' currentDocument.value = ''
documentVersions.value = []
currentRound.value = 0 currentRound.value = 0
conversationHistory.value = [] conversationHistory.value = []
} }
@ -213,14 +194,12 @@ export const useCollaborationStore = defineStore('collaboration', () => {
loading, loading,
error, error,
currentDocument, currentDocument,
documentVersions,
currentRound, currentRound,
conversationHistory, conversationHistory,
createSession, createSession,
startSession, startSession,
runRound, runRound,
getSession, getSession,
updateDocumentFromMessage,
completeSession, completeSession,
clearCurrentSession, clearCurrentSession,
downloadDocument downloadDocument