palimpseste-max / web /src /components /CortexPanel.tsx
thefinalboss's picture
Upload web/src/components/CortexPanel.tsx with huggingface_hub
21bc16e verified
Raw
History Blame Contribute Delete
12.2 kB
import { useState } from 'react'
interface CortexPanelProps {
onLearnText: (text: string) => Promise<any>
onDream: (cycles: number) => Promise<any>
onReason: (question: string) => Promise<any>
loading: boolean
}
type Tab = 'learn' | 'dream' | 'reason'
export function CortexPanel({ onLearnText, onDream, onReason, loading }: CortexPanelProps) {
const [tab, setTab] = useState<Tab>('learn')
const [learnText, setLearnText] = useState('')
const [learnResult, setLearnResult] = useState<any>(null)
const [dreamCycles, setDreamCycles] = useState(3)
const [dreamResult, setDreamResult] = useState<any>(null)
const [reasonQuestion, setReasonQuestion] = useState('')
const [reasonResult, setReasonResult] = useState<any>(null)
const handleLearn = async () => {
if (!learnText.trim() || loading) return
try {
const r = await onLearnText(learnText.trim())
setLearnResult(r)
} catch {
setLearnResult({ error: 'API not reachable' })
}
}
const handleDream = async () => {
if (loading) return
try {
const r = await onDream(dreamCycles)
setDreamResult(r)
} catch {
setDreamResult({ error: 'API not reachable' })
}
}
const handleReason = async () => {
if (!reasonQuestion.trim() || loading) return
try {
const r = await onReason(reasonQuestion.trim())
setReasonResult(r)
} catch {
setReasonResult({ error: 'API not reachable' })
}
}
return (
<div className="flex flex-col h-full glass-card overflow-hidden">
{/* Header */}
<div className="px-5 py-3 border-b border-pal-border">
<div className="flex items-center gap-2">
<span className="text-base">๐Ÿง </span>
<h2 className="text-sm font-semibold text-pal-text">Cortex</h2>
<span className="text-[10px] px-1.5 py-0.5 rounded-full bg-pal-accent/15 text-pal-accent font-semibold">
7 FEATURES
</span>
</div>
</div>
{/* Tabs */}
<div className="flex gap-1 p-2 border-b border-pal-border">
{([
['learn', '๐Ÿ“– Learn', 'from-pal-accent2/20'],
['dream', '๐ŸŒ™ Dream', 'from-pal-accent/20'],
['reason', '๐Ÿ”— Reason', 'from-pal-gold/20'],
] as [Tab, string, string][]).map(([t, label, color]) => (
<button
key={t}
onClick={() => setTab(t)}
className={`flex-1 py-1.5 rounded-lg text-xs font-semibold transition-all ${
tab === t ? `bg-gradient-to-r ${color} text-pal-text` : 'text-pal-muted hover:text-pal-text'
}`}
>
{label}
</button>
))}
</div>
{/* Content */}
<div className="flex-1 overflow-y-auto p-4">
{/* LEARN TAB */}
{tab === 'learn' && (
<div className="space-y-3">
<div>
<label className="text-[11px] text-pal-muted font-medium uppercase tracking-wide block mb-1">
Paste any text โ€” PALIMPSESTE becomes an expert instantly
</label>
<textarea
value={learnText}
onChange={e => setLearnText(e.target.value)}
placeholder="Paste a Wikipedia article, documentation, book chapter..."
rows={6}
className="w-full bg-pal-surface border border-pal-border rounded-lg px-3 py-2 text-sm text-pal-text placeholder:text-pal-muted focus:outline-none focus:border-pal-accent2/50 transition-all resize-none"
/>
</div>
<button
onClick={handleLearn}
disabled={!learnText.trim() || loading}
className="w-full py-2 rounded-lg bg-gradient-to-r from-pal-accent2 to-pal-green text-white font-semibold text-sm transition-all hover:scale-[1.02] disabled:opacity-30 disabled:cursor-not-allowed"
>
{loading ? 'Learning...' : 'โšก Learn Instantly (O(1) per token)'}
</button>
{learnResult && !learnResult.error && (
<div className="p-3 rounded-lg bg-pal-surface/60 border border-pal-accent2/20 animate-slide-up">
<div className="flex items-center gap-3 mb-2">
<div className="text-pal-green text-lg">โœ“</div>
<div>
<div className="text-xs font-semibold text-pal-text">
Learned {learnResult.n_facts} facts in {learnResult.n_seconds}s
</div>
<div className="text-[10px] text-pal-muted">
{learnResult.n_tokens} tokens written ยท tag: {learnResult.document_tag}
</div>
</div>
</div>
{learnResult.facts && learnResult.facts.length > 0 && (
<div className="space-y-1 mt-2 pt-2 border-t border-pal-border/50">
<div className="text-[10px] text-pal-muted uppercase font-medium">Extracted facts:</div>
{learnResult.facts.slice(0, 8).map((f: any, i: number) => (
<div key={i} className="text-[11px] text-pal-text">
<span className="text-pal-accent2">Q:</span> {f.q}
<br />
<span className="text-pal-green">A:</span> {f.a.slice(0, 80)}{f.a.length > 80 ? '...' : ''}
</div>
))}
</div>
)}
</div>
)}
{learnResult?.error && (
<div className="text-xs text-pal-red text-center">{learnResult.error}</div>
)}
</div>
)}
{/* DREAM TAB */}
{tab === 'dream' && (
<div className="space-y-3">
<div className="text-center py-4">
<div className="text-3xl mb-2 animate-float">๐ŸŒ™</div>
<p className="text-xs text-pal-muted max-w-xs mx-auto">
When idle, PALIMPSESTE replays its memory, discovers co-activations,
and creates new abstract concepts. It gets smarter without any new data.
</p>
</div>
<div className="flex items-center gap-3">
<label className="text-xs text-pal-muted font-medium whitespace-nowrap">
Cycles: <span className="text-pal-accent font-mono">{dreamCycles}</span>
</label>
<input
type="range" min="1" max="10" step="1"
value={dreamCycles}
onChange={e => setDreamCycles(parseInt(e.target.value))}
className="flex-1 accent-pal-accent"
/>
</div>
<button
onClick={handleDream}
disabled={loading}
className="w-full py-2 rounded-lg bg-gradient-to-r from-pal-accent to-pal-accent2 text-white font-semibold text-sm transition-all hover:scale-[1.02] hover:glow-accent disabled:opacity-30"
>
{loading ? 'Dreaming...' : '๐ŸŒ™ Trigger Dream'}
</button>
{dreamResult && !dreamResult.error && (
<div className="p-3 rounded-lg bg-pal-surface/60 border border-pal-accent/20 animate-slide-up space-y-2">
<div className="flex items-center gap-3">
<div className="text-pal-accent text-lg">โœ“</div>
<div>
<div className="text-xs font-semibold text-pal-text">
{dreamResult.n_concepts_promoted} promoted, {dreamResult.n_concepts_extracted} extracted
</div>
<div className="text-[10px] text-pal-muted">
{dreamResult.n_seconds}s ยท {dreamResult.n_total_concepts} total concepts
</div>
</div>
</div>
{dreamResult.concept_labels && dreamResult.concept_labels.length > 0 && (
<div className="pt-2 border-t border-pal-border/50">
<div className="text-[10px] text-pal-muted uppercase font-medium mb-1">Concepts:</div>
<div className="flex flex-wrap gap-1">
{dreamResult.concept_labels.map((label: string, i: number) => (
<span key={i} className="text-[10px] px-2 py-0.5 rounded-full bg-pal-accent/10 text-pal-accent">
{label}
</span>
))}
</div>
</div>
)}
</div>
)}
</div>
)}
{/* REASON TAB */}
{tab === 'reason' && (
<div className="space-y-3">
<div>
<label className="text-[11px] text-pal-muted font-medium uppercase tracking-wide block mb-1">
Ask a complex question โ€” PALIMPSESTE decomposes and resolves
</label>
<input
type="text"
value={reasonQuestion}
onChange={e => setReasonQuestion(e.target.value)}
onKeyDown={e => e.key === 'Enter' && handleReason()}
placeholder="what is the capital of the country that won the world cup 2018?"
className="w-full bg-pal-surface border border-pal-border rounded-lg px-3 py-2 text-sm text-pal-text placeholder:text-pal-muted focus:outline-none focus:border-pal-gold/50"
/>
</div>
<button
onClick={handleReason}
disabled={!reasonQuestion.trim() || loading}
className="w-full py-2 rounded-lg bg-gradient-to-r from-pal-gold to-pal-accent text-white font-semibold text-sm transition-all hover:scale-[1.02] disabled:opacity-30"
>
{loading ? 'Reasoning...' : '๐Ÿ”— Decompose & Resolve'}
</button>
{reasonResult && !reasonResult.error && (
<div className="p-3 rounded-lg bg-pal-surface/60 border border-pal-gold/20 animate-slide-up space-y-2">
<div className="flex items-center gap-2">
<div className={`text-lg ${reasonResult.success ? 'text-pal-green' : 'text-pal-red'}`}>
{reasonResult.success ? 'โœ“' : 'โœ—'}
</div>
<div className="text-xs font-semibold text-pal-text">
{reasonResult.n_decompositions} decompositions, {reasonResult.n_hops} hops, {reasonResult.n_seconds}s
</div>
</div>
<div className="p-2 rounded-lg bg-pal-bg/50 border border-pal-border/30">
<div className="text-[10px] text-pal-muted uppercase font-medium mb-0.5">Answer:</div>
<div className="text-sm text-pal-gold font-medium">{reasonResult.answer}</div>
</div>
{reasonResult.steps && reasonResult.steps.length > 0 && (
<div className="pt-2 border-t border-pal-border/50 space-y-1">
<div className="text-[10px] text-pal-muted uppercase font-medium">Reasoning trace:</div>
{reasonResult.steps.filter((s: any) => s.answer).map((s: any, i: number) => (
<div key={i} className="text-[11px] flex items-start gap-2">
<span className={`px-1 rounded font-mono ${
s.type === 'resolve' ? 'bg-pal-green/10 text-pal-green' :
s.type === 'compose' ? 'bg-pal-gold/10 text-pal-gold' :
'bg-pal-muted/10 text-pal-muted'
}`}>
{s.type}
</span>
<div>
<span className="text-pal-muted">{s.question?.slice(0, 40)}</span>
{s.answer && <span className="text-pal-text"> โ†’ <b>{s.answer.slice(0, 50)}</b></span>}
</div>
</div>
))}
</div>
)}
</div>
)}
</div>
)}
</div>
</div>
)
}