| <template> |
| <div v-if="isVisible" class="modal-overlay" @click="closeModal"> |
| <div class="modal-container" @click.stop> |
| |
| <div class="modal-header"> |
| <h2 class="modal-title"> |
| 🤖 Multi-Agent Communication |
| </h2> |
| <p class="modal-subtitle"> |
| Jane Alesi Master Coordinator mit automatischer Spezialist-Delegation |
| </p> |
| <button @click="closeModal" class="close-button"> |
| <svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24"> |
| <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path> |
| </svg> |
| </button> |
| </div> |
| |
| <div class="modal-body"> |
| |
| <div class="capabilities-section"> |
| <h3 class="section-title">Verfügbare Spezialisten</h3> |
| <div class="specialists-grid"> |
| <div |
| v-for="specialist in availableSpecialists" |
| :key="specialist.id" |
| :class="['specialist-card', { 'active': specialist.id === selectedSpecialist }]" |
| > |
| <div class="specialist-icon">{{ getSpecialistIcon(specialist.specialization) }}</div> |
| <div class="specialist-info"> |
| <div class="specialist-name">{{ specialist.name }}</div> |
| <div class="specialist-role">{{ specialist.specialization }}</div> |
| </div> |
| </div> |
| </div> |
| </div> |
| |
| |
| <div class="chat-section"> |
| <div class="chat-messages" ref="messagesContainer"> |
| <div |
| v-for="message in chatMessages" |
| :key="message.id" |
| :class="['message', message.type]" |
| > |
| <div class="message-header"> |
| <div class="message-agent"> |
| <span class="agent-icon">{{ getAgentIcon(message.agent) }}</span> |
| <span class="agent-name">{{ message.agentName }}</span> |
| <span v-if="message.role" class="agent-role">({{ message.role }})</span> |
| </div> |
| <div class="message-time">{{ formatTime(message.timestamp) }}</div> |
| </div> |
| <div class="message-content"> |
| {{ message.content }} |
| </div> |
| <div v-if="message.processingTime || message.provider" class="message-meta"> |
| <span v-if="message.provider" class="provider-badge" :class="message.provider"> |
| <span v-if="message.provider === 'colossus'">🔒 Colossus (Intern)</span> |
| <span v-else-if="message.provider === 'openrouter'">🌐 OpenRouter (Extern)</span> |
| <span v-else>{{ message.provider }}</span> |
| </span> |
| <span v-if="message.processingTime" class="processing-time">⏱️ {{ message.processingTime }}s</span> |
| <span v-if="message.cost" class="cost-info">💰 {{ message.cost }}</span> |
| </div> |
| </div> |
| |
| |
| <div v-if="currentCoordinationChain.length > 0" class="coordination-chain"> |
| <h4 class="chain-title">🔄 Agent Delegation Chain:</h4> |
| <div class="chain-flow"> |
| <div |
| v-for="(agent, index) in currentCoordinationChain" |
| :key="index" |
| class="chain-agent" |
| > |
| <span class="chain-icon">{{ getAgentIcon(agent) }}</span> |
| <span class="chain-name">{{ getAgentName(agent) }}</span> |
| <svg v-if="index < currentCoordinationChain.length - 1" class="chain-arrow" fill="currentColor" viewBox="0 0 20 20"> |
| <path fill-rule="evenodd" d="M10.293 15.707a1 1 0 010-1.414L14.586 10l-4.293-4.293a1 1 0 111.414-1.414l5 5a1 1 0 010 1.414l-5 5a1 1 0 01-1.414 0z" clip-rule="evenodd"></path> |
| </svg> |
| </div> |
| </div> |
| </div> |
| |
| |
| <div v-if="isProcessing" class="message processing"> |
| <div class="message-header"> |
| <div class="message-agent"> |
| <span class="agent-icon loading">🤖</span> |
| <span class="agent-name">{{ currentProcessor }}</span> |
| <span class="agent-role">({{ processingStatus }})</span> |
| </div> |
| </div> |
| <div class="message-content"> |
| <div class="typing-indicator"> |
| <span></span> |
| <span></span> |
| <span></span> |
| </div> |
| <div v-if="processingStatus.includes('delegiert')" class="delegation-status"> |
| 🔄 Agent-zu-Agent Kommunikation läuft... |
| </div> |
| </div> |
| </div> |
| </div> |
| </div> |
| </div> |
| |
| |
| <transition name="toast-fade"> |
| <div v-if="showPrivacyInfo" class="privacy-toast"> |
| <div class="toast-icon">🔒</div> |
| <div class="toast-content"> |
| <div class="toast-title">Sensible Daten erkannt</div> |
| <div class="toast-message">Ihre Anfrage wird automatisch über den sicheren Colossus-Server (intern) verarbeitet</div> |
| </div> |
| <div class="toast-indicator"> |
| <div class="progress-bar"></div> |
| </div> |
| </div> |
| </transition> |
| |
| |
| <div class="chat-input-section"> |
| <div class="input-group"> |
| <textarea |
| v-model="currentMessage" |
| @keydown.enter.exact.prevent="sendMessage" |
| @keydown.enter.shift.exact="() => {}" |
| placeholder="Stelle eine Frage oder gib eine Aufgabe ein..." |
| class="message-input" |
| rows="3" |
| :disabled="isProcessing" |
| ></textarea> |
| <button |
| @click="sendMessage" |
| :disabled="!canSendMessage" |
| class="send-button" |
| > |
| <svg v-if="!isProcessing" class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"> |
| <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 19l9 2-9-18-9 18 9-2zm0 0v-8"></path> |
| </svg> |
| <div v-else class="spinner"></div> |
| </button> |
| </div> |
| |
| <div class="input-options"> |
| <div class="options-row"> |
| <div class="task-priority"> |
| <label for="priority">Priorität:</label> |
| <select v-model="taskPriority" id="priority" class="priority-select"> |
| <option value="normal">Normal</option> |
| <option value="high">Hoch</option> |
| <option value="urgent">Dringend</option> |
| <option value="low">Niedrig</option> |
| </select> |
| </div> |
| |
| <div class="preferred-agent"> |
| <label for="preferredAgent">Bevorzugter Agent:</label> |
| <select v-model="preferredAgent" id="preferredAgent" class="agent-select"> |
| <option value="">Automatische Auswahl</option> |
| <option value="john_alesi">John Alesi (Development)</option> |
| <option value="lara_alesi">Lara Alesi (Medical)</option> |
| <option value="justus_alesi">Justus Alesi (Legal)</option> |
| <option value="theo_alesi">Theo Alesi (Finance)</option> |
| <option value="leon_alesi">Leon Alesi (System)</option> |
| <option value="luna_alesi">Luna Alesi (Coaching)</option> |
| </select> |
| </div> |
| </div> |
| |
| <div class="provider-row"> |
| <div class="provider-selection"> |
| <label for="provider">🔒 Datenschutz-Modus:</label> |
| <select |
| v-model="selectedProvider" |
| id="provider" |
| class="provider-select" |
| :class="{'privacy-mode': selectedProvider === 'colossus'}" |
| > |
| <option value="auto">🔄 Automatisch</option> |
| <option value="colossus">🔒 Colossus (Intern)</option> |
| <option value="openrouter">🌐 OpenRouter (Extern)</option> |
| </select> |
| <div v-if="selectedProvider === 'colossus'" class="privacy-badge"> |
| ✅ Daten geschützt |
| </div> |
| <div v-else-if="selectedProvider === 'auto'" class="privacy-badge auto"> |
| 🔄 Auto-Erkennung |
| </div> |
| </div> |
| </div> |
| </div> |
| </div> |
| |
| |
| <div class="modal-footer"> |
| <div class="footer-stats"> |
| <span class="stat-item"> |
| <span class="stat-icon">💬</span> |
| <span class="stat-value">{{ chatMessages.length }}</span> |
| <span class="stat-label">Messages</span> |
| </span> |
| <span class="stat-item"> |
| <span class="stat-icon">🤖</span> |
| <span class="stat-value">{{ uniqueAgentsCount }}</span> |
| <span class="stat-label">Agents</span> |
| </span> |
| <span class="stat-item"> |
| <span class="stat-icon">⏱️</span> |
| <span class="stat-value">{{ averageResponseTime }}s</span> |
| <span class="stat-label">Avg Response</span> |
| </span> |
| </div> |
| <div class="footer-actions"> |
| <button @click="showSystemStatus" class="action-button system-status"> |
| 📊 System Status |
| </button> |
| <button @click="showPerformanceReport" class="action-button performance"> |
| ⚡ Performance Report |
| </button> |
| <button @click="showListAgents" class="action-button list-agents"> |
| 👥 List Agents |
| </button> |
| <button @click="showHelp" class="action-button help"> |
| ❓ Help |
| </button> |
| <button @click="clearChat" class="clear-button"> |
| 🗑️ Clear |
| </button> |
| <button @click="closeModal" class="close-modal-button"> |
| Schließen |
| </button> |
| </div> |
| </div> |
| </div> |
| </div> |
| </template> |
| |
| <script> |
| import { ref, computed, nextTick, onMounted } from 'vue' |
| import { saapApi } from '../../services/saapApi' |
| |
| export default { |
| name: 'MultiAgentChatModal', |
| props: { |
| isVisible: { |
| type: Boolean, |
| default: false |
| } |
| }, |
| emits: ['close'], |
| setup(props, { emit }) { |
| |
| const chatMessages = ref([]) |
| const currentMessage = ref('') |
| const isProcessing = ref(false) |
| const currentProcessor = ref('Jane Alesi') |
| const processingStatus = ref('Ready') |
| const selectedSpecialist = ref(null) |
| const currentCoordinationChain = ref([]) |
| const taskPriority = ref('normal') |
| const preferredAgent = ref('') |
| const messagesContainer = ref(null) |
| const selectedProvider = ref('auto') |
| const showPrivacyInfo = ref(false) |
| |
| |
| const availableSpecialists = ref([ |
| { |
| id: 'john_alesi', |
| name: 'John Alesi', |
| specialization: 'Development', |
| description: 'Software-Entwicklung und AGI-Architekturen' |
| }, |
| { |
| id: 'lara_alesi', |
| name: 'Lara Alesi', |
| specialization: 'Medical', |
| description: 'Medizinische Expertise und Gesundheitswesen' |
| }, |
| { |
| id: 'justus_alesi', |
| name: 'Justus Alesi', |
| specialization: 'Legal', |
| description: 'Rechtsberatung für DE/CH/EU' |
| }, |
| { |
| id: 'theo_alesi', |
| name: 'Theo Alesi', |
| specialization: 'Finance', |
| description: 'Finanz- und Investitionsanalyse' |
| }, |
| { |
| id: 'leon_alesi', |
| name: 'Leon Alesi', |
| specialization: 'System', |
| description: 'IT-Systemintegration und Infrastructure' |
| }, |
| { |
| id: 'luna_alesi', |
| name: 'Luna Alesi', |
| specialization: 'Coaching', |
| description: 'Coaching und Organisationsentwicklung' |
| } |
| ]) |
| |
| |
| const canSendMessage = computed(() => { |
| return currentMessage.value.trim().length > 0 && !isProcessing.value |
| }) |
| |
| const uniqueAgentsCount = computed(() => { |
| const agents = new Set(chatMessages.value.map(msg => msg.agent)) |
| return agents.size |
| }) |
| |
| const averageResponseTime = computed(() => { |
| const responseTimes = chatMessages.value |
| .filter(msg => msg.processingTime) |
| .map(msg => msg.processingTime) |
| |
| if (responseTimes.length === 0) return 0 |
| return (responseTimes.reduce((sum, time) => sum + time, 0) / responseTimes.length).toFixed(1) |
| }) |
| |
| |
| const getSpecialistIcon = (specialization) => { |
| const icons = { |
| 'Development': '💻', |
| 'Medical': '⚕️', |
| 'Legal': '⚖️', |
| 'Finance': '💰', |
| 'System': '🔧', |
| 'Coaching': '🎯' |
| } |
| return icons[specialization] || '🤖' |
| } |
| |
| const getAgentIcon = (agentId) => { |
| const icons = { |
| 'jane_alesi': '👩💼', |
| 'john_alesi': '💻', |
| 'lara_alesi': '⚕️', |
| 'justus_alesi': '⚖️', |
| 'theo_alesi': '💰', |
| 'leon_alesi': '🔧', |
| 'luna_alesi': '🎯', |
| 'user': '👤' |
| } |
| return icons[agentId] || '🤖' |
| } |
| |
| const getAgentName = (agentId) => { |
| const names = { |
| 'jane_alesi': 'Jane Alesi', |
| 'john_alesi': 'John Alesi', |
| 'lara_alesi': 'Lara Alesi', |
| 'justus_alesi': 'Justus Alesi', |
| 'theo_alesi': 'Theo Alesi', |
| 'leon_alesi': 'Leon Alesi', |
| 'luna_alesi': 'Luna Alesi' |
| } |
| return names[agentId] || agentId |
| } |
| |
| const formatTime = (timestamp) => { |
| return new Date(timestamp).toLocaleTimeString('de-DE', { |
| hour: '2-digit', |
| minute: '2-digit', |
| second: '2-digit' |
| }) |
| } |
| |
| const scrollToBottom = () => { |
| nextTick(() => { |
| if (messagesContainer.value) { |
| messagesContainer.value.scrollTop = messagesContainer.value.scrollHeight |
| } |
| }) |
| } |
| |
| |
| const addMessage = (content, agent, agentName, type = 'agent', meta = {}) => { |
| const message = { |
| id: Date.now() + Math.random(), |
| content, |
| agent, |
| agentName, |
| type, |
| timestamp: new Date(), |
| role: meta.role, |
| processingTime: meta.processingTime, |
| cost: meta.cost, |
| provider: meta.provider |
| } |
| |
| chatMessages.value.push(message) |
| scrollToBottom() |
| return message |
| } |
| |
| const sendMessage = async () => { |
| if (!canSendMessage.value) return |
| |
| const message = currentMessage.value.trim() |
| currentMessage.value = '' |
| |
| |
| if (selectedProvider.value !== 'colossus') { |
| const privacyCheck = await checkPrivacy(message) |
| if (privacyCheck.requiresLocal) { |
| |
| showPrivacyInfo.value = true |
| |
| |
| setTimeout(() => { |
| showPrivacyInfo.value = false |
| }, 3000) |
| |
| |
| await sendMessageToBackend(message, 'colossus') |
| return |
| } |
| } |
| |
| await sendMessageToBackend(message) |
| } |
| |
| const sendMessageToBackend = async (message, forceProvider = null) => { |
| |
| addMessage(message, 'user', 'Du', 'user') |
| |
| |
| isProcessing.value = true |
| currentProcessor.value = 'Jane Alesi' |
| processingStatus.value = 'Analysiert Intent...' |
| |
| try { |
| |
| let provider = forceProvider || selectedProvider.value |
| if (provider === 'auto') { |
| provider = null |
| } |
| |
| |
| const response = await saapApi.multiAgentChat(message, { |
| user_context: { |
| timestamp: new Date().toISOString().split('.')[0] + 'Z', |
| priority: taskPriority.value |
| }, |
| preferred_agent: preferredAgent.value || null, |
| task_priority: taskPriority.value, |
| provider: provider, |
| privacy_mode: forceProvider || selectedProvider.value |
| }) |
| |
| |
| const coordinationChain = [] |
| coordinationChain.push('jane_alesi') |
| if (response.specialist_agent && response.specialist_agent !== 'jane_alesi') { |
| coordinationChain.push(response.specialist_agent) |
| } |
| currentCoordinationChain.value = coordinationChain |
| |
| |
| if (response.delegation_used && response.specialist_agent && response.specialist_agent !== 'jane_alesi') { |
| processingStatus.value = `Delegiert an ${getAgentName(response.specialist_agent)}...` |
| } |
| |
| |
| const responseContent = response.response?.content || response.content || 'Keine Antwort erhalten' |
| const responseTime = response.response?.response_time || response.response_time || 0 |
| const costUsd = response.response?.cost_usd || 0 |
| const usedProvider = response.response?.provider || response.privacy_protection?.selected_provider || forceProvider || selectedProvider.value || 'unknown' |
| |
| |
| if (response.delegation_used && response.specialist_agent) { |
| const specialistName = getAgentName(response.specialist_agent) |
| const specialist = availableSpecialists.value.find(s => s.id === response.specialist_agent) |
| |
| selectedSpecialist.value = response.specialist_agent |
| |
| |
| addMessage( |
| `🔄 Jane Alesi delegiert an ${specialistName} für spezialisierte Bearbeitung...`, |
| 'system', |
| 'System', |
| 'delegation', |
| { role: 'Delegation Info' } |
| ) |
| |
| |
| setTimeout(() => { |
| addMessage( |
| responseContent, |
| response.specialist_agent, |
| specialistName, |
| 'specialist', |
| { |
| role: specialist?.specialization || 'Specialist', |
| processingTime: responseTime, |
| provider: usedProvider |
| } |
| ) |
| selectedSpecialist.value = null |
| }, 500) |
| } else { |
| |
| addMessage( |
| responseContent, |
| 'jane_alesi', |
| 'Jane Alesi', |
| 'coordinator', |
| { |
| role: 'Master Coordinator', |
| processingTime: responseTime, |
| cost: costUsd > 0 ? `$${costUsd.toFixed(6)}` : null, |
| provider: usedProvider |
| } |
| ) |
| } |
| |
| } catch (error) { |
| console.error('Multi-Agent Chat Error:', error) |
| addMessage( |
| 'Entschuldigung, es ist ein Fehler bei der Multi-Agent-Kommunikation aufgetreten. Bitte versuche es erneut.', |
| 'jane_alesi', |
| 'Jane Alesi', |
| 'error', |
| { role: 'Error Handler' } |
| ) |
| } finally { |
| isProcessing.value = false |
| currentProcessor.value = 'Jane Alesi' |
| processingStatus.value = 'Ready' |
| } |
| } |
| |
| const checkPrivacy = async (message) => { |
| |
| const sensitiveKeywords = { |
| medical: ['patient', 'diagnose', 'krankheit', 'symptom', 'arzt', 'medikament'], |
| financial: ['konto', 'iban', 'kreditkarte', 'gehalt', 'steuer'], |
| personal: ['geburtsdatum', 'adresse', 'telefon', 'ausweis', 'pass'] |
| } |
| |
| const messageLower = message.toLowerCase() |
| const detectedCategories = [] |
| |
| for (const [category, keywords] of Object.entries(sensitiveKeywords)) { |
| if (keywords.some(keyword => messageLower.includes(keyword))) { |
| detectedCategories.push(category) |
| } |
| } |
| |
| |
| const patterns = { |
| email: /\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b/, |
| phone: /\b(?:\+49|0)\s?\d{3,4}\s?\d{6,8}\b/, |
| iban: /\b[A-Z]{2}\d{2}[A-Z0-9]{13,29}\b/, |
| creditCard: /\b\d{4}[-\s]?\d{4}[-\s]?\d{4}[-\s]?\d{4}\b/ |
| } |
| |
| for (const [type, pattern] of Object.entries(patterns)) { |
| if (pattern.test(message)) { |
| detectedCategories.push(type) |
| } |
| } |
| |
| return { |
| requiresLocal: detectedCategories.length > 0, |
| categories: detectedCategories, |
| level: detectedCategories.length > 0 ? 'private' : 'public' |
| } |
| } |
| |
| const clearChat = () => { |
| chatMessages.value = [] |
| currentCoordinationChain.value = [] |
| selectedSpecialist.value = null |
| |
| |
| addMessage( |
| 'Hallo! Ich bin Jane Alesi, deine Master Coordinatorin. Ich kann Aufgaben an spezialisierte Agenten delegieren oder sie selbst bearbeiten. Wie kann ich dir helfen?', |
| 'jane_alesi', |
| 'Jane Alesi', |
| 'coordinator', |
| { role: 'Master Coordinator' } |
| ) |
| } |
| |
| const closeModal = () => { |
| emit('close') |
| } |
| |
| |
| const showSystemStatus = async () => { |
| try { |
| const response = await saapApi.getHealth() |
| const status = response.status === 'healthy' ? '✅ Online' : '⚠️ Degraded' |
| const agentsActive = availableSpecialists.value.length |
| |
| const statusMessage = `📊 SYSTEM STATUS REPORT |
| |
| 🟢 Backend: ${status} |
| 🤖 Agents verfügbar: ${agentsActive}/6 |
| 🔒 Colossus: ${response.colossus_available ? '✅ Verfügbar' : '❌ Nicht verfügbar'} |
| 🌐 OpenRouter: ${response.openrouter_available ? '✅ Verfügbar' : '❌ Nicht verfügbar'} |
| 💾 Datenbank: ${response.database ? '✅ Verbunden' : '❌ Getrennt'} |
| ⏱️ Uptime: ${response.uptime || 'N/A'} |
| 📅 Zeitstempel: ${new Date().toLocaleString('de-DE')}` |
| |
| addMessage(statusMessage, 'system', 'System', 'coordinator', { role: 'System Status' }) |
| } catch (error) { |
| addMessage( |
| '❌ Fehler beim Abrufen des System-Status. Backend möglicherweise offline.', |
| 'system', |
| 'System', |
| 'error', |
| { role: 'Error' } |
| ) |
| } |
| } |
| |
| |
| const showPerformanceReport = () => { |
| const totalMessages = chatMessages.value.length |
| const userMessages = chatMessages.value.filter(m => m.type === 'user').length |
| const agentMessages = chatMessages.value.filter(m => m.type !== 'user').length |
| const delegations = chatMessages.value.filter(m => m.type === 'delegation').length |
| |
| const responseTimes = chatMessages.value |
| .filter(m => m.processingTime) |
| .map(m => m.processingTime) |
| |
| const avgResponseTime = responseTimes.length > 0 |
| ? (responseTimes.reduce((sum, t) => sum + t, 0) / responseTimes.length).toFixed(2) |
| : '0' |
| |
| const minResponseTime = responseTimes.length > 0 ? Math.min(...responseTimes).toFixed(2) : '0' |
| const maxResponseTime = responseTimes.length > 0 ? Math.max(...responseTimes).toFixed(2) : '0' |
| |
| const colossusMessages = chatMessages.value.filter(m => m.provider === 'colossus').length |
| const openrouterMessages = chatMessages.value.filter(m => m.provider === 'openrouter').length |
| |
| const performanceMessage = `⚡ PERFORMANCE REPORT |
| |
| 📊 Nachrichten-Statistik: |
| Gesamt: ${totalMessages} |
| Nutzer: ${userMessages} |
| Agents: ${agentMessages} |
| Delegationen: ${delegations} |
| |
| ⏱️ Response Times: |
| Durchschnitt: ${avgResponseTime}s |
| Minimum: ${minResponseTime}s |
| Maximum: ${maxResponseTime}s |
| |
| 🔒 Provider-Verteilung: |
| Colossus (Intern): ${colossusMessages} |
| OpenRouter (Extern): ${openrouterMessages} |
| |
| 🤖 Aktive Agents: ${uniqueAgentsCount.value} |
| 📅 Session-Start: ${chatMessages.value[0]?.timestamp ? new Date(chatMessages.value[0].timestamp).toLocaleString('de-DE') : 'N/A'}` |
| |
| addMessage(performanceMessage, 'system', 'System', 'coordinator', { role: 'Performance Report' }) |
| } |
| |
| |
| const showListAgents = () => { |
| const agentsList = availableSpecialists.value |
| .map(agent => { |
| const messageCount = chatMessages.value.filter(m => m.agent === agent.id).length |
| const lastUsed = chatMessages.value |
| .filter(m => m.agent === agent.id) |
| .sort((a, b) => new Date(b.timestamp) - new Date(a.timestamp))[0] |
| |
| return `${getSpecialistIcon(agent.specialization)} ${agent.name} |
| Expertise: ${agent.specialization} |
| Beschreibung: ${agent.description} |
| Nachrichten: ${messageCount} |
| Zuletzt aktiv: ${lastUsed ? formatTime(lastUsed.timestamp) : 'Noch nicht verwendet'}` |
| }) |
| .join('\n\n') |
| |
| const listMessage = `👥 VERFÜGBARE SPEZIALISTEN (${availableSpecialists.value.length}) |
| |
| ${agentsList} |
| |
| 💡 Hinweis: Jane Alesi delegiert automatisch an den passenden Spezialisten basierend auf deiner Anfrage.` |
| |
| addMessage(listMessage, 'system', 'System', 'coordinator', { role: 'Agent List' }) |
| } |
| |
| |
| const showHelp = () => { |
| const helpMessage = `❓ MULTI-AGENT CHAT - HILFE |
| |
| 🤖 Was ist Multi-Agent Communication? |
| Jane Alesi ist deine Master Coordinatorin. Sie analysiert deine Anfragen und delegiert automatisch an spezialisierte Agenten. |
| |
| 👥 Verfügbare Spezialisten: |
| 💻 John Alesi - Software-Entwicklung & AGI |
| ⚕️ Lara Alesi - Medizinische Expertise |
| ⚖️ Justus Alesi - Rechtsberatung (DE/CH/EU) |
| 💰 Theo Alesi - Finanz- & Investitionsanalyse |
| 🔧 Leon Alesi - IT-Systemintegration |
| 🎯 Luna Alesi - Coaching & Organisation |
| |
| 🔒 Datenschutz: |
| 🔄 Automatisch: Erkennt sensible Daten automatisch |
| 🔒 Colossus: Interner Server für sensible Daten |
| 🌐 OpenRouter: Externer Provider für normale Anfragen |
| |
| ⚙️ Optionen: |
| Priorität: Normal, Hoch, Dringend, Niedrig |
| Agent-Auswahl: Bevorzugter Agent (optional) |
| Datenschutz-Modus: Auto, Colossus, OpenRouter |
| |
| 💡 Tipps: |
| ✓ Stelle klare, spezifische Fragen |
| ✓ Jane delegiert automatisch zum besten Spezialisten |
| ✓ Sensible Daten werden automatisch geschützt |
| ✓ Nutze die Quick-Actions für System-Infos |
| |
| 📊 Quick Actions: |
| 📊 System Status - Zeigt Backend-Status |
| ⚡ Performance Report - Zeigt Session-Statistiken |
| 👥 List Agents - Zeigt alle verfügbaren Spezialisten |
| ❓ Help - Zeigt diese Hilfe |
| |
| 🚀 Beispiele: |
| "Hilf mir beim Debuggen meines React-Codes" → John |
| "Erkläre mir diesen medizinischen Befund" → Lara |
| "Ist dieser Vertrag rechtlich korrekt?" → Justus |
| "Analysiere diese Investitionsmöglichkeit" → Theo` |
| |
| addMessage(helpMessage, 'system', 'System', 'coordinator', { role: 'Help & Documentation' }) |
| } |
| |
| |
| onMounted(() => { |
| if (chatMessages.value.length === 0) { |
| clearChat() |
| } |
| }) |
| |
| return { |
| |
| chatMessages, |
| currentMessage, |
| isProcessing, |
| currentProcessor, |
| processingStatus, |
| selectedSpecialist, |
| currentCoordinationChain, |
| taskPriority, |
| preferredAgent, |
| messagesContainer, |
| availableSpecialists, |
| selectedProvider, |
| showPrivacyInfo, |
| |
| |
| canSendMessage, |
| uniqueAgentsCount, |
| averageResponseTime, |
| |
| |
| getSpecialistIcon, |
| getAgentIcon, |
| getAgentName, |
| formatTime, |
| sendMessage, |
| clearChat, |
| closeModal, |
| showSystemStatus, |
| showPerformanceReport, |
| showListAgents, |
| showHelp |
| } |
| } |
| } |
| </script> |
| |
| <style scoped> |
| |
| .modal-overlay { |
| position: fixed; |
| top: 0; |
| left: 0; |
| right: 0; |
| bottom: 0; |
| background-color: rgba(0, 0, 0, 0.75); |
| display: flex; |
| align-items: center; |
| justify-content: center; |
| z-index: 1000; |
| backdrop-filter: blur(4px); |
| } |
| |
| |
| .modal-container { |
| background: white; |
| border-radius: 16px; |
| width: 95vw; |
| max-width: 1200px; |
| height: 95vh; |
| max-height: 98vh; |
| display: flex; |
| flex-direction: column; |
| box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25); |
| overflow: hidden; |
| } |
| |
| |
| .modal-header { |
| background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); |
| color: white; |
| padding: 20px; |
| position: relative; |
| flex-shrink: 0; |
| } |
| |
| .modal-title { |
| margin: 0; |
| font-size: 24px; |
| font-weight: 700; |
| } |
| |
| .modal-subtitle { |
| margin: 8px 0 0 0; |
| font-size: 14px; |
| opacity: 0.9; |
| } |
| |
| .close-button { |
| position: absolute; |
| top: 20px; |
| right: 20px; |
| background: rgba(255, 255, 255, 0.2); |
| border: none; |
| border-radius: 8px; |
| padding: 8px; |
| color: white; |
| cursor: pointer; |
| transition: background-color 0.2s; |
| } |
| |
| .close-button:hover { |
| background: rgba(255, 255, 255, 0.3); |
| } |
| |
| .close-button svg { |
| width: 24px; |
| height: 24px; |
| } |
| |
| |
| .modal-body { |
| flex: 1; |
| display: flex; |
| flex-direction: column; |
| overflow: hidden; |
| min-height: 0; |
| } |
| |
| |
| .capabilities-section { |
| padding: 4px 12px; |
| border-bottom: 1px solid #e5e7eb; |
| background: #f9fafb; |
| flex-shrink: 0; |
| max-height: 80px; |
| overflow: hidden; |
| } |
| |
| .section-title { |
| margin: 0 0 4px 0; |
| font-size: 12px; |
| font-weight: 600; |
| color: #374151; |
| } |
| |
| .specialists-grid { |
| display: grid; |
| grid-template-columns: repeat(auto-fit, minmax(100px, 1fr)); |
| gap: 3px; |
| } |
| |
| .specialist-card { |
| display: flex; |
| align-items: center; |
| padding: 4px 6px; |
| border: 1px solid #e5e7eb; |
| border-radius: 6px; |
| background: white; |
| transition: all 0.2s; |
| cursor: pointer; |
| } |
| |
| .specialist-card:hover { |
| border-color: #9ca3af; |
| transform: translateY(-1px); |
| } |
| |
| .specialist-card.active { |
| border-color: #3b82f6; |
| background: #eff6ff; |
| box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1); |
| } |
| |
| .specialist-icon { |
| font-size: 14px; |
| margin-right: 4px; |
| } |
| |
| .specialist-info { |
| flex: 1; |
| } |
| |
| .specialist-name { |
| font-size: 10px; |
| font-weight: 600; |
| color: #374151; |
| } |
| |
| .specialist-role { |
| font-size: 9px; |
| color: #6b7280; |
| } |
| |
| |
| .chat-section { |
| flex: 1; |
| display: flex; |
| flex-direction: column; |
| overflow: hidden; |
| min-height: 0; |
| } |
| |
| |
| .chat-messages { |
| flex: 1; |
| min-height: 0; |
| height: 100%; |
| overflow-y: auto !important; |
| overflow-x: hidden; |
| padding: 16px 20px; |
| background: #fafafa; |
| scroll-behavior: smooth; |
| } |
| |
| |
| .message { |
| margin-bottom: 16px; |
| padding: 12px 16px; |
| border-radius: 12px; |
| background: white; |
| border: 1px solid #e5e7eb; |
| } |
| |
| .message.user { |
| background: #eff6ff; |
| border-color: #3b82f6; |
| } |
| |
| .message.coordinator { |
| background: #f3e8ff; |
| border-color: #a855f7; |
| } |
| |
| .message.specialist { |
| background: #fef3c7; |
| border-color: #f59e0b; |
| } |
| |
| .message.delegation { |
| background: #dbeafe; |
| border-color: #60a5fa; |
| } |
| |
| .message.error { |
| background: #fee2e2; |
| border-color: #ef4444; |
| } |
| |
| .message.processing { |
| background: #f0f9ff; |
| border-color: #38bdf8; |
| } |
| |
| .message-header { |
| display: flex; |
| justify-content: space-between; |
| align-items: center; |
| margin-bottom: 8px; |
| } |
| |
| .message-agent { |
| display: flex; |
| align-items: center; |
| gap: 8px; |
| } |
| |
| .agent-icon { |
| font-size: 18px; |
| } |
| |
| .agent-icon.loading { |
| animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite; |
| } |
| |
| @keyframes pulse { |
| 0%, 100% { |
| opacity: 1; |
| } |
| 50% { |
| opacity: 0.5; |
| } |
| } |
| |
| .agent-name { |
| font-weight: 600; |
| color: #1f2937; |
| font-size: 14px; |
| } |
| |
| .agent-role { |
| font-size: 12px; |
| color: #6b7280; |
| font-style: italic; |
| } |
| |
| .message-time { |
| font-size: 11px; |
| color: #9ca3af; |
| } |
| |
| .message-content { |
| color: #374151; |
| line-height: 1.6; |
| font-size: 14px; |
| white-space: pre-wrap; |
| word-wrap: break-word; |
| } |
| |
| .message-meta { |
| display: flex; |
| gap: 12px; |
| margin-top: 8px; |
| font-size: 11px; |
| flex-wrap: wrap; |
| } |
| |
| .provider-badge { |
| padding: 2px 8px; |
| border-radius: 4px; |
| font-weight: 600; |
| font-size: 10px; |
| } |
| |
| .provider-badge.colossus { |
| background: #dcfce7; |
| color: #16a34a; |
| } |
| |
| .provider-badge.openrouter { |
| background: #dbeafe; |
| color: #2563eb; |
| } |
| |
| .processing-time { |
| color: #6b7280; |
| } |
| |
| .cost-info { |
| color: #f59e0b; |
| font-weight: 600; |
| } |
| |
| |
| .typing-indicator { |
| display: flex; |
| gap: 4px; |
| padding: 8px 0; |
| } |
| |
| .typing-indicator span { |
| width: 8px; |
| height: 8px; |
| border-radius: 50%; |
| background: #3b82f6; |
| animation: typing 1.4s infinite; |
| } |
| |
| .typing-indicator span:nth-child(2) { |
| animation-delay: 0.2s; |
| } |
| |
| .typing-indicator span:nth-child(3) { |
| animation-delay: 0.4s; |
| } |
| |
| @keyframes typing { |
| 0%, 60%, 100% { |
| transform: translateY(0); |
| } |
| 30% { |
| transform: translateY(-10px); |
| } |
| } |
| |
| .delegation-status { |
| margin-top: 8px; |
| color: #3b82f6; |
| font-weight: 600; |
| font-size: 12px; |
| } |
| |
| |
| .coordination-chain { |
| margin: 16px 0; |
| padding: 12px; |
| background: #f0f9ff; |
| border: 1px solid #38bdf8; |
| border-radius: 8px; |
| } |
| |
| .chain-title { |
| margin: 0 0 8px 0; |
| font-size: 13px; |
| font-weight: 600; |
| color: #0c4a6e; |
| } |
| |
| .chain-flow { |
| display: flex; |
| align-items: center; |
| gap: 8px; |
| flex-wrap: wrap; |
| } |
| |
| .chain-agent { |
| display: flex; |
| align-items: center; |
| gap: 6px; |
| padding: 6px 12px; |
| background: white; |
| border: 1px solid #38bdf8; |
| border-radius: 6px; |
| font-size: 12px; |
| } |
| |
| .chain-icon { |
| font-size: 16px; |
| } |
| |
| .chain-name { |
| font-weight: 600; |
| color: #0c4a6e; |
| } |
| |
| .chain-arrow { |
| width: 16px; |
| height: 16px; |
| color: #38bdf8; |
| } |
| |
| |
| .privacy-toast { |
| position: fixed; |
| bottom: 100px; |
| left: 50%; |
| transform: translateX(-50%); |
| z-index: 1100; |
| min-width: 400px; |
| max-width: 500px; |
| } |
| |
| .privacy-toast { |
| background: linear-gradient(135deg, #10b981 0%, #059669 100%); |
| border-radius: 12px; |
| padding: 16px 20px; |
| box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04); |
| display: flex; |
| align-items: center; |
| gap: 16px; |
| color: white; |
| } |
| |
| .toast-icon { |
| font-size: 28px; |
| flex-shrink: 0; |
| animation: shield-pulse 2s ease-in-out infinite; |
| } |
| |
| @keyframes shield-pulse { |
| 0%, 100% { |
| transform: scale(1); |
| } |
| 50% { |
| transform: scale(1.1); |
| } |
| } |
| |
| .toast-content { |
| flex: 1; |
| } |
| |
| .toast-title { |
| font-size: 15px; |
| font-weight: 700; |
| margin-bottom: 4px; |
| letter-spacing: 0.3px; |
| } |
| |
| .toast-message { |
| font-size: 13px; |
| opacity: 0.95; |
| line-height: 1.4; |
| } |
| |
| .toast-indicator { |
| position: absolute; |
| bottom: 0; |
| left: 0; |
| right: 0; |
| height: 3px; |
| background: rgba(255, 255, 255, 0.2); |
| border-radius: 0 0 12px 12px; |
| overflow: hidden; |
| } |
| |
| .progress-bar { |
| height: 100%; |
| background: rgba(255, 255, 255, 0.8); |
| animation: progress 3s linear forwards; |
| } |
| |
| @keyframes progress { |
| from { |
| width: 100%; |
| } |
| to { |
| width: 0%; |
| } |
| } |
| |
| |
| .toast-fade-enter-active { |
| animation: toast-slide-up 0.3s ease-out; |
| } |
| |
| .toast-fade-leave-active { |
| animation: toast-slide-down 0.3s ease-in; |
| } |
| |
| @keyframes toast-slide-up { |
| from { |
| transform: translateX(-50%) translateY(50px); |
| opacity: 0; |
| } |
| to { |
| transform: translateX(-50%) translateY(0); |
| opacity: 1; |
| } |
| } |
| |
| @keyframes toast-slide-down { |
| from { |
| transform: translateX(-50%) translateY(0); |
| opacity: 1; |
| } |
| to { |
| transform: translateX(-50%) translateY(50px); |
| opacity: 0; |
| } |
| } |
| |
| |
| .chat-input-section { |
| border-top: 2px solid #e5e7eb; |
| background: white; |
| padding: 16px 20px; |
| flex-shrink: 0; |
| } |
| |
| .input-group { |
| display: flex; |
| gap: 12px; |
| margin-bottom: 12px; |
| } |
| |
| .message-input { |
| flex: 1; |
| border: 2px solid #e5e7eb; |
| border-radius: 8px; |
| padding: 12px; |
| font-size: 14px; |
| font-family: inherit; |
| resize: vertical; |
| min-height: 60px; |
| transition: border-color 0.2s; |
| } |
| |
| .message-input:focus { |
| outline: none; |
| border-color: #3b82f6; |
| } |
| |
| .message-input:disabled { |
| background: #f3f4f6; |
| cursor: not-allowed; |
| } |
| |
| .send-button { |
| align-self: flex-end; |
| background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%); |
| color: white; |
| border: none; |
| border-radius: 8px; |
| padding: 12px 16px; |
| cursor: pointer; |
| transition: all 0.2s; |
| display: flex; |
| align-items: center; |
| justify-content: center; |
| min-width: 48px; |
| } |
| |
| .send-button:hover:not(:disabled) { |
| transform: translateY(-2px); |
| box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1); |
| } |
| |
| .send-button:disabled { |
| background: #9ca3af; |
| cursor: not-allowed; |
| transform: none; |
| } |
| |
| .send-button svg { |
| width: 20px; |
| height: 20px; |
| } |
| |
| .spinner { |
| width: 20px; |
| height: 20px; |
| border: 2px solid rgba(255, 255, 255, 0.3); |
| border-top-color: white; |
| border-radius: 50%; |
| animation: spin 0.8s linear infinite; |
| } |
| |
| @keyframes spin { |
| to { |
| transform: rotate(360deg); |
| } |
| } |
| |
| |
| .input-options { |
| display: flex; |
| flex-direction: column; |
| gap: 8px; |
| } |
| |
| .options-row { |
| display: flex; |
| gap: 12px; |
| flex-wrap: wrap; |
| } |
| |
| .task-priority, |
| .preferred-agent { |
| display: flex; |
| align-items: center; |
| gap: 8px; |
| } |
| |
| .task-priority label, |
| .preferred-agent label { |
| font-size: 12px; |
| color: #6b7280; |
| font-weight: 500; |
| } |
| |
| .priority-select, |
| .agent-select { |
| padding: 6px 10px; |
| border: 1px solid #d1d5db; |
| border-radius: 6px; |
| font-size: 12px; |
| background: white; |
| cursor: pointer; |
| transition: border-color 0.2s; |
| } |
| |
| .priority-select:focus, |
| .agent-select:focus { |
| outline: none; |
| border-color: #3b82f6; |
| } |
| |
| .provider-row { |
| display: flex; |
| align-items: center; |
| } |
| |
| .provider-selection { |
| display: flex; |
| align-items: center; |
| gap: 8px; |
| } |
| |
| .provider-selection label { |
| font-size: 12px; |
| color: #6b7280; |
| font-weight: 500; |
| } |
| |
| .provider-select { |
| padding: 6px 10px; |
| border: 1px solid #d1d5db; |
| border-radius: 6px; |
| font-size: 12px; |
| background: white; |
| cursor: pointer; |
| transition: all 0.2s; |
| } |
| |
| .provider-select:focus { |
| outline: none; |
| border-color: #3b82f6; |
| } |
| |
| .provider-select.privacy-mode { |
| border-color: #16a34a; |
| background: #f0fdf4; |
| } |
| |
| .privacy-badge { |
| padding: 4px 8px; |
| background: #dcfce7; |
| color: #16a34a; |
| border-radius: 4px; |
| font-size: 10px; |
| font-weight: 600; |
| } |
| |
| .privacy-badge.auto { |
| background: #dbeafe; |
| color: #2563eb; |
| } |
| |
| |
| .modal-footer { |
| border-top: 2px solid #e5e7eb; |
| padding: 12px 20px; |
| background: #f9fafb; |
| display: flex; |
| justify-content: space-between; |
| align-items: center; |
| flex-shrink: 0; |
| } |
| |
| .footer-stats { |
| display: flex; |
| gap: 20px; |
| } |
| |
| .stat-item { |
| display: flex; |
| align-items: center; |
| gap: 6px; |
| font-size: 12px; |
| } |
| |
| .stat-icon { |
| font-size: 16px; |
| } |
| |
| .stat-value { |
| font-weight: 700; |
| color: #1f2937; |
| } |
| |
| .stat-label { |
| color: #6b7280; |
| } |
| |
| .footer-actions { |
| display: flex; |
| gap: 8px; |
| flex-wrap: wrap; |
| } |
| |
| |
| .action-button { |
| padding: 6px 12px; |
| border-radius: 6px; |
| font-size: 11px; |
| font-weight: 600; |
| cursor: pointer; |
| transition: all 0.2s; |
| border: 1px solid; |
| white-space: nowrap; |
| } |
| |
| .action-button:hover { |
| transform: translateY(-1px); |
| box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); |
| } |
| |
| .action-button.system-status { |
| background: #dbeafe; |
| color: #1e40af; |
| border-color: #93c5fd; |
| } |
| |
| .action-button.system-status:hover { |
| background: #bfdbfe; |
| } |
| |
| .action-button.performance { |
| background: #fef3c7; |
| color: #92400e; |
| border-color: #fcd34d; |
| } |
| |
| .action-button.performance:hover { |
| background: #fde68a; |
| } |
| |
| .action-button.list-agents { |
| background: #e9d5ff; |
| color: #6b21a8; |
| border-color: #d8b4fe; |
| } |
| |
| .action-button.list-agents:hover { |
| background: #ddd6fe; |
| } |
| |
| .action-button.help { |
| background: #d1fae5; |
| color: #065f46; |
| border-color: #6ee7b7; |
| } |
| |
| .action-button.help:hover { |
| background: #a7f3d0; |
| } |
| |
| .clear-button, |
| .close-modal-button { |
| padding: 6px 12px; |
| border-radius: 6px; |
| font-size: 11px; |
| font-weight: 600; |
| cursor: pointer; |
| transition: all 0.2s; |
| } |
| |
| .clear-button { |
| background: #fee2e2; |
| color: #dc2626; |
| border: 1px solid #fca5a5; |
| } |
| |
| .clear-button:hover { |
| background: #fecaca; |
| transform: translateY(-1px); |
| } |
| |
| .close-modal-button { |
| background: #e5e7eb; |
| color: #374151; |
| border: 1px solid #d1d5db; |
| } |
| |
| .close-modal-button:hover { |
| background: #d1d5db; |
| transform: translateY(-1px); |
| } |
| |
| |
| .w-5 { |
| width: 1.25rem; |
| } |
| |
| .h-5 { |
| height: 1.25rem; |
| } |
| |
| .w-6 { |
| width: 1.5rem; |
| } |
| |
| .h-6 { |
| height: 1.5rem; |
| } |
| |
| |
| .chat-messages::-webkit-scrollbar { |
| width: 8px; |
| } |
| |
| .chat-messages::-webkit-scrollbar-track { |
| background: #f3f4f6; |
| border-radius: 4px; |
| } |
| |
| .chat-messages::-webkit-scrollbar-thumb { |
| background: #d1d5db; |
| border-radius: 4px; |
| } |
| |
| .chat-messages::-webkit-scrollbar-thumb:hover { |
| background: #9ca3af; |
| } |
| </style> |
| |