Spaces:
Running
Running
Komalpreet Kaur
feat: implement memory consolidation service with sleep cycle, add frontend visualization components, and integrate backend database orchestration.
2e9dd8e unverified | import { useState, useMemo, useEffect, useCallback } from 'react'; | |
| import { apiFetch } from '../api'; | |
| import './KnowledgeInput.css'; | |
| function KnowledgeInput({ onKnowledgeSubmit, isBusy, status }) { | |
| const [text, setText] = useState(''); | |
| const [analysis, setAnalysis] = useState(null); | |
| const [lastAnalysis, setLastAnalysis] = useState(null); | |
| const [isAnalyzing, setIsAnalyzing] = useState(false); | |
| const handleSubmit = () => { | |
| if (!text.trim() || isBusy) return; | |
| onKnowledgeSubmit(text); | |
| if (analysis) { | |
| setLastAnalysis(analysis); | |
| } else { | |
| // Fallback if they clicked instantly or text was < 50 chars | |
| const charCount = text.length; | |
| setLastAnalysis({ | |
| metrics: { | |
| density: Math.min(charCount / 2000, 1.0), | |
| chunks: Math.floor(charCount / 500) + 1, | |
| estimated_links: 0, | |
| reinforcement_index: 0 | |
| }, | |
| entities: [], | |
| existing_links: [] | |
| }); | |
| } | |
| setText(''); | |
| setAnalysis(null); | |
| }; | |
| // Debounced analysis function | |
| useEffect(() => { | |
| if (text.length < 50) { | |
| setAnalysis(null); | |
| return; | |
| } | |
| const timer = setTimeout(async () => { | |
| setIsAnalyzing(true); | |
| try { | |
| const res = await apiFetch('/api/v1/analyze', { | |
| method: 'POST', | |
| body: JSON.stringify({ text }) | |
| }); | |
| if (res.ok) { | |
| const data = await res.json(); | |
| setAnalysis(data); | |
| } | |
| } catch (error) { | |
| console.error('Analysis failed', error); | |
| } finally { | |
| setIsAnalyzing(false); | |
| } | |
| }, 1500); // 1.5s debounce | |
| return () => clearTimeout(timer); | |
| }, [text]); | |
| const displayAnalysis = analysis || lastAnalysis; | |
| return ( | |
| <div className="inscription-grid fade-in"> | |
| {/* ── LEFT: The Inscription Terminal ── */} | |
| <div className="inscription-main"> | |
| {status && ( | |
| <div className="inscription-status fade-in"> | |
| <span className="material-icons">check_circle</span> | |
| {status} | |
| </div> | |
| )} | |
| <div className="inscription-header"> | |
| <div className="inscription-icon"> | |
| <span className="material-icons">auto_awesome</span> | |
| </div> | |
| <div className="inscription-title"> | |
| <h3>Neural Inscription</h3> | |
| <p>Directly seed the core cortex with high-fidelity knowledge.</p> | |
| </div> | |
| </div> | |
| <div className="inscription-composer"> | |
| <textarea | |
| value={text} | |
| onChange={(e) => { | |
| setText(e.target.value); | |
| if (lastAnalysis) setLastAnalysis(null); | |
| }} | |
| placeholder="Paste research, notes, or data chunks here for deep integration..." | |
| maxLength={10000} | |
| /> | |
| <div className="inscription-footer"> | |
| <div className="inscription-stats"> | |
| <span className="char-count">{text.length} / 10000 Chars</span> | |
| {isBusy && <span className="busy-tag pulse">Integrating...</span>} | |
| {isAnalyzing && <span className="busy-tag pulse" style={{color: '#3b82f6'}}>Analyzing Pattern...</span>} | |
| </div> | |
| <button | |
| className="inscription-btn" | |
| onClick={handleSubmit} | |
| disabled={!text.trim() || isBusy} | |
| > | |
| <span className="material-icons">memory</span> | |
| Inscribe Knowledge | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| {/* ── RIGHT: Neural Analysis Preview ── */} | |
| <div className="inscription-sidebar"> | |
| <div className="analysis-panel"> | |
| <div className="panel-label"> | |
| <span className="material-icons">biotech</span> | |
| {(!analysis && lastAnalysis) ? 'Last Inscription Details' : 'Neural Analysis Preview'} | |
| </div> | |
| <div className="analysis-content"> | |
| <div className="analysis-metric"> | |
| <label>Information Density</label> | |
| <div className="mini-meter"> | |
| <div className="mini-fill" style={{ width: `${(displayAnalysis?.metrics?.density || 0) * 100}%` }} /> | |
| </div> | |
| <div className="metric-sub"> | |
| <span>{Math.round((displayAnalysis?.metrics?.density || 0) * 100)}% Salience</span> | |
| </div> | |
| </div> | |
| <div className="analysis-metric"> | |
| <label>Knowledge Reinforcement</label> | |
| <div className="mini-meter"> | |
| <div className="mini-fill" style={{ | |
| width: `${(displayAnalysis?.metrics?.reinforcement_index || 0) * 100}%`, | |
| background: '#10b981' | |
| }} /> | |
| </div> | |
| <div className="metric-sub"> | |
| <span>{Math.round((displayAnalysis?.metrics?.reinforcement_index || 0) * 100)}% Integration</span> | |
| </div> | |
| </div> | |
| <div className="analysis-section"> | |
| <label>Potential Semantic Links</label> | |
| <div className="link-tags"> | |
| {(displayAnalysis?.entities || []).map((entity, i) => { | |
| const isExisting = displayAnalysis?.existing_links?.some(l => l.name.toLowerCase() === entity.toLowerCase()); | |
| return ( | |
| <div | |
| key={i} | |
| className={`link-tag fade-in ${isExisting ? 'reinforced' : ''}`} | |
| style={{animationDelay: `${i * 0.1}s`}} | |
| > | |
| <span className="dot" /> | |
| {entity} | |
| {isExisting && <span className="material-icons reinforced-icon">offline_bolt</span>} | |
| </div> | |
| ); | |
| })} | |
| {(!displayAnalysis?.entities || displayAnalysis.entities.length === 0) && ( | |
| <div className="empty-tag"> | |
| {isAnalyzing ? 'Decoding semantic structure...' : 'Awaiting sufficient context (50+ chars)...'} | |
| </div> | |
| )} | |
| </div> | |
| </div> | |
| {displayAnalysis?.existing_links?.length > 0 && ( | |
| <div className="analysis-section fade-in"> | |
| <label>Reinforced Concepts</label> | |
| <div className="existing-links-list"> | |
| {displayAnalysis.existing_links.map((link, i) => ( | |
| <div key={i} className="existing-link-item"> | |
| <span className="material-icons">hub</span> | |
| <div className="link-info"> | |
| <strong>{link.name}</strong> | |
| <span>{link.connections} existing connections</span> | |
| </div> | |
| </div> | |
| ))} | |
| </div> | |
| </div> | |
| )} | |
| <div className="analysis-section"> | |
| <label>Estimated Memory Impact</label> | |
| <div className="impact-stats"> | |
| <div className="impact-item"> | |
| <span className="impact-val">{displayAnalysis?.metrics?.chunks || 0}</span> | |
| <span className="impact-label">Sensory Chunks</span> | |
| </div> | |
| <div className="impact-item"> | |
| <span className="impact-val">{Math.floor(displayAnalysis?.metrics?.estimated_links || 0)}</span> | |
| <span className="impact-label">Graph Triples</span> | |
| </div> | |
| </div> | |
| </div> | |
| <div className="analysis-section"> | |
| <label>Target Layers</label> | |
| <div className="layer-item"> | |
| <span className="dot" style={{background: '#ff6b35'}} /> | |
| <span>Semantic Memory (Graph)</span> | |
| </div> | |
| <div className="layer-item"> | |
| <span className="dot" style={{background: '#10b981'}} /> | |
| <span>Sensory Cortex (Vector)</span> | |
| </div> | |
| </div> | |
| </div> | |
| <div className="panel-footer"> | |
| <span className="material-icons">info</span> | |
| Data will be parsed into triples and vectorized. | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| ); | |
| } | |
| export default KnowledgeInput; | |