1/** 2 * Phase 3: Unified Frontend-Backend Context Integration 3 * Connects frontend context system with backend context engine for unified reference resolution 4 */ 5 6class UnifiedContextManager { 7 constructor() { 8 this.backendSessionId = null; 9 this.frontendContext = null; 10 this.isBackendSyncEnabled = true; 11 this.apiEndpoint = '/api/chat'; // Use main chat endpoint with Wikipedia processing 12 this.lastSyncTimestamp = 0; 13 this.entitySyncEnabled = true; 14 15 console.log('[Phase3] Initializing unified context manager...'); 16 this.initialize(); 17 } 18 19 /** 20 * Initialize unified context system 21 */ 22 async initialize() { 23 // Initialize frontend context if not already done 24 if (!window.conversationContext) { 25 window.conversationContext = new ConversationContext(); 26 } 27 this.frontendContext = window.conversationContext; 28 29 // Generate consistent session ID for backend 30 this.backendSessionId = this.generateBackendSessionId(); 31 32 // Sync initial session with backend 33 await this.syncWithBackend(); 34 35 console.log(`[Phase3] Unified context initialized with session: ${this.backendSessionId}`); 36 } 37 38 /** 39 * Generate backend-compatible session ID 40 */ 41 generateBackendSessionId() { 42 // Use frontend session ID if available, otherwise generate new one 43 if (this.frontendContext && this.frontendContext.sessionId) { 44 return `frontend_${this.frontendContext.sessionId}`; 45 } 46 return `unified_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; 47 } 48 49 /** 50 * Sync frontend context with backend 51 */ 52 async syncWithBackend() { 53 // Disable backend sync during chat conversations to avoid conflicts 54 // Entity synchronization now happens through the main chat stream 55 console.log('[Phase3] Backend sync disabled during conversations - using chat stream for entity sync'); 56 return null; 57 } 58 59 /** 60 * Build messages array for backend context processing 61 */ 62 buildMessagesForBackend() { 63 const messages = [ 64 { 65 role: 'system', 66 content: 'You are a helpful assistant that provides information about celebrities and movies.' 67 } 68 ]; 69 70 // Add conversation history from frontend 71 if (this.frontendContext && this.frontendContext.conversationHistory) {
72 this.frontendContext.conversationHistory.forEach(msg => { 73 messages.push({ 74 role: msg.role, 75 content: msg.content 76 }); 77 }); 78 } 79 80 return messages; 81 } 82 83 /** 84 * Update frontend context with backend insights 85 */ 86 updateFrontendFromBackend(backendResult) { 87 if (!backendResult || !backendResult.context) return; 88 89 const backendContext = backendResult.context; 90 91 // Sync entity information 92 if (backendContext.mentionedEntities) { 93 backendContext.mentionedEntities.forEach(entity => { 94 // Update frontend entity memory with backend insights 95 const key = entity.text.toLowerCase(); 96 if (this.frontendContext.entityMemory.has(key)) { 97 const existing = this.frontendContext.entityMemory.get(key); 98 existing.mentions = entity.mentions || existing.mentions; 99 existing.type = entity.type || existing.type; 100 existing.lastMention = Date.now(); 101 } else { 102 // Add new entity from backend 103 this.frontendContext.entityMemory.set(key, { 104 text: entity.text, 105 type: entity.type, 106 mentions: entity.mentions || 1, 107 firstMention: Date.now(), 108 lastMention: Date.now(), 109 contexts: [] 110 }); 111 } 112 }); 113 } 114 115 // Sync topic information 116 if (backendContext.activeTopics) { 117 backendContext.activeTopics.forEach(topic => { 118 this.frontendContext.topicMemory.add(topic); 119 }); 120 } 121 122 console.log('[Phase3] Frontend updated with backend context insights'); 123 } 124 125 /** 126 * Extract entities from AI response content to preserve them in memory 127 */ 128 extractEntitiesFromResponse(responseContent) { 129 if (!responseContent || !this.entitySyncEnabled) return; 130 131 // Extract person names (capitalized words that could be names) 132 const personPattern = /\b([A-Z][a-z]+ [A-Z][a-z]+(?:\s[A-Z][a-z]+)*)\b/g; 133 const personMatches = responseContent.match(personPattern) || []; 134 135 // Extract movie/show titles (quoted text or titles after "in", "from", etc.) 136 const titlePattern = /"([^"]+)"|(?:in|from|movie|film|show)\s+([A-Z][^.!?]*)/gi; 137 const titleMatches = []; 138 let match; 139 while ((match = titlePattern.exec(responseContent)) !== null) { 140 titleMatches.push(match[1] || match[2]); 141 } 142 143 // Add extracted entities to memory 144 [...personMatches, ...titleMatches].forEach(entityText => { 145 if (entityText && entityText.length > 2) { 146 const key = entityText.toLowerCase(); 147 const entityType = personMatches.includes(entityText) ? 'person' : 'title'; 148 149 if (this.frontendContext.entityMemory.has(key)) { 150 const existing = this.frontendContext.entityMemory.get(key); 151 existing.mentions += 1; 152 existing.lastMention = Date.now(); 153 } else { 154 this.frontendContext.entityMemory.set(key, { 155 text: entityText, 156 type: entityType, 157 mentions: 1, 158 firstMention: Date.now(), 159 lastMention: Date.now(), 160 contexts: [] 161 }); 162 } 163 } 164 }); 165 166 console.log(`[Phase3] Extracted ${personMatches.length} persons and ${titleMatches.length} titles from AI response`); 167 } 168 169 /** 170 * Enhanced reference resolution using backend context 171 */ 172 async resolveReferencesWithBackend(query) { 173 console.log(`[Phase3] Resolving references for: "${query}"`); 174 175 // First try frontend resolution 176 let resolvedQuery = this.frontendContext.resolveReferences(query); 177 178 // If no change, try backend-enhanced resolution 179 if (resolvedQuery === query && this.isBackendSyncEnabled) { 180 try { 181 // Send query to backend for context-enhanced resolution 182 const messages = this.buildMessagesForBackend(); 183 messages.push({ 184 role: 'user', 185 content: query 186 }); 187 188 const response = await fetch(this.apiEndpoint, { 189 method: 'POST', 190 headers: { 191 'Content-Type': 'application/json', 192 'X-Session-ID': this.backendSessionId 193 }, 194 body: JSON.stringify({ messages }) 195 }); 196 197 if (response.ok) { 198 const result = await response.json(); 199 200 // Check if backend provided better context 201 if (result.context && result.context.contextSummary) { 202 // Use backend context to enhance resolution 203 resolvedQuery = this.enhanceQueryWithBacken
203dContext(query, result.context); 204 } 205 } 206 } catch (error) { 207 console.warn('[Phase3] Backend reference resolution failed:', error); 208 } 209 } 210 211 if (resolvedQuery !== query) { 212 console.log(`[Phase3] Resolved: "${query}" -> "${resolvedQuery}"`); 213 } 214 215 return resolvedQuery; 216 } 217 218 /** 219 * Enhance query with backend context insights 220 */ 221 enhanceQueryWithBackendContext(query, backendContext) { 222 let enhancedQuery = query; 223 224 // Extract entity information from backend context summary 225 if (backendContext.contextSummary) { 226 const summary = backendContext.contextSummary; 227 228 // Look for person entities mentioned in context 229 const personMatch = summary.match(/([A-Z][a-z]+ [A-Z][a-z]+) \(person/); 230 if (personMatch) { 231 const personName = personMatch[1]; 232 233 // Replace pronouns with person name 234 enhancedQuery = enhancedQuery.replace(/\bhis\b/gi, `${personName}'s`); 235 enhancedQuery = enhancedQuery.replace(/\bher\b/gi, `${personName}'s`); 236 enhancedQuery = enhancedQuery.replace(/\bhim\b/gi, personName); 237 enhancedQuery = enhancedQuery.replace(/\bshe\b/gi, personName); 238 } 239 } 240 241 return enhancedQuery; 242 } 243 244 /** 245 * Add message to both frontend and backend context 246 */ 247 async addMessage(role, content, metadata = {}) { 248 // Add to frontend context 249 this.frontendContext.addMessage(role, content, metadata); 250 251 // Sync with backend periodically 252 if (role === 'user') { 253 await this.syncWithBackend(); 254 } 255 256 console.log(`[Phase3] Added ${role} message to unified context`); 257 } 258 259 /** 260 * Get unified context for query 261 */ 262 async getContextForQuery(query) { 263 // Get frontend context 264 const frontendContext = this.frontendContext.getContextForQuery(query); 265 266 // Enhance with backend context if available 267 const backendSync = await this.syncWithBackend(); 268 269 return { 270 ...frontendContext, 271 backendSessionId: this.backendSessionId, 272 backendContext: backendSync ? backendSync.context : null, 273 unified: true 274 }; 275 } 276 277 /** 278 * Get session statistics from both systems 279 */ 280 getSessionStats() { 281 const frontendStats = { 282 conversationLength: this.frontendContext.conversationHistory.length, 283 entityCount: this.frontendContext.entityMemory.size, 284 topicCount: this.frontendContext.topicMemory.size 285 }; 286 287 return { 288 sessionId: this.backendSessionId, 289 frontend: frontendStats, 290 unified: true 291 }; 292 } 293} 294
295// Enhanced sendMessage function for Phase 3 296let originalSendMessage = null; 297let unifiedContextManager = null; 298 299async function phase3EnhancedSendMessage(text) { 300 console.log('[Phase3] Enhanced sendMessage called with:', text); 301 302 // Handle undefined/null text input 303 if (!text || typeof text !== 'string' || text.trim() === '') { 304 console.warn('[Phase3] Invalid text input, skipping contextual processing'); 305 if (originalSendMessage) { 306 return originalSendMessage.call(this, text); 307 } 308 return; 309 } 310 311 // Initialize unified context manager if not done 312 if (!unifiedContextManager) { 313 unifiedContextManager = new UnifiedContextManager(); 314 await unifiedContextManager.initialize(); 315 } 316 317 try { 318 // Periodic cleanup to prevent stale context carryover 319 if (Math.random() < 0.1) { // 10% chance to run cleanup 320 console.log('[Phase3] Running periodic context cleanup'); 321 unifiedContextManager.frontendContext.clearStaleContext(); 322 } 323 324 // Extract entities from user query first 325 unifiedContextManager.extractEntitiesFromResponse(text); 326 327 // DISABLED: Frontend reference resolution causes issues like "his wives" -> "Albert Einsteinss" 328 // The backend OpenAI entity extraction is much more sophisticated and handles this correctly 329 console.log('[Phase3] Skipping frontend reference resolution - using backend AI resolution instead'); 330 331 // Add user message to unified context 332 await unifiedContextManager.addMessage('user', text); 333 334 // Get context info for display 335 const contextInfo = unifiedContextManager.frontendContext.getContextForQuery(text); 336 console.log('[Phase3] Context info:', contextInfo); 337 338 // Display Phase 3 status 339 const statusElement = document.getElementById('phase3-status'); 340 if (statusElement) { 341 const entityCount = unifiedContextManager.frontendContext.entityMemory.size; 342 const topicCount = unifiedContextManager.frontendContext.topicMemory.size; 343 statusElement.innerHTML = `Phase 3 Active | Session: ${unifiedContextManager.backendSessionId} | Entities: ${entityCount} | Topics: ${topicCount}`; 344 } 345 346 } catch (error) { 347 console.error('[Phase3] Enhanced sendMessage error:', error); 348 } 349 350 // Call original sendMessage with potentially resolved text 351 if (originalSendMessage) { 352 return originalSendMessage.call(this, text); 353 } 354} 355 356// Function to enhance AI responses with unified context 357async function phase3EnhanceAIResponse(response) { 358 if (unifiedContextManager && response) { 359 // Add AI response to unified context 360 await unifiedContextManager.addMessage('assistant', response); 361 console.log('[Phase3] Added AI response to unified context'); 362 363 // Extract entities from AI response 364 unifiedContextManager.extractEntitiesFromResponse(response); 365 } 366 return response; 367} 368 369// Initialize Phase 3 contextual integration 370function initializePhase3Integration() { 371 console.log('[Phase3] Initializing unified frontend-backend context integration...'); 372 373 // Wait for main.js to load and sendMessage to be available 374 const checkForSendMessage = () => { 375 if (typeof window.sendMessage === 'function') { 376 console.log('[Phase3] Found sendMessage function, enhancing with unified context...'); 377 378 // Store original function 379 originalSendMessage = window.sendMessage; 380 381 // Replace with Phase 3 enhanced version 382 window.sendMessage = phase3EnhancedSendMessage; 383 384 console.log('[Phase3] Unified context integration complete!'); 385 386 /* 387 // Add Phase 3 status indicator 388 const statusDiv = document.createElement('div'); 389 statusDiv.id = 'phase3-status'; 390 statusDiv.innerHTML = 'ð Phase 3: Unified Context Active'; 391 statusDiv.style.cssText = `
392 position: fixed; 393 top: 10px; 394 right: 10px; 395 background: #e8f0ff; 396 color: #0066cc; 397 padding: 5px 10px; 398 border-radius: 15px; 399 font-size: 12px; 400 z-index: 1000; 401 border: 1px solid #99ccff; 402 opacity: 0.9; 403 font-weight: bold; 404 `; 405 document.body.appendChild(statusDiv); 406 */ 407 408 // Remove old status indicators 409 const oldStatus = document.getElementById('context-status'); 410 if (oldStatus) oldStatus.remove(); 411 412 } else { 413 setTimeout(checkForSendMessage, 100); 414 } 415 }; 416 417 checkForSendMessage(); 418} 419 420// Auto-initialize when DOM is ready 421if (document.readyState === 'loading') { 422 document.addEventListener('DOMContentLoaded', initializePhase3Integration); 423} else { 424 initializePhase3Integration(); 425} 426 427// Export for global access 428window.UnifiedContextManager = UnifiedContextManager; 429window.phase3EnhancedSendMessage = phase3EnhancedSendMessage; 430window.initializePhase3Integration = initializePhase3Integration;
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.