shieldcortex 2.1.1 → 2.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/hooks/clawdbot/cortex-memory/HOOK.md +2 -2
- package/package.json +13 -9
- package/dashboard/components.json +0 -22
- package/dashboard/eslint.config.mjs +0 -42
- package/dashboard/next.config.ts +0 -7
- package/dashboard/package-lock.json +0 -8053
- package/dashboard/package.json +0 -44
- package/dashboard/postcss.config.mjs +0 -7
- package/dashboard/public/file.svg +0 -1
- package/dashboard/public/globe.svg +0 -1
- package/dashboard/public/next.svg +0 -1
- package/dashboard/public/vercel.svg +0 -1
- package/dashboard/public/window.svg +0 -1
- package/dashboard/scripts/ensure-api.mjs +0 -76
- package/dashboard/src/app/error.tsx +0 -49
- package/dashboard/src/app/favicon.ico +0 -0
- package/dashboard/src/app/globals.css +0 -130
- package/dashboard/src/app/layout.tsx +0 -35
- package/dashboard/src/app/page.tsx +0 -364
- package/dashboard/src/components/Providers.tsx +0 -27
- package/dashboard/src/components/brain/ActivityPulseSystem.tsx +0 -229
- package/dashboard/src/components/brain/BrainMesh.tsx +0 -133
- package/dashboard/src/components/brain/BrainRegions.tsx +0 -254
- package/dashboard/src/components/brain/BrainScene.tsx +0 -255
- package/dashboard/src/components/brain/CategoryLabels.tsx +0 -103
- package/dashboard/src/components/brain/CoreSphere.tsx +0 -215
- package/dashboard/src/components/brain/DataFlowParticles.tsx +0 -123
- package/dashboard/src/components/brain/DataStreamRings.tsx +0 -161
- package/dashboard/src/components/brain/ElectronFlow.tsx +0 -323
- package/dashboard/src/components/brain/HolographicGrid.tsx +0 -235
- package/dashboard/src/components/brain/MemoryLinks.tsx +0 -271
- package/dashboard/src/components/brain/MemoryNode.tsx +0 -245
- package/dashboard/src/components/brain/NeuralPathways.tsx +0 -441
- package/dashboard/src/components/brain/SynapseNodes.tsx +0 -312
- package/dashboard/src/components/brain/TimelineControls.tsx +0 -205
- package/dashboard/src/components/chip/ChipScene.tsx +0 -497
- package/dashboard/src/components/chip/ChipSubstrate.tsx +0 -238
- package/dashboard/src/components/chip/CortexCore.tsx +0 -210
- package/dashboard/src/components/chip/DataBus.tsx +0 -416
- package/dashboard/src/components/chip/MemoryCell.tsx +0 -225
- package/dashboard/src/components/chip/MemoryGrid.tsx +0 -328
- package/dashboard/src/components/chip/QuantumCell.tsx +0 -316
- package/dashboard/src/components/chip/SectionLabel.tsx +0 -113
- package/dashboard/src/components/chip/index.ts +0 -14
- package/dashboard/src/components/controls/ControlPanel.tsx +0 -106
- package/dashboard/src/components/controls/VersionPanel.tsx +0 -185
- package/dashboard/src/components/dashboard/StatsPanel.tsx +0 -164
- package/dashboard/src/components/debug/ActivityLog.tsx +0 -250
- package/dashboard/src/components/debug/DebugPanel.tsx +0 -101
- package/dashboard/src/components/debug/QueryTester.tsx +0 -192
- package/dashboard/src/components/debug/RelationshipGraph.tsx +0 -403
- package/dashboard/src/components/debug/SqlConsole.tsx +0 -319
- package/dashboard/src/components/graph/KnowledgeGraph.tsx +0 -230
- package/dashboard/src/components/graph/OntologyGraph.tsx +0 -631
- package/dashboard/src/components/insights/ActivityHeatmap.tsx +0 -131
- package/dashboard/src/components/insights/InsightsView.tsx +0 -46
- package/dashboard/src/components/insights/KnowledgeMapPanel.tsx +0 -80
- package/dashboard/src/components/insights/QualityPanel.tsx +0 -116
- package/dashboard/src/components/memories/MemoriesView.tsx +0 -150
- package/dashboard/src/components/memories/MemoryCard.tsx +0 -103
- package/dashboard/src/components/memory/MemoryDetail.tsx +0 -325
- package/dashboard/src/components/nav/NavRail.tsx +0 -54
- package/dashboard/src/components/ui/button.tsx +0 -62
- package/dashboard/src/components/ui/card.tsx +0 -92
- package/dashboard/src/components/ui/input.tsx +0 -21
- package/dashboard/src/hooks/useDebouncedValue.ts +0 -24
- package/dashboard/src/hooks/useMemories.ts +0 -458
- package/dashboard/src/hooks/useSuggestions.ts +0 -46
- package/dashboard/src/lib/category-colors.ts +0 -84
- package/dashboard/src/lib/position-algorithm.ts +0 -177
- package/dashboard/src/lib/simplex-noise.ts +0 -217
- package/dashboard/src/lib/store.ts +0 -88
- package/dashboard/src/lib/utils.ts +0 -6
- package/dashboard/src/lib/websocket.ts +0 -249
- package/dashboard/src/types/memory.ts +0 -73
- package/dashboard/tsconfig.json +0 -34
|
@@ -1,250 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Activity Log Component
|
|
5
|
-
*
|
|
6
|
-
* Real-time event stream showing memory operations.
|
|
7
|
-
* Uses WebSocket for live updates.
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
import { useState, useEffect, useRef } from 'react';
|
|
11
|
-
import { useMemoryWebSocket, MemoryEventType } from '@/lib/websocket';
|
|
12
|
-
|
|
13
|
-
interface LogEntry {
|
|
14
|
-
id: number;
|
|
15
|
-
timestamp: Date;
|
|
16
|
-
type: MemoryEventType;
|
|
17
|
-
message: string;
|
|
18
|
-
details?: Record<string, unknown>;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
const EVENT_COLORS: Record<MemoryEventType, string> = {
|
|
22
|
-
memory_created: 'text-green-400',
|
|
23
|
-
memory_accessed: 'text-blue-400',
|
|
24
|
-
memory_updated: 'text-yellow-400',
|
|
25
|
-
memory_deleted: 'text-red-400',
|
|
26
|
-
consolidation_complete: 'text-purple-400',
|
|
27
|
-
decay_tick: 'text-slate-500',
|
|
28
|
-
initial_state: 'text-slate-400',
|
|
29
|
-
worker_light_tick: 'text-slate-500',
|
|
30
|
-
worker_medium_tick: 'text-slate-500',
|
|
31
|
-
link_discovered: 'text-cyan-400',
|
|
32
|
-
predictive_consolidation: 'text-purple-400',
|
|
33
|
-
update_started: 'text-blue-400',
|
|
34
|
-
update_complete: 'text-green-400',
|
|
35
|
-
update_failed: 'text-red-400',
|
|
36
|
-
server_restarting: 'text-orange-400',
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
const EVENT_ICONS: Record<MemoryEventType, string> = {
|
|
40
|
-
memory_created: '+',
|
|
41
|
-
memory_accessed: '👁',
|
|
42
|
-
memory_updated: '✏',
|
|
43
|
-
memory_deleted: '✕',
|
|
44
|
-
consolidation_complete: '🔄',
|
|
45
|
-
decay_tick: '⏱',
|
|
46
|
-
initial_state: '📋',
|
|
47
|
-
worker_light_tick: '⚡',
|
|
48
|
-
worker_medium_tick: '🔋',
|
|
49
|
-
link_discovered: '🔗',
|
|
50
|
-
predictive_consolidation: '🔮',
|
|
51
|
-
update_started: '⬆',
|
|
52
|
-
update_complete: '✓',
|
|
53
|
-
update_failed: '✗',
|
|
54
|
-
server_restarting: '🔄',
|
|
55
|
-
};
|
|
56
|
-
|
|
57
|
-
export function ActivityLog() {
|
|
58
|
-
const [logs, setLogs] = useState<LogEntry[]>([]);
|
|
59
|
-
const [autoScroll, setAutoScroll] = useState(true);
|
|
60
|
-
const [filters, setFilters] = useState<Record<MemoryEventType, boolean>>({
|
|
61
|
-
memory_created: true,
|
|
62
|
-
memory_accessed: true,
|
|
63
|
-
memory_updated: true,
|
|
64
|
-
memory_deleted: true,
|
|
65
|
-
consolidation_complete: true,
|
|
66
|
-
decay_tick: false, // Off by default (noisy)
|
|
67
|
-
initial_state: false,
|
|
68
|
-
worker_light_tick: false,
|
|
69
|
-
worker_medium_tick: false,
|
|
70
|
-
link_discovered: true,
|
|
71
|
-
predictive_consolidation: true,
|
|
72
|
-
update_started: true,
|
|
73
|
-
update_complete: true,
|
|
74
|
-
update_failed: true,
|
|
75
|
-
server_restarting: true,
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
const logContainerRef = useRef<HTMLDivElement>(null);
|
|
79
|
-
const nextIdRef = useRef(1);
|
|
80
|
-
|
|
81
|
-
// Connect to WebSocket
|
|
82
|
-
const { lastEvent, isConnected } = useMemoryWebSocket();
|
|
83
|
-
|
|
84
|
-
// Process incoming events
|
|
85
|
-
useEffect(() => {
|
|
86
|
-
if (!lastEvent) return;
|
|
87
|
-
|
|
88
|
-
const entry: LogEntry = {
|
|
89
|
-
id: nextIdRef.current++,
|
|
90
|
-
timestamp: new Date(lastEvent.timestamp),
|
|
91
|
-
type: lastEvent.type,
|
|
92
|
-
message: formatEventMessage(lastEvent),
|
|
93
|
-
details: lastEvent.data as Record<string, unknown>,
|
|
94
|
-
};
|
|
95
|
-
|
|
96
|
-
setLogs((prev) => {
|
|
97
|
-
const updated = [...prev, entry];
|
|
98
|
-
// Keep only last 500 entries
|
|
99
|
-
return updated.slice(-500);
|
|
100
|
-
});
|
|
101
|
-
}, [lastEvent]);
|
|
102
|
-
|
|
103
|
-
// Auto-scroll to bottom
|
|
104
|
-
useEffect(() => {
|
|
105
|
-
if (autoScroll && logContainerRef.current) {
|
|
106
|
-
logContainerRef.current.scrollTop = logContainerRef.current.scrollHeight;
|
|
107
|
-
}
|
|
108
|
-
}, [logs, autoScroll]);
|
|
109
|
-
|
|
110
|
-
const filteredLogs = logs.filter((log) => filters[log.type]);
|
|
111
|
-
|
|
112
|
-
const toggleFilter = (type: MemoryEventType) => {
|
|
113
|
-
setFilters((prev) => ({ ...prev, [type]: !prev[type] }));
|
|
114
|
-
};
|
|
115
|
-
|
|
116
|
-
const clearLogs = () => {
|
|
117
|
-
setLogs([]);
|
|
118
|
-
};
|
|
119
|
-
|
|
120
|
-
const exportLogs = () => {
|
|
121
|
-
const json = JSON.stringify(filteredLogs, null, 2);
|
|
122
|
-
const blob = new Blob([json], { type: 'application/json' });
|
|
123
|
-
const url = URL.createObjectURL(blob);
|
|
124
|
-
const a = document.createElement('a');
|
|
125
|
-
a.href = url;
|
|
126
|
-
a.download = `cortex-activity-${new Date().toISOString().split('T')[0]}.json`;
|
|
127
|
-
a.click();
|
|
128
|
-
URL.revokeObjectURL(url);
|
|
129
|
-
};
|
|
130
|
-
|
|
131
|
-
return (
|
|
132
|
-
<div className="h-full flex flex-col">
|
|
133
|
-
{/* Controls */}
|
|
134
|
-
<div className="p-3 border-b border-slate-700 flex items-center gap-3 flex-wrap">
|
|
135
|
-
{/* Connection Status */}
|
|
136
|
-
<div className="flex items-center gap-2 text-xs">
|
|
137
|
-
<span
|
|
138
|
-
className={`w-2 h-2 rounded-full ${isConnected ? 'bg-green-500' : 'bg-red-500'}`}
|
|
139
|
-
/>
|
|
140
|
-
<span className="text-slate-400">
|
|
141
|
-
{isConnected ? 'Connected' : 'Disconnected'}
|
|
142
|
-
</span>
|
|
143
|
-
</div>
|
|
144
|
-
|
|
145
|
-
<div className="w-px h-4 bg-slate-700" />
|
|
146
|
-
|
|
147
|
-
{/* Filters */}
|
|
148
|
-
<div className="flex items-center gap-1 flex-wrap">
|
|
149
|
-
{(Object.keys(filters) as MemoryEventType[]).map((type) => (
|
|
150
|
-
<button
|
|
151
|
-
key={type}
|
|
152
|
-
onClick={() => toggleFilter(type)}
|
|
153
|
-
className={`px-2 py-0.5 text-xs rounded transition-colors ${
|
|
154
|
-
filters[type]
|
|
155
|
-
? `${EVENT_COLORS[type]} bg-slate-700`
|
|
156
|
-
: 'text-slate-600 bg-slate-800'
|
|
157
|
-
}`}
|
|
158
|
-
>
|
|
159
|
-
{type.replace(/_/g, ' ').replace('memory ', '')}
|
|
160
|
-
</button>
|
|
161
|
-
))}
|
|
162
|
-
</div>
|
|
163
|
-
|
|
164
|
-
<div className="flex-1" />
|
|
165
|
-
|
|
166
|
-
{/* Actions */}
|
|
167
|
-
<label className="flex items-center gap-1 text-xs text-slate-400 cursor-pointer">
|
|
168
|
-
<input
|
|
169
|
-
type="checkbox"
|
|
170
|
-
checked={autoScroll}
|
|
171
|
-
onChange={(e) => setAutoScroll(e.target.checked)}
|
|
172
|
-
className="rounded border-slate-600 bg-slate-800"
|
|
173
|
-
/>
|
|
174
|
-
Auto-scroll
|
|
175
|
-
</label>
|
|
176
|
-
|
|
177
|
-
<button
|
|
178
|
-
onClick={exportLogs}
|
|
179
|
-
className="text-xs text-slate-400 hover:text-white"
|
|
180
|
-
>
|
|
181
|
-
Export
|
|
182
|
-
</button>
|
|
183
|
-
|
|
184
|
-
<button
|
|
185
|
-
onClick={clearLogs}
|
|
186
|
-
className="text-xs text-red-400 hover:text-red-300"
|
|
187
|
-
>
|
|
188
|
-
Clear
|
|
189
|
-
</button>
|
|
190
|
-
</div>
|
|
191
|
-
|
|
192
|
-
{/* Log Entries */}
|
|
193
|
-
<div
|
|
194
|
-
ref={logContainerRef}
|
|
195
|
-
className="flex-1 overflow-auto p-3 font-mono text-xs"
|
|
196
|
-
>
|
|
197
|
-
{filteredLogs.length === 0 ? (
|
|
198
|
-
<div className="text-slate-500 text-center py-8">
|
|
199
|
-
{isConnected ? 'Waiting for events...' : 'Not connected to server'}
|
|
200
|
-
</div>
|
|
201
|
-
) : (
|
|
202
|
-
<div className="space-y-1">
|
|
203
|
-
{filteredLogs.map((log) => (
|
|
204
|
-
<div key={log.id} className="flex gap-2 hover:bg-slate-800/50 px-1 rounded">
|
|
205
|
-
<span className="text-slate-500 shrink-0">
|
|
206
|
-
{log.timestamp.toLocaleTimeString()}
|
|
207
|
-
</span>
|
|
208
|
-
<span className={`shrink-0 w-4 ${EVENT_COLORS[log.type]}`}>
|
|
209
|
-
{EVENT_ICONS[log.type]}
|
|
210
|
-
</span>
|
|
211
|
-
<span className={EVENT_COLORS[log.type]}>{log.message}</span>
|
|
212
|
-
</div>
|
|
213
|
-
))}
|
|
214
|
-
</div>
|
|
215
|
-
)}
|
|
216
|
-
</div>
|
|
217
|
-
</div>
|
|
218
|
-
);
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
function formatEventMessage(event: { type: MemoryEventType; data?: unknown }): string {
|
|
222
|
-
const data = event.data as Record<string, unknown> | undefined;
|
|
223
|
-
|
|
224
|
-
switch (event.type) {
|
|
225
|
-
case 'memory_created':
|
|
226
|
-
return `Created: "${data?.title || 'Unknown'}" (${data?.type || 'unknown'})`;
|
|
227
|
-
case 'memory_accessed':
|
|
228
|
-
return `Accessed: "${data?.title || 'Unknown'}" → salience ${((data?.newSalience as number) * 100).toFixed(0)}%`;
|
|
229
|
-
case 'memory_updated':
|
|
230
|
-
return `Updated: "${data?.title || 'Unknown'}"`;
|
|
231
|
-
case 'memory_deleted':
|
|
232
|
-
return `Deleted: "${data?.title || 'Unknown'}" (ID: ${data?.memoryId})`;
|
|
233
|
-
case 'consolidation_complete':
|
|
234
|
-
return `Consolidation: ${data?.consolidated || 0} promoted, ${data?.decayed || 0} decayed, ${data?.deleted || 0} deleted`;
|
|
235
|
-
case 'decay_tick':
|
|
236
|
-
return `Decay tick: ${(data?.updates as unknown[])?.length || 0} memories updated`;
|
|
237
|
-
case 'initial_state':
|
|
238
|
-
return 'Connected - received initial state';
|
|
239
|
-
case 'worker_light_tick':
|
|
240
|
-
return 'Worker light tick completed';
|
|
241
|
-
case 'worker_medium_tick':
|
|
242
|
-
return 'Worker medium tick completed';
|
|
243
|
-
case 'link_discovered':
|
|
244
|
-
return `Link discovered: ${data?.sourceTitle || '?'} → ${data?.targetTitle || '?'}`;
|
|
245
|
-
case 'predictive_consolidation':
|
|
246
|
-
return `Predictive consolidation: ${data?.promoted || 0} promoted`;
|
|
247
|
-
default:
|
|
248
|
-
return `Event: ${event.type}`;
|
|
249
|
-
}
|
|
250
|
-
}
|
|
@@ -1,101 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Debug Panel Component
|
|
5
|
-
*
|
|
6
|
-
* Collapsible bottom panel with tabbed interface for debug tools:
|
|
7
|
-
* - Memory Detail (enhanced)
|
|
8
|
-
* - Query Tester
|
|
9
|
-
* - Activity Log
|
|
10
|
-
* - Relationship Graph
|
|
11
|
-
* - SQL Console
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
import { useState } from 'react';
|
|
15
|
-
import { QueryTester } from './QueryTester';
|
|
16
|
-
import { ActivityLog } from './ActivityLog';
|
|
17
|
-
import { RelationshipGraph } from './RelationshipGraph';
|
|
18
|
-
import { SqlConsole } from './SqlConsole';
|
|
19
|
-
|
|
20
|
-
type TabId = 'detail' | 'query' | 'activity' | 'graph' | 'sql';
|
|
21
|
-
|
|
22
|
-
interface Tab {
|
|
23
|
-
id: TabId;
|
|
24
|
-
label: string;
|
|
25
|
-
icon: string;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
const TABS: Tab[] = [
|
|
29
|
-
{ id: 'query', label: 'Query', icon: '🔍' },
|
|
30
|
-
{ id: 'activity', label: 'Activity', icon: '📋' },
|
|
31
|
-
{ id: 'graph', label: 'Graph', icon: '🕸' },
|
|
32
|
-
{ id: 'sql', label: 'SQL', icon: '💾' },
|
|
33
|
-
];
|
|
34
|
-
|
|
35
|
-
interface DebugPanelProps {
|
|
36
|
-
onCollapse?: () => void;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
export function DebugPanel({ onCollapse }: DebugPanelProps) {
|
|
40
|
-
const [activeTab, setActiveTab] = useState<TabId>('query');
|
|
41
|
-
const [isCollapsed, setIsCollapsed] = useState(false);
|
|
42
|
-
|
|
43
|
-
const handleCollapse = () => {
|
|
44
|
-
setIsCollapsed(!isCollapsed);
|
|
45
|
-
onCollapse?.();
|
|
46
|
-
};
|
|
47
|
-
|
|
48
|
-
if (isCollapsed) {
|
|
49
|
-
return (
|
|
50
|
-
<div className="h-10 border-t border-slate-700 bg-slate-900/80 flex items-center px-4">
|
|
51
|
-
<button
|
|
52
|
-
onClick={handleCollapse}
|
|
53
|
-
className="flex items-center gap-2 text-sm text-slate-400 hover:text-white"
|
|
54
|
-
>
|
|
55
|
-
<span>▲</span>
|
|
56
|
-
<span>Debug Panel</span>
|
|
57
|
-
</button>
|
|
58
|
-
</div>
|
|
59
|
-
);
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
return (
|
|
63
|
-
<div className="h-96 border-t border-slate-700 bg-slate-900/80 flex flex-col">
|
|
64
|
-
{/* Tab Bar */}
|
|
65
|
-
<div className="flex items-center border-b border-slate-700 px-2">
|
|
66
|
-
{TABS.map((tab) => (
|
|
67
|
-
<button
|
|
68
|
-
key={tab.id}
|
|
69
|
-
onClick={() => setActiveTab(tab.id)}
|
|
70
|
-
className={`px-4 py-2 text-sm flex items-center gap-1.5 border-b-2 transition-colors ${
|
|
71
|
-
activeTab === tab.id
|
|
72
|
-
? 'text-white border-blue-500'
|
|
73
|
-
: 'text-slate-400 border-transparent hover:text-white hover:border-slate-600'
|
|
74
|
-
}`}
|
|
75
|
-
>
|
|
76
|
-
<span>{tab.icon}</span>
|
|
77
|
-
<span>{tab.label}</span>
|
|
78
|
-
</button>
|
|
79
|
-
))}
|
|
80
|
-
|
|
81
|
-
<div className="flex-1" />
|
|
82
|
-
|
|
83
|
-
<button
|
|
84
|
-
onClick={handleCollapse}
|
|
85
|
-
className="p-2 text-slate-400 hover:text-white"
|
|
86
|
-
title="Collapse panel"
|
|
87
|
-
>
|
|
88
|
-
▼
|
|
89
|
-
</button>
|
|
90
|
-
</div>
|
|
91
|
-
|
|
92
|
-
{/* Tab Content */}
|
|
93
|
-
<div className="flex-1 overflow-hidden min-h-0">
|
|
94
|
-
{activeTab === 'query' && <QueryTester />}
|
|
95
|
-
{activeTab === 'activity' && <ActivityLog />}
|
|
96
|
-
{activeTab === 'graph' && <RelationshipGraph />}
|
|
97
|
-
{activeTab === 'sql' && <SqlConsole />}
|
|
98
|
-
</div>
|
|
99
|
-
</div>
|
|
100
|
-
);
|
|
101
|
-
}
|
|
@@ -1,192 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Query Tester Component
|
|
5
|
-
*
|
|
6
|
-
* Allows testing search queries against the memory system
|
|
7
|
-
* with detailed score breakdowns and explanations.
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
import { useState } from 'react';
|
|
11
|
-
import { Button } from '@/components/ui/button';
|
|
12
|
-
import { Input } from '@/components/ui/input';
|
|
13
|
-
import { Memory } from '@/types/memory';
|
|
14
|
-
|
|
15
|
-
const API_BASE = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:3001';
|
|
16
|
-
|
|
17
|
-
interface SearchResult {
|
|
18
|
-
memory: Memory & { decayedScore: number };
|
|
19
|
-
relevanceScore: number;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
type SearchMode = 'hybrid' | 'fts' | 'vector';
|
|
23
|
-
|
|
24
|
-
export function QueryTester() {
|
|
25
|
-
const [query, setQuery] = useState('');
|
|
26
|
-
const [mode, setMode] = useState<SearchMode>('hybrid');
|
|
27
|
-
const [results, setResults] = useState<SearchResult[]>([]);
|
|
28
|
-
const [isSearching, setIsSearching] = useState(false);
|
|
29
|
-
const [error, setError] = useState<string | null>(null);
|
|
30
|
-
|
|
31
|
-
const handleSearch = async () => {
|
|
32
|
-
if (!query.trim()) return;
|
|
33
|
-
|
|
34
|
-
setIsSearching(true);
|
|
35
|
-
setError(null);
|
|
36
|
-
|
|
37
|
-
try {
|
|
38
|
-
const params = new URLSearchParams({
|
|
39
|
-
query: query.trim(),
|
|
40
|
-
mode: 'search',
|
|
41
|
-
limit: '20',
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
const response = await fetch(`${API_BASE}/api/memories?${params}`);
|
|
45
|
-
if (!response.ok) {
|
|
46
|
-
throw new Error(`Search failed: ${response.statusText}`);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
const data = await response.json();
|
|
50
|
-
// Transform API response to include relevance score
|
|
51
|
-
const resultsWithScore: SearchResult[] = data.memories.map((m: Memory & { decayedScore: number }) => ({
|
|
52
|
-
memory: m,
|
|
53
|
-
relevanceScore: m.decayedScore, // Using decayedScore as proxy for relevance
|
|
54
|
-
}));
|
|
55
|
-
|
|
56
|
-
setResults(resultsWithScore);
|
|
57
|
-
} catch (err) {
|
|
58
|
-
setError((err as Error).message);
|
|
59
|
-
setResults([]);
|
|
60
|
-
} finally {
|
|
61
|
-
setIsSearching(false);
|
|
62
|
-
}
|
|
63
|
-
};
|
|
64
|
-
|
|
65
|
-
const handleKeyDown = (e: React.KeyboardEvent) => {
|
|
66
|
-
if (e.key === 'Enter') {
|
|
67
|
-
handleSearch();
|
|
68
|
-
}
|
|
69
|
-
};
|
|
70
|
-
|
|
71
|
-
return (
|
|
72
|
-
<div className="h-full flex flex-col">
|
|
73
|
-
{/* Search Controls */}
|
|
74
|
-
<div className="p-3 border-b border-slate-700 flex gap-2 items-center">
|
|
75
|
-
<Input
|
|
76
|
-
type="text"
|
|
77
|
-
placeholder="Enter search query..."
|
|
78
|
-
value={query}
|
|
79
|
-
onChange={(e) => setQuery(e.target.value)}
|
|
80
|
-
onKeyDown={handleKeyDown}
|
|
81
|
-
className="flex-1 bg-slate-800 border-slate-600 text-white"
|
|
82
|
-
/>
|
|
83
|
-
|
|
84
|
-
{/* Mode Toggle */}
|
|
85
|
-
<div className="flex gap-1 bg-slate-800 rounded-lg p-1">
|
|
86
|
-
{(['hybrid', 'fts', 'vector'] as SearchMode[]).map((m) => (
|
|
87
|
-
<button
|
|
88
|
-
key={m}
|
|
89
|
-
onClick={() => setMode(m)}
|
|
90
|
-
className={`px-2 py-1 text-xs rounded transition-colors ${
|
|
91
|
-
mode === m
|
|
92
|
-
? 'bg-blue-600 text-white'
|
|
93
|
-
: 'text-slate-400 hover:text-white'
|
|
94
|
-
}`}
|
|
95
|
-
>
|
|
96
|
-
{m.toUpperCase()}
|
|
97
|
-
</button>
|
|
98
|
-
))}
|
|
99
|
-
</div>
|
|
100
|
-
|
|
101
|
-
<Button
|
|
102
|
-
onClick={handleSearch}
|
|
103
|
-
disabled={isSearching || !query.trim()}
|
|
104
|
-
size="sm"
|
|
105
|
-
className="bg-blue-600 hover:bg-blue-700"
|
|
106
|
-
>
|
|
107
|
-
{isSearching ? 'Searching...' : 'Search'}
|
|
108
|
-
</Button>
|
|
109
|
-
</div>
|
|
110
|
-
|
|
111
|
-
{/* Results */}
|
|
112
|
-
<div className="flex-1 overflow-auto p-3">
|
|
113
|
-
{error && (
|
|
114
|
-
<div className="p-3 rounded-lg bg-red-500/20 border border-red-500/50 text-red-300 text-sm mb-3">
|
|
115
|
-
{error}
|
|
116
|
-
</div>
|
|
117
|
-
)}
|
|
118
|
-
|
|
119
|
-
{results.length === 0 && !error && (
|
|
120
|
-
<div className="text-slate-500 text-sm text-center py-8">
|
|
121
|
-
{query ? 'No results found' : 'Enter a query to search memories'}
|
|
122
|
-
</div>
|
|
123
|
-
)}
|
|
124
|
-
|
|
125
|
-
{results.length > 0 && (
|
|
126
|
-
<div className="space-y-2">
|
|
127
|
-
<div className="text-xs text-slate-400 mb-2">
|
|
128
|
-
Found {results.length} results
|
|
129
|
-
</div>
|
|
130
|
-
|
|
131
|
-
<table className="w-full text-sm">
|
|
132
|
-
<thead>
|
|
133
|
-
<tr className="text-left text-slate-400 border-b border-slate-700">
|
|
134
|
-
<th className="pb-2 pr-4">Title</th>
|
|
135
|
-
<th className="pb-2 pr-4 w-20">Score</th>
|
|
136
|
-
<th className="pb-2 pr-4 w-24">Type</th>
|
|
137
|
-
<th className="pb-2 w-24">Category</th>
|
|
138
|
-
</tr>
|
|
139
|
-
</thead>
|
|
140
|
-
<tbody>
|
|
141
|
-
{results.map((result, index) => (
|
|
142
|
-
<tr
|
|
143
|
-
key={result.memory.id}
|
|
144
|
-
className="border-b border-slate-800 hover:bg-slate-800/50"
|
|
145
|
-
>
|
|
146
|
-
<td className="py-2 pr-4">
|
|
147
|
-
<div className="flex items-center gap-2">
|
|
148
|
-
<span className="text-slate-500 text-xs w-5">
|
|
149
|
-
{index + 1}.
|
|
150
|
-
</span>
|
|
151
|
-
<span className="text-white truncate max-w-[300px]">
|
|
152
|
-
{result.memory.title}
|
|
153
|
-
</span>
|
|
154
|
-
</div>
|
|
155
|
-
</td>
|
|
156
|
-
<td className="py-2 pr-4">
|
|
157
|
-
<div className="flex items-center gap-1">
|
|
158
|
-
<div
|
|
159
|
-
className="h-1.5 rounded-full bg-gradient-to-r from-blue-600 to-purple-600"
|
|
160
|
-
style={{ width: `${result.relevanceScore * 100}%`, maxWidth: '60px' }}
|
|
161
|
-
/>
|
|
162
|
-
<span className="text-xs text-slate-400">
|
|
163
|
-
{(result.relevanceScore * 100).toFixed(0)}%
|
|
164
|
-
</span>
|
|
165
|
-
</div>
|
|
166
|
-
</td>
|
|
167
|
-
<td className="py-2 pr-4">
|
|
168
|
-
<span className={`text-xs px-1.5 py-0.5 rounded ${
|
|
169
|
-
result.memory.type === 'long_term'
|
|
170
|
-
? 'bg-purple-600/30 text-purple-300'
|
|
171
|
-
: result.memory.type === 'short_term'
|
|
172
|
-
? 'bg-blue-600/30 text-blue-300'
|
|
173
|
-
: 'bg-green-600/30 text-green-300'
|
|
174
|
-
}`}>
|
|
175
|
-
{result.memory.type.replace('_', '-')}
|
|
176
|
-
</span>
|
|
177
|
-
</td>
|
|
178
|
-
<td className="py-2">
|
|
179
|
-
<span className="text-xs text-slate-400">
|
|
180
|
-
{result.memory.category}
|
|
181
|
-
</span>
|
|
182
|
-
</td>
|
|
183
|
-
</tr>
|
|
184
|
-
))}
|
|
185
|
-
</tbody>
|
|
186
|
-
</table>
|
|
187
|
-
</div>
|
|
188
|
-
)}
|
|
189
|
-
</div>
|
|
190
|
-
</div>
|
|
191
|
-
);
|
|
192
|
-
}
|