Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>LTMS Truth Maintenance — Live Console</title> | |
| <style> | |
| :root { | |
| --bg-void: #020508; | |
| --bg-deep: #050b12; | |
| --bg-panel: rgba(8, 18, 28, 0.82); | |
| --bg-panel-strong: rgba(9, 24, 36, 0.96); | |
| --line-soft: rgba(112, 220, 255, 0.12); | |
| --line-medium: rgba(112, 220, 255, 0.28); | |
| --line-bright: rgba(112, 220, 255, 0.72); | |
| --text-primary: #e8fbff; | |
| --text-secondary: #8eb5c1; | |
| --text-muted: #52707a; | |
| --cyan: #56e8ff; | |
| --cyan-hot: #a9f7ff; | |
| --teal: #38ffc7; | |
| --violet: #ab7cff; | |
| --gold: #ffc857; | |
| --red: #ff5470; | |
| --green: #5dff9d; | |
| --shadow-cyan: 0 0 18px rgba(86, 232, 255, 0.2), 0 0 52px rgba(86, 232, 255, 0.08); | |
| --shadow-violet: 0 0 20px rgba(171, 124, 255, 0.2), 0 0 60px rgba(171, 124, 255, 0.08); | |
| --radius-xs: 6px; | |
| --radius-sm: 10px; | |
| --radius-md: 16px; | |
| --radius-lg: 24px; | |
| --font-display: "Orbitron", "Rajdhani", "Segoe UI", sans-serif; | |
| --font-mono: "JetBrains Mono", "IBM Plex Mono", "Cascadia Code", monospace; | |
| --ease-system: cubic-bezier(0.22, 1, 0.36, 1); | |
| } | |
| * { box-sizing: border-box; } | |
| html { min-height: 100%; background: var(--bg-void); } | |
| body { | |
| min-height: 100vh; | |
| margin: 0; | |
| color: var(--text-primary); | |
| font-family: var(--font-mono); | |
| background: linear-gradient(180deg, #020508 0%, #041019 48%, #020609 100%); | |
| } | |
| button, input, textarea { font: inherit; color: inherit; } | |
| .ltms-shell { | |
| width: min(1800px, 100%); | |
| margin: 0 auto; | |
| padding: 18px; | |
| } | |
| .ltms-header { | |
| position: sticky; | |
| top: 12px; | |
| z-index: 30; | |
| display: flex; | |
| gap: 20px; | |
| align-items: center; | |
| min-height: 76px; | |
| padding: 14px 18px; | |
| border: 1px solid var(--line-medium); | |
| border-radius: var(--radius-lg); | |
| background: linear-gradient(135deg, rgba(9, 24, 36, 0.94), rgba(4, 12, 19, 0.91)); | |
| box-shadow: var(--shadow-cyan), inset 0 1px rgba(255, 255, 255, 0.04); | |
| backdrop-filter: blur(22px) saturate(135%); | |
| margin-bottom: 20px; | |
| } | |
| .ltms-title { | |
| font-family: var(--font-display); | |
| font-size: 1.35rem; | |
| font-weight: 700; | |
| letter-spacing: 0.15em; | |
| text-transform: uppercase; | |
| color: var(--violet); | |
| } | |
| .ltms-subtitle { | |
| color: var(--text-secondary); | |
| font-size: 0.75rem; | |
| letter-spacing: 0.1em; | |
| } | |
| .ltms-status { | |
| display: flex; | |
| gap: 12px; | |
| margin-left: auto; | |
| } | |
| .status-chip { | |
| display: inline-flex; | |
| gap: 8px; | |
| align-items: center; | |
| padding: 8px 12px; | |
| border: 1px solid var(--line-soft); | |
| border-radius: 999px; | |
| color: var(--text-secondary); | |
| background: rgba(2, 10, 16, 0.55); | |
| font-size: 0.75rem; | |
| } | |
| .status-chip[data-state="ready"] { | |
| color: var(--green); | |
| border-color: rgba(93, 255, 157, 0.35); | |
| } | |
| .status-dot { | |
| width: 6px; | |
| height: 6px; | |
| border-radius: 50%; | |
| background: currentColor; | |
| box-shadow: 0 0 10px currentColor; | |
| } | |
| .ltms-grid { | |
| display: grid; | |
| grid-template-columns: 1fr 1fr; | |
| gap: 16px; | |
| margin-bottom: 20px; | |
| } | |
| .ltms-panel { | |
| border: 1px solid var(--line-soft); | |
| border-radius: var(--radius-md); | |
| background: linear-gradient(160deg, rgba(10, 26, 38, 0.9), rgba(3, 11, 17, 0.96)); | |
| box-shadow: 0 20px 80px rgba(0, 0, 0, 0.26), inset 0 1px rgba(255, 255, 255, 0.035); | |
| overflow: hidden; | |
| } | |
| .panel-header { | |
| display: flex; | |
| gap: 12px; | |
| align-items: center; | |
| min-height: 52px; | |
| padding: 0 16px; | |
| border-bottom: 1px solid var(--line-soft); | |
| background: linear-gradient(180deg, rgba(255, 255, 255, 0.025), transparent); | |
| } | |
| .panel-title { | |
| display: flex; | |
| gap: 10px; | |
| align-items: center; | |
| margin: 0; | |
| color: var(--cyan-hot); | |
| font-family: var(--font-display); | |
| font-size: 0.78rem; | |
| letter-spacing: 0.14em; | |
| text-transform: uppercase; | |
| } | |
| .panel-body { | |
| padding: 14px; | |
| } | |
| .input-group { | |
| display: flex; | |
| gap: 8px; | |
| margin-bottom: 12px; | |
| } | |
| .input-group input { | |
| flex: 1; | |
| padding: 8px 12px; | |
| border: 1px solid var(--line-soft); | |
| background: #03080d; | |
| color: var(--cyan); | |
| border-radius: var(--radius-xs); | |
| font-size: 0.85rem; | |
| } | |
| .input-group button { | |
| padding: 8px 16px; | |
| border: 1px solid var(--line-medium); | |
| background: linear-gradient(180deg, rgba(18, 47, 65, 0.9), rgba(6, 19, 28, 0.95)); | |
| color: var(--text-primary); | |
| border-radius: var(--radius-xs); | |
| cursor: pointer; | |
| transition: all 160ms var(--ease-system); | |
| text-transform: uppercase; | |
| font-size: 0.75rem; | |
| font-weight: bold; | |
| } | |
| .input-group button:hover { | |
| border-color: var(--line-bright); | |
| box-shadow: var(--shadow-cyan); | |
| } | |
| .input-group button[data-variant="primary"] { | |
| background: linear-gradient(135deg, var(--cyan-hot), var(--cyan)); | |
| color: #001115; | |
| border-color: transparent; | |
| } | |
| .belief-list { | |
| display: flex; | |
| flex-direction: column; | |
| gap: 8px; | |
| max-height: 500px; | |
| overflow-y: auto; | |
| } | |
| .belief-node { | |
| position: relative; | |
| padding: 12px 14px 12px 18px; | |
| border: 1px solid var(--line-soft); | |
| border-radius: var(--radius-sm); | |
| background: rgba(4, 13, 20, 0.78); | |
| transition: all 200ms var(--ease-system); | |
| } | |
| .belief-node::before { | |
| position: absolute; | |
| top: 14px; | |
| bottom: 14px; | |
| left: 8px; | |
| width: 2px; | |
| content: ""; | |
| border-radius: 2px; | |
| background: linear-gradient(var(--cyan), var(--violet)); | |
| } | |
| .belief-node[data-status="asserted"] { | |
| border-left-color: var(--green); | |
| } | |
| .belief-node[data-status="assumed"] { | |
| border-left-color: var(--gold); | |
| } | |
| .belief-node[data-status="derived"] { | |
| border-left-color: var(--teal); | |
| } | |
| .belief-node[data-status="retracted"] { | |
| border-left-color: var(--red); | |
| opacity: 0.5; | |
| text-decoration: line-through; | |
| } | |
| .belief-label { | |
| color: var(--text-primary); | |
| font-weight: bold; | |
| margin-bottom: 4px; | |
| } | |
| .belief-meta { | |
| display: flex; | |
| gap: 12px; | |
| font-size: 0.7rem; | |
| color: var(--text-muted); | |
| margin-top: 4px; | |
| } | |
| .belief-action { | |
| font-size: 0.65rem; | |
| color: var(--cyan); | |
| cursor: pointer; | |
| padding: 2px 6px; | |
| border-radius: 3px; | |
| transition: all 100ms var(--ease-system); | |
| } | |
| .belief-action:hover { | |
| background: rgba(86, 232, 255, 0.1); | |
| } | |
| .derivation-chain { | |
| display: flex; | |
| flex-direction: column; | |
| gap: 8px; | |
| max-height: 500px; | |
| overflow-y: auto; | |
| } | |
| .derivation-step { | |
| padding: 10px; | |
| border-left: 3px solid var(--teal); | |
| background: rgba(56, 255, 199, 0.05); | |
| border-radius: var(--radius-xs); | |
| font-size: 0.8rem; | |
| } | |
| .derivation-step .step-label { | |
| color: var(--teal); | |
| font-weight: bold; | |
| margin-bottom: 4px; | |
| } | |
| .derivation-step .step-content { | |
| color: var(--text-secondary); | |
| font-family: var(--font-mono); | |
| } | |
| .contradiction-alert { | |
| padding: 12px; | |
| border: 1px solid var(--red); | |
| background: rgba(255, 84, 112, 0.1); | |
| border-radius: var(--radius-sm); | |
| color: var(--red); | |
| margin-bottom: 12px; | |
| animation: pulse-red 1s infinite; | |
| } | |
| @keyframes pulse-red { | |
| 0%, 100% { box-shadow: inset 0 0 0 1px rgba(255, 84, 112, 0.3); } | |
| 50% { box-shadow: inset 0 0 0 1px rgba(255, 84, 112, 0.8); } | |
| } | |
| .execution-trace { | |
| display: flex; | |
| flex-direction: column; | |
| gap: 6px; | |
| max-height: 600px; | |
| overflow-y: auto; | |
| font-size: 0.75rem; | |
| } | |
| .trace-line { | |
| padding: 6px 8px; | |
| border-left: 2px solid var(--line-soft); | |
| color: var(--text-secondary); | |
| } | |
| .trace-line.assert { | |
| border-left-color: var(--green); | |
| color: var(--green); | |
| } | |
| .trace-line.assume { | |
| border-left-color: var(--gold); | |
| color: var(--gold); | |
| } | |
| .trace-line.derive { | |
| border-left-color: var(--teal); | |
| color: var(--teal); | |
| } | |
| .trace-line.retract { | |
| border-left-color: var(--red); | |
| color: var(--red); | |
| } | |
| .demo-buttons { | |
| display: grid; | |
| grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); | |
| gap: 8px; | |
| margin-top: 16px; | |
| padding-top: 16px; | |
| border-top: 1px solid var(--line-soft); | |
| } | |
| .demo-btn { | |
| padding: 10px 12px; | |
| border: 1px solid var(--line-soft); | |
| background: rgba(2, 10, 16, 0.55); | |
| color: var(--cyan); | |
| border-radius: var(--radius-xs); | |
| cursor: pointer; | |
| font-size: 0.75rem; | |
| transition: all 100ms var(--ease-system); | |
| } | |
| .demo-btn:hover { | |
| border-color: var(--line-bright); | |
| background: rgba(86, 232, 255, 0.1); | |
| } | |
| .full-width { | |
| grid-column: 1 / -1; | |
| } | |
| @media (max-width: 1024px) { | |
| .ltms-grid { | |
| grid-template-columns: 1fr; | |
| } | |
| .ltms-header { | |
| flex-direction: column; | |
| align-items: flex-start; | |
| } | |
| .ltms-status { | |
| margin-left: 0; | |
| width: 100%; | |
| } | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="ltms-shell"> | |
| <div class="ltms-header"> | |
| <div> | |
| <h1 class="ltms-title">🧠 LTMS</h1> | |
| <p class="ltms-subtitle">Layered Truth Maintenance System — Live Belief Graph</p> | |
| </div> | |
| <div class="ltms-status"> | |
| <div class="status-chip" data-state="ready"> | |
| <div class="status-dot"></div> | |
| <span>KNOWLEDGE ENGINE</span> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="ltms-grid"> | |
| <!-- BELIEF OPERATIONS --> | |
| <div class="ltms-panel"> | |
| <div class="panel-header"> | |
| <h2 class="panel-title">📝 Operations</h2> | |
| </div> | |
| <div class="panel-body"> | |
| <div class="input-group"> | |
| <input type="text" id="belief-input" placeholder="Enter belief (e.g., Socrates is human)"> | |
| <button onclick="assertBelief()" data-variant="primary">Assert</button> | |
| </div> | |
| <div class="input-group"> | |
| <input type="text" id="rule-input" placeholder="Enter rule (e.g., Every human is mortal)"> | |
| <button onclick="addRule()">Rule</button> | |
| </div> | |
| <div style="margin-top: 16px; border-top: 1px solid var(--line-soft); padding-top: 16px;"> | |
| <h3 style="color: var(--gold); font-size: 0.8rem; margin: 0 0 12px; text-transform: uppercase;">Queries</h3> | |
| <div class="input-group"> | |
| <input type="text" id="query-input" placeholder="Query belief (e.g., Socrates is mortal)"> | |
| <button onclick="queryBelief()">Exact</button> | |
| </div> | |
| <div class="input-group"> | |
| <input type="text" id="semantic-input" placeholder="Semantic search (e.g., mortality)"> | |
| <button onclick="semanticSearch()">🔍 Semantic</button> | |
| </div> | |
| </div> | |
| <div class="demo-buttons"> | |
| <button class="demo-btn" onclick="socraticDemo()">📖 Socratic Demo</button> | |
| <button class="demo-btn" onclick="contradictionDemo()">⚠️ Contradiction</button> | |
| <button class="demo-btn" onclick="clearAll()">🔄 Clear All</button> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- BELIEF STATE --> | |
| <div class="ltms-panel"> | |
| <div class="panel-header"> | |
| <h2 class="panel-title">💭 Beliefs</h2> | |
| <span style="margin-left: auto; color: var(--text-muted); font-size: 0.7rem;" id="belief-count">0 active</span> | |
| </div> | |
| <div class="panel-body"> | |
| <div id="contradiction-alerts"></div> | |
| <div class="belief-list" id="belief-list"> | |
| <div style="color: var(--text-muted); font-size: 0.75rem;">No beliefs yet. Assert or assume a belief to begin.</div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- DERIVATIONS (FULL WIDTH) --> | |
| <div class="ltms-panel full-width"> | |
| <div class="panel-header"> | |
| <h2 class="panel-title">⛓️ Derivation Chain</h2> | |
| <span style="margin-left: auto; color: var(--text-muted); font-size: 0.7rem;" id="step-count">0 steps</span> | |
| </div> | |
| <div class="panel-body"> | |
| <div class="derivation-chain" id="derivation-chain"> | |
| <div style="color: var(--text-muted); font-size: 0.75rem;">Waiting for inferences...</div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- EXECUTION TRACE (FULL WIDTH) --> | |
| <div class="ltms-panel full-width"> | |
| <div class="panel-header"> | |
| <h2 class="panel-title">📋 Execution Log</h2> | |
| </div> | |
| <div class="panel-body"> | |
| <div class="execution-trace" id="execution-trace"> | |
| <div class="trace-line" style="color: var(--cyan);">• System initialized</div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <script type="module"> | |
| import ONNXBridge from './onnx-bridge.mjs'; | |
| window.ONNXBridge = ONNXBridge; | |
| </script> | |
| <script> | |
| // ======================================================================== | |
| // LTMS State Management | |
| // ======================================================================== | |
| const state = { | |
| beliefs: new Map(), // Map<id, { label, status, justification, timestamp }> | |
| rules: [], // Array<{ antecedent, consequent }> | |
| derivations: [], // Array<{ step, result, source }> | |
| contradictions: [], // Array<{ belief1, belief2 }> | |
| executionLog: [], // Array<{ timestamp, action, detail }> | |
| beliefCounter: 0, | |
| }; | |
| const STATUS = { | |
| ASSERTED: 'asserted', | |
| ASSUMED: 'assumed', | |
| DERIVED: 'derived', | |
| RETRACTED: 'retracted', | |
| }; | |
| // ======================================================================== | |
| // Core Operations | |
| // ======================================================================== | |
| function assertBelief() { | |
| const input = document.getElementById('belief-input'); | |
| const label = input.value.trim(); | |
| if (!label) return; | |
| const id = `belief-${++state.beliefCounter}`; | |
| state.beliefs.set(id, { | |
| id, | |
| label, | |
| status: STATUS.ASSERTED, | |
| justification: 'user assertion', | |
| timestamp: Date.now(), | |
| dependents: [], | |
| }); | |
| logExecution('assert', label); | |
| input.value = ''; | |
| // Check for contradictions | |
| checkContradictions(); | |
| // Trigger inference | |
| inferenceStep(); | |
| render(); | |
| } | |
| function addRule() { | |
| const input = document.getElementById('rule-input'); | |
| const rule = input.value.trim(); | |
| if (!rule) return; | |
| // Parse simple rule format: "If X then Y" | |
| const match = rule.match(/If\s+(.+?)\s+then\s+(.+)/i); | |
| if (!match) { | |
| logExecution('error', 'Rule format: "If X then Y"'); | |
| return; | |
| } | |
| state.rules.push({ | |
| antecedent: match[1].trim(), | |
| consequent: match[2].trim(), | |
| timestamp: Date.now(), | |
| }); | |
| logExecution('rule_add', rule); | |
| input.value = ''; | |
| // Re-run inference with new rule | |
| inferenceStep(); | |
| render(); | |
| } | |
| function queryBelief() { | |
| const input = document.getElementById('query-input'); | |
| const query = input.value.trim(); | |
| if (!query) return; | |
| const matches = Array.from(state.beliefs.values()) | |
| .filter(b => b.label.toLowerCase().includes(query.toLowerCase()) && b.status !== STATUS.RETRACTED); | |
| if (matches.length === 0) { | |
| logExecution('query', `No beliefs match: "${query}"`); | |
| } else { | |
| matches.forEach(m => { | |
| logExecution('query_result', `${m.label} (${m.status})`); | |
| }); | |
| } | |
| input.value = ''; | |
| render(); | |
| } | |
| async function semanticSearch() { | |
| const input = document.getElementById('semantic-input'); | |
| const query = input.value.trim(); | |
| if (!query) return; | |
| logExecution('semantic_search', `Searching for: "${query}"`); | |
| try { | |
| if (!window.ONNXBridge) { | |
| logExecution('error', 'ONNX not loaded'); | |
| return; | |
| } | |
| // Get all active beliefs | |
| const candidates = Array.from(state.beliefs.values()) | |
| .filter(b => b.status !== STATUS.RETRACTED) | |
| .map(b => b.label); | |
| if (candidates.length === 0) { | |
| logExecution('semantic_result', 'No beliefs to search'); | |
| input.value = ''; | |
| return; | |
| } | |
| // Perform semantic search | |
| const results = await window.ONNXBridge.search(query, candidates); | |
| if (results.length === 0) { | |
| logExecution('semantic_result', 'No semantically similar beliefs found'); | |
| } else { | |
| results.slice(0, 5).forEach(r => { | |
| logExecution('semantic_result', `"${r.text}" (similarity: ${(r.similarity * 100).toFixed(0)}%)`); | |
| }); | |
| } | |
| } catch (err) { | |
| logExecution('error', `Semantic search failed: ${err.message}`); | |
| } | |
| input.value = ''; | |
| render(); | |
| } | |
| function inferenceStep() { | |
| state.rules.forEach(rule => { | |
| // Check if antecedent matches any active belief | |
| const antecedentBeliefs = Array.from(state.beliefs.values()) | |
| .filter(b => b.label.toLowerCase().includes(rule.antecedent.toLowerCase()) && b.status !== STATUS.RETRACTED); | |
| if (antecedentBeliefs.length > 0) { | |
| // Derive consequent | |
| const consequentId = `belief-${++state.beliefCounter}`; | |
| const consequent = rule.consequent.replace('{subject}', 'it'); | |
| state.beliefs.set(consequentId, { | |
| id: consequentId, | |
| label: consequent, | |
| status: STATUS.DERIVED, | |
| justification: `From: ${rule.antecedent}`, | |
| timestamp: Date.now(), | |
| dependents: [], | |
| }); | |
| state.derivations.push({ | |
| step: state.derivations.length + 1, | |
| antecedent: antecedentBeliefs[0].label, | |
| rule: rule.antecedent + ' → ' + rule.consequent, | |
| consequent, | |
| timestamp: Date.now(), | |
| }); | |
| logExecution('derive', consequent); | |
| } | |
| }); | |
| } | |
| function checkContradictions() { | |
| const active = Array.from(state.beliefs.values()) | |
| .filter(b => b.status !== STATUS.RETRACTED); | |
| state.contradictions = []; | |
| for (let i = 0; i < active.length; i++) { | |
| for (let j = i + 1; j < active.length; j++) { | |
| const b1 = active[i]; | |
| const b2 = active[j]; | |
| // Simple contradiction: "X is Y" vs "X is not Y" | |
| const negPattern1 = `${b1.label.split(' is ')[0]} is not ${b1.label.split(' is ')[1]}`; | |
| const negPattern2 = `${b2.label.split(' is ')[0]} is not ${b2.label.split(' is ')[1]}`; | |
| if (b1.label === negPattern2 || b2.label === negPattern1) { | |
| state.contradictions.push({ belief1: b1, belief2: b2 }); | |
| } | |
| } | |
| } | |
| } | |
| function retractBelief(beliefId) { | |
| const belief = state.beliefs.get(beliefId); | |
| if (!belief) return; | |
| // Mark as retracted | |
| belief.status = STATUS.RETRACTED; | |
| // Cascade: retract dependent beliefs | |
| state.derivations.forEach(d => { | |
| if (d.antecedent === belief.label) { | |
| const dependentBeliefs = Array.from(state.beliefs.values()) | |
| .filter(b => b.label === d.consequent); | |
| dependentBeliefs.forEach(b => { | |
| b.status = STATUS.RETRACTED; | |
| logExecution('cascade_retract', b.label); | |
| }); | |
| } | |
| }); | |
| logExecution('retract', belief.label); | |
| render(); | |
| } | |
| // ======================================================================== | |
| // Demos | |
| // ======================================================================== | |
| function socraticDemo() { | |
| clearAll(); | |
| setTimeout(() => { | |
| document.getElementById('belief-input').value = 'Socrates is human'; | |
| assertBelief(); | |
| }, 200); | |
| setTimeout(() => { | |
| document.getElementById('rule-input').value = 'If Socrates is human then Socrates is mortal'; | |
| addRule(); | |
| }, 600); | |
| setTimeout(() => { | |
| document.getElementById('query-input').value = 'mortal'; | |
| queryBelief(); | |
| }, 1200); | |
| setTimeout(() => { | |
| logExecution('demo_info', 'Now retracting: "Socrates is human"'); | |
| }, 1600); | |
| setTimeout(() => { | |
| const beliefId = Array.from(state.beliefs.entries()) | |
| .find(([_, b]) => b.label === 'Socrates is human')?.[0]; | |
| if (beliefId) retractBelief(beliefId); | |
| }, 2200); | |
| } | |
| function contradictionDemo() { | |
| clearAll(); | |
| setTimeout(() => { | |
| document.getElementById('belief-input').value = 'The light is on'; | |
| assertBelief(); | |
| }, 200); | |
| setTimeout(() => { | |
| document.getElementById('belief-input').value = 'The light is not on'; | |
| assertBelief(); | |
| }, 600); | |
| } | |
| function clearAll() { | |
| state.beliefs.clear(); | |
| state.rules = []; | |
| state.derivations = []; | |
| state.contradictions = []; | |
| state.executionLog = []; | |
| state.beliefCounter = 0; | |
| logExecution('system', 'Cleared all state'); | |
| render(); | |
| } | |
| // ======================================================================== | |
| // Logging | |
| // ======================================================================== | |
| function logExecution(action, detail) { | |
| state.executionLog.push({ | |
| timestamp: new Date().toLocaleTimeString(), | |
| action, | |
| detail, | |
| }); | |
| } | |
| // ======================================================================== | |
| // Rendering | |
| // ======================================================================== | |
| function render() { | |
| renderBeliefs(); | |
| renderDerivations(); | |
| renderExecutionLog(); | |
| renderContradictions(); | |
| } | |
| function renderBeliefs() { | |
| const list = document.getElementById('belief-list'); | |
| const beliefs = Array.from(state.beliefs.values()); | |
| const count = beliefs.filter(b => b.status !== STATUS.RETRACTED).length; | |
| document.getElementById('belief-count').textContent = `${count} active`; | |
| if (beliefs.length === 0) { | |
| list.innerHTML = '<div style="color: var(--text-muted); font-size: 0.75rem;">No beliefs yet.</div>'; | |
| return; | |
| } | |
| list.innerHTML = beliefs | |
| .map(b => ` | |
| <div class="belief-node" data-status="${b.status}"> | |
| <div class="belief-label">${b.label}</div> | |
| <div class="belief-meta"> | |
| <span>${b.status}</span> | |
| <span>${b.justification}</span> | |
| <span class="belief-action" onclick="retractBelief('${b.id}')">retract</span> | |
| </div> | |
| </div> | |
| `) | |
| .join(''); | |
| } | |
| function renderDerivations() { | |
| const chain = document.getElementById('derivation-chain'); | |
| const derivations = state.derivations; | |
| document.getElementById('step-count').textContent = `${derivations.length} steps`; | |
| if (derivations.length === 0) { | |
| chain.innerHTML = '<div style="color: var(--text-muted); font-size: 0.75rem;">Waiting for inferences...</div>'; | |
| return; | |
| } | |
| chain.innerHTML = derivations | |
| .map((d, i) => ` | |
| <div class="derivation-step"> | |
| <div class="step-label">Step ${d.step}:</div> | |
| <div class="step-content"> | |
| <strong>${d.antecedent}</strong><br> | |
| + ${d.rule}<br> | |
| ⇒ <strong>${d.consequent}</strong> | |
| </div> | |
| </div> | |
| `) | |
| .join(''); | |
| } | |
| function renderContradictions() { | |
| const alerts = document.getElementById('contradiction-alerts'); | |
| if (state.contradictions.length === 0) { | |
| alerts.innerHTML = ''; | |
| return; | |
| } | |
| alerts.innerHTML = state.contradictions | |
| .map(c => ` | |
| <div class="contradiction-alert"> | |
| ⚠️ Contradiction: "${c.belief1.label}" vs "${c.belief2.label}" | |
| </div> | |
| `) | |
| .join(''); | |
| } | |
| function renderExecutionLog() { | |
| const trace = document.getElementById('execution-trace'); | |
| const logs = state.executionLog.slice(-50); // Last 50 entries | |
| trace.innerHTML = logs | |
| .map(log => ` | |
| <div class="trace-line ${log.action}"> | |
| <strong>${log.timestamp}</strong> • ${log.action}: ${log.detail} | |
| </div> | |
| `) | |
| .join(''); | |
| trace.scrollTop = trace.scrollHeight; | |
| } | |
| // ======================================================================== | |
| // Initialization | |
| // ======================================================================== | |
| window.addEventListener('load', () => { | |
| logExecution('system', 'LTMS console initialized'); | |
| render(); | |
| }); | |
| window.assertBelief = assertBelief; | |
| window.addRule = addRule; | |
| window.queryBelief = queryBelief; | |
| window.semanticSearch = semanticSearch; | |
| window.retractBelief = retractBelief; | |
| window.socraticDemo = socraticDemo; | |
| window.contradictionDemo = contradictionDemo; | |
| window.clearAll = clearAll; | |
| // Initialize ONNX on page load | |
| window.addEventListener('load', async () => { | |
| if (window.ONNXBridge) { | |
| const ready = await window.ONNXBridge.initialize(); | |
| if (ready) { | |
| logExecution('onnx', 'Semantic search ready (ONNX in-browser)'); | |
| } else { | |
| logExecution('onnx', 'Semantic search ready (fallback mode)'); | |
| } | |
| } | |
| }); | |
| </script> | |
| </body> | |
| </html> | |