Compare commits
2 Commits
b566671ea4
...
c5f7806718
| Author | SHA1 | Date | |
|---|---|---|---|
| c5f7806718 | |||
| 1a7e0d218e |
@ -98,7 +98,7 @@ async function* parseStreamResponse(reader) {
|
||||
/**
|
||||
* Generate agent response with streaming thoughts
|
||||
*/
|
||||
export async function generateAgentResponse(agentName, prompt, currentDocument = '', onThought = null) {
|
||||
export async function* generateAgentResponse(agentName, prompt, currentDocument = '', onThought = null) {
|
||||
const systemPrompt = getAgentPrompt(agentName)
|
||||
|
||||
const messages = [
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import { ref, computed } from 'vue'
|
||||
import { useCollaborationStore } from '../stores/collaboration'
|
||||
|
||||
const emit = defineEmits(['session-created'])
|
||||
@ -11,11 +11,12 @@ const contextFile = ref(null)
|
||||
const agentCount = ref(7)
|
||||
const isCreating = ref(false)
|
||||
|
||||
const agentOptions = [
|
||||
{ value: 3, label: '3 agents (Quick)' },
|
||||
{ value: 5, label: '5 agents (Balanced)' },
|
||||
{ value: 7, label: '7 agents (Comprehensive)' }
|
||||
]
|
||||
const agentOptions = computed(() => {
|
||||
return Array.from({ length: 48 }, (_, i) => ({
|
||||
value: i + 3,
|
||||
label: `${i + 3} agents`
|
||||
}))
|
||||
})
|
||||
|
||||
const handleFileSelect = (event) => {
|
||||
const file = event.target.files?.[0]
|
||||
|
||||
@ -21,43 +21,39 @@ const isRunningRound = ref(false)
|
||||
const sessionStarted = ref(false)
|
||||
const isAutoRunning = ref(false)
|
||||
const autoRunTimeout = ref(null)
|
||||
const currentWorkingAgent = ref(null)
|
||||
const currentAgentThinking = ref('')
|
||||
const isStopping = ref(false)
|
||||
|
||||
const currentSession = computed(() => collaborationStore.currentSession)
|
||||
const currentDocument = computed(() => collaborationStore.currentDocument)
|
||||
const agents = computed(() => currentSession.value?.agents || [])
|
||||
const conversationHistory = computed(() => collaborationStore.conversationHistory)
|
||||
const agentCount = computed(() => currentSession.value?.agentCount || 0)
|
||||
|
||||
// True convergence: ALL 7 agents must say no changes
|
||||
// Convergence logic: consecutive rounds with no changes >= agent count
|
||||
const hasConverged = computed(() => {
|
||||
if (conversationHistory.value.length === 0) return false
|
||||
const lastRound = conversationHistory.value[conversationHistory.value.length - 1]
|
||||
// Check if last round has no changes from ANY agent
|
||||
return !lastRound.agentsMadeChanges || (Array.isArray(lastRound.agentsMadeChanges) && lastRound.agentsMadeChanges.length === 0)
|
||||
})
|
||||
|
||||
// Count of agents in current session
|
||||
const agentCount = computed(() => agents.value?.length || 0)
|
||||
|
||||
// Check if all available agents have participated without changes
|
||||
const allAgentsConverged = computed(() => {
|
||||
if (conversationHistory.value.length === 0) return false
|
||||
const lastRound = conversationHistory.value[conversationHistory.value.length - 1]
|
||||
// All agents in this session must have zero changes
|
||||
return hasConverged.value && agentCount.value > 0
|
||||
if (conversationHistory.value.length < 1) return false
|
||||
let consecutiveNoChanges = 0
|
||||
for (let i = conversationHistory.value.length - 1; i >= 0; i--) {
|
||||
const round = conversationHistory.value[i]
|
||||
if (!round.agentsMadeChanges || round.agentsMadeChanges.length === 0) {
|
||||
consecutiveNoChanges++
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
return consecutiveNoChanges >= agentCount.value
|
||||
})
|
||||
|
||||
onMounted(async () => {
|
||||
try {
|
||||
// Get session details
|
||||
await collaborationStore.getSession(props.sessionId)
|
||||
|
||||
// Connect WebSocket
|
||||
ws.connect()
|
||||
|
||||
// Listen to WebSocket messages
|
||||
const messageInterval = setInterval(() => {
|
||||
if (ws.messages.value.length > 0) {
|
||||
const message = ws.messages.value.pop()
|
||||
const message = ws.messages.value.shift()
|
||||
handleWebSocketMessage(message)
|
||||
}
|
||||
}, 100)
|
||||
@ -67,7 +63,6 @@ onMounted(async () => {
|
||||
if (autoRunTimeout.value) clearTimeout(autoRunTimeout.value)
|
||||
})
|
||||
|
||||
// If session hasn't been started, start it
|
||||
if (currentSession.value?.status === 'created') {
|
||||
await startSession()
|
||||
}
|
||||
@ -79,35 +74,49 @@ onMounted(async () => {
|
||||
const handleWebSocketMessage = (message) => {
|
||||
if (message.type === 'initial_document_created') {
|
||||
sessionStarted.value = true
|
||||
collaborationStore.updateDocumentFromMessage(message)
|
||||
// Start first auto-round after initial document
|
||||
collaborationStore.currentDocument = message.content
|
||||
currentWorkingAgent.value = null
|
||||
currentAgentThinking.value = ''
|
||||
scheduleNextRound(2000)
|
||||
} else if (message.type === 'document_modified') {
|
||||
collaborationStore.updateDocumentFromMessage(message)
|
||||
collaborationStore.currentDocument = message.content
|
||||
} else if (message.type === 'agent_working') {
|
||||
currentWorkingAgent.value = message.agentName
|
||||
currentAgentThinking.value = ''
|
||||
} else if (message.type === 'agent_thinking') {
|
||||
currentAgentThinking.value = message.thinking || ''
|
||||
} else if (message.type === 'round_complete') {
|
||||
isRunningRound.value = false
|
||||
collaborationStore.updateDocumentFromMessage(message)
|
||||
collaborationStore.conversationHistory.push({
|
||||
roundNumber: message.roundNumber,
|
||||
agentsMadeChanges: message.agentsMadeChanges,
|
||||
timestamp: Date.now()
|
||||
})
|
||||
currentWorkingAgent.value = null
|
||||
currentAgentThinking.value = ''
|
||||
|
||||
// Check convergence
|
||||
if (hasConverged.value && allAgentsConverged.value) {
|
||||
// All agents converged - auto-complete after delay
|
||||
if (hasConverged.value) {
|
||||
isAutoRunning.value = false
|
||||
setTimeout(() => {
|
||||
completeSession()
|
||||
}, 2000)
|
||||
} else {
|
||||
// Schedule next round with delay
|
||||
scheduleNextRound(1500)
|
||||
}
|
||||
} else if (message.type === 'session_error') {
|
||||
console.error('Session error:', message.error)
|
||||
isRunningRound.value = false
|
||||
isAutoRunning.value = false
|
||||
currentWorkingAgent.value = null
|
||||
} else if (message.type === 'session_completed') {
|
||||
currentWorkingAgent.value = null
|
||||
isAutoRunning.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const scheduleNextRound = (delay) => {
|
||||
if (autoRunTimeout.value) clearTimeout(autoRunTimeout.value)
|
||||
if (isStopping.value) return
|
||||
isAutoRunning.value = true
|
||||
autoRunTimeout.value = setTimeout(() => {
|
||||
runNextRoundAuto()
|
||||
@ -115,13 +124,13 @@ const scheduleNextRound = (delay) => {
|
||||
}
|
||||
|
||||
const runNextRoundAuto = async () => {
|
||||
if (isRunningRound.value || hasConverged.value) return
|
||||
if (isRunningRound.value || hasConverged.value || isStopping.value) return
|
||||
|
||||
isRunningRound.value = true
|
||||
try {
|
||||
await collaborationStore.runRound(props.sessionId)
|
||||
} catch (error) {
|
||||
console.error('Error running auto round:', error)
|
||||
console.error('Error running round:', error)
|
||||
isRunningRound.value = false
|
||||
isAutoRunning.value = false
|
||||
}
|
||||
@ -146,23 +155,35 @@ const completeSession = async () => {
|
||||
}
|
||||
}
|
||||
|
||||
const stopSession = async () => {
|
||||
isStopping.value = true
|
||||
isAutoRunning.value = false
|
||||
if (autoRunTimeout.value) clearTimeout(autoRunTimeout.value)
|
||||
await completeSession()
|
||||
}
|
||||
|
||||
const downloadDocument = () => {
|
||||
const format = currentSession.value?.documentFormat || 'md'
|
||||
const extension = format === 'md' ? 'md' : 'txt'
|
||||
collaborationStore.downloadDocument(`collaborative-document.${extension}`)
|
||||
}
|
||||
|
||||
function formatAgentName(agent) {
|
||||
if (!agent) return 'Unknown'
|
||||
return agent.charAt(0).toUpperCase() + agent.slice(1)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="collaborative-session">
|
||||
<!-- Header with minimal controls -->
|
||||
<!-- Header -->
|
||||
<div class="session-header">
|
||||
<div class="header-content">
|
||||
<h1>Collaborative Design</h1>
|
||||
<p class="session-meta">
|
||||
<span>Session #{{ sessionId }}</span>
|
||||
<span class="badge" :class="{ active: sessionStarted, converged: allAgentsConverged }">
|
||||
{{ allAgentsConverged ? 'Converged' : sessionStarted ? 'Active' : 'Waiting' }}
|
||||
<span class="badge" :class="{ active: sessionStarted, converged: hasConverged }">
|
||||
{{ hasConverged ? 'Converged' : sessionStarted ? 'Active' : 'Waiting' }}
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
@ -177,6 +198,15 @@ const downloadDocument = () => {
|
||||
Download
|
||||
</button>
|
||||
|
||||
<button
|
||||
v-if="isAutoRunning || isRunningRound"
|
||||
@click="stopSession"
|
||||
class="stop-btn"
|
||||
title="Stop the session"
|
||||
>
|
||||
Stop
|
||||
</button>
|
||||
|
||||
<div v-if="isAutoRunning || isRunningRound" class="auto-run-indicator">
|
||||
<span class="pulse"></span>
|
||||
Auto-running...
|
||||
@ -184,13 +214,31 @@ const downloadDocument = () => {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Status message -->
|
||||
<div v-if="allAgentsConverged" class="convergence-message">
|
||||
<span class="checkmark">[OK]</span>
|
||||
All {{ agentCount }} agents have reviewed and approved. Auto-completing...
|
||||
<!-- Working Agent Display -->
|
||||
<div v-if="currentWorkingAgent" class="working-agent-card">
|
||||
<div class="working-agent-header">
|
||||
<div class="raised-hand-animation">
|
||||
<span class="hand">✋</span>
|
||||
</div>
|
||||
<div class="agent-name-display">
|
||||
<h3>{{ formatAgentName(currentWorkingAgent) }}</h3>
|
||||
<p class="agent-label">is reviewing...</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="currentAgentThinking" class="agent-thinking">
|
||||
<p class="thinking-label">Thinking:</p>
|
||||
<p class="thinking-content">{{ currentAgentThinking }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Team agents display -->
|
||||
<!-- Convergence Message -->
|
||||
<div v-if="hasConverged" class="convergence-message">
|
||||
<span class="checkmark">[OK]</span>
|
||||
All {{ agentCount }} agents have reviewed and approved. Session complete!
|
||||
</div>
|
||||
|
||||
<!-- Team Display -->
|
||||
<div class="team-display">
|
||||
<h3>Team</h3>
|
||||
<div class="team-grid">
|
||||
@ -200,7 +248,7 @@ const downloadDocument = () => {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Main Document Viewer -->
|
||||
<!-- Main Document -->
|
||||
<div class="document-section">
|
||||
<div class="document-header">
|
||||
<h2>Architecture Document</h2>
|
||||
@ -220,6 +268,14 @@ const downloadDocument = () => {
|
||||
50% { opacity: 0.5; }
|
||||
}
|
||||
|
||||
@keyframes raise-hand {
|
||||
0% { transform: translateY(0) rotate(0deg); }
|
||||
25% { transform: translateY(-10px) rotate(-5deg); }
|
||||
50% { transform: translateY(-20px) rotate(5deg); }
|
||||
75% { transform: translateY(-10px) rotate(-5deg); }
|
||||
100% { transform: translateY(0) rotate(0deg); }
|
||||
}
|
||||
|
||||
.collaborative-session {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
@ -311,6 +367,24 @@ const downloadDocument = () => {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.stop-btn {
|
||||
padding: 0.75rem 1.5rem;
|
||||
background: rgba(244, 67, 54, 0.8);
|
||||
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||
color: white;
|
||||
border-radius: 10px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
backdrop-filter: blur(10px);
|
||||
}
|
||||
|
||||
.stop-btn:hover {
|
||||
background: rgba(244, 67, 54, 0.95);
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 8px 20px rgba(244, 67, 54, 0.3);
|
||||
}
|
||||
|
||||
.auto-run-indicator {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@ -333,6 +407,70 @@ const downloadDocument = () => {
|
||||
animation: pulse 1.5s infinite;
|
||||
}
|
||||
|
||||
.working-agent-card {
|
||||
padding: 1.5rem;
|
||||
background: rgba(102, 126, 234, 0.1);
|
||||
border: 2px solid rgba(102, 126, 234, 0.4);
|
||||
border-radius: 16px;
|
||||
backdrop-filter: blur(10px);
|
||||
}
|
||||
|
||||
.working-agent-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.raised-hand-animation {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
background: rgba(102, 126, 234, 0.2);
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.hand {
|
||||
font-size: 2rem;
|
||||
animation: raise-hand 1s infinite;
|
||||
}
|
||||
|
||||
.agent-name-display h3 {
|
||||
margin: 0;
|
||||
font-size: 1.3rem;
|
||||
color: rgba(102, 126, 234, 0.95);
|
||||
}
|
||||
|
||||
.agent-label {
|
||||
margin: 0.25rem 0 0 0;
|
||||
font-size: 0.85rem;
|
||||
color: rgba(255, 255, 255, 0.6);
|
||||
}
|
||||
|
||||
.agent-thinking {
|
||||
padding: 1rem;
|
||||
background: rgba(0, 0, 0, 0.2);
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.thinking-label {
|
||||
margin: 0 0 0.5rem 0;
|
||||
font-size: 0.85rem;
|
||||
font-weight: 600;
|
||||
color: rgba(102, 126, 234, 0.9);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
.thinking-content {
|
||||
margin: 0;
|
||||
font-size: 0.95rem;
|
||||
color: rgba(255, 255, 255, 0.8);
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.convergence-message {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@ -431,12 +569,3 @@ const downloadDocument = () => {
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
function formatAgentName(agent) {
|
||||
return agent
|
||||
.split('_')
|
||||
.map(word => word.charAt(0).toUpperCase() + word.slice(1))
|
||||
.join(' ')
|
||||
}
|
||||
</script>
|
||||
|
||||
@ -6,28 +6,23 @@ const collaborationStore = useCollaborationStore()
|
||||
|
||||
const conversationHistory = computed(() => collaborationStore.conversationHistory)
|
||||
const currentRound = computed(() => collaborationStore.currentRound)
|
||||
|
||||
const allAgents = ['lead_architect', 'backend_engineer', 'frontend_engineer', 'ui_designer', 'devops_engineer', 'product_manager', 'security_specialist']
|
||||
const agents = computed(() => collaborationStore.currentSession?.agents || [])
|
||||
const agentCount = computed(() => collaborationStore.currentSession?.agentCount || 0)
|
||||
|
||||
const getAgentStatus = (round) => {
|
||||
if (!round.agentsMadeChanges) return []
|
||||
return allAgents.map(agent => ({
|
||||
name: formatAgentName(agent),
|
||||
made_changes: round.agentsMadeChanges.includes(agent) || round.agentsMadeChanges.some(a => a.includes(agent))
|
||||
if (!round.agentsMadeChanges) return agents.value.map(agent => ({ name: agent, made_changes: false }))
|
||||
return agents.value.map(agent => ({
|
||||
name: agent,
|
||||
made_changes: round.agentsMadeChanges.includes(agent)
|
||||
}))
|
||||
}
|
||||
|
||||
const roundProgress = computed(() => {
|
||||
if (conversationHistory.value.length === 0) return 0
|
||||
return Math.round((conversationHistory.value.length / 10) * 100)
|
||||
if (agentCount.value === 0 || conversationHistory.value.length === 0) return 0
|
||||
// Estimate progress: assume ~10 rounds max before convergence
|
||||
const maxRounds = Math.ceil(agentCount.value * 1.5)
|
||||
return Math.min(Math.round((conversationHistory.value.length / maxRounds) * 100), 100)
|
||||
})
|
||||
|
||||
function formatAgentName(agent) {
|
||||
return agent
|
||||
.split('_')
|
||||
.map(word => word.charAt(0).toUpperCase() + word.slice(1))
|
||||
.join(' ')
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@ -66,7 +61,7 @@ function formatAgentName(agent) {
|
||||
:title="agent.made_changes ? 'Made changes' : 'No changes'"
|
||||
>
|
||||
<span class="agent-dot" :class="{ changed: agent.made_changes }"></span>
|
||||
<span class="agent-short">{{ agent.name.split(' ')[0].slice(0, 1) }}</span>
|
||||
<span class="agent-name">{{ agent.name }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -228,9 +223,13 @@ function formatAgentName(agent) {
|
||||
height: 5px;
|
||||
}
|
||||
|
||||
.agent-short {
|
||||
.agent-name {
|
||||
font-weight: 600;
|
||||
font-size: 0.65rem;
|
||||
font-size: 0.7rem;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
max-width: 60px;
|
||||
}
|
||||
|
||||
.changes-count {
|
||||
|
||||
@ -143,14 +143,14 @@ export const useCollaborationStore = defineStore('collaboration', () => {
|
||||
*/
|
||||
function updateDocumentFromMessage(message) {
|
||||
if (message.type === 'initial_document_created') {
|
||||
currentDocument.value = message.document
|
||||
currentDocument.value = message.content
|
||||
currentRound.value = 1
|
||||
} else if (message.type === 'document_modified') {
|
||||
currentDocument.value = message.document
|
||||
currentDocument.value = message.content
|
||||
} else if (message.type === 'round_complete') {
|
||||
conversationHistory.value.push({
|
||||
roundNumber: message.roundNumber,
|
||||
agentsMadeChanges: message.agentsWhoModified
|
||||
agentsMadeChanges: message.agentsMadeChanges
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user