@cortexmemory/cli 0.26.0 → 0.27.1
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/dist/commands/db.d.ts.map +1 -1
- package/dist/commands/db.js +2 -18
- package/dist/commands/db.js.map +1 -1
- package/dist/commands/dev.d.ts.map +1 -1
- package/dist/commands/dev.js +153 -17
- package/dist/commands/dev.js.map +1 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +373 -70
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/setup.d.ts.map +1 -1
- package/dist/commands/setup.js +102 -46
- package/dist/commands/setup.js.map +1 -1
- package/dist/commands/status.d.ts.map +1 -1
- package/dist/commands/status.js +94 -7
- package/dist/commands/status.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/types.d.ts +23 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/config.d.ts +11 -0
- package/dist/utils/config.d.ts.map +1 -1
- package/dist/utils/config.js +20 -0
- package/dist/utils/config.js.map +1 -1
- package/dist/utils/init/convex-setup.d.ts +58 -6
- package/dist/utils/init/convex-setup.d.ts.map +1 -1
- package/dist/utils/init/convex-setup.js +261 -57
- package/dist/utils/init/convex-setup.js.map +1 -1
- package/dist/utils/init/env-generator.d.ts.map +1 -1
- package/dist/utils/init/env-generator.js +12 -2
- package/dist/utils/init/env-generator.js.map +1 -1
- package/dist/utils/init/graph-setup.d.ts.map +1 -1
- package/dist/utils/init/graph-setup.js +12 -0
- package/dist/utils/init/graph-setup.js.map +1 -1
- package/dist/utils/init/quickstart-setup.d.ts +87 -0
- package/dist/utils/init/quickstart-setup.d.ts.map +1 -0
- package/dist/utils/init/quickstart-setup.js +462 -0
- package/dist/utils/init/quickstart-setup.js.map +1 -0
- package/dist/utils/init/types.d.ts +4 -0
- package/dist/utils/init/types.d.ts.map +1 -1
- package/dist/utils/schema-sync.d.ts.map +1 -1
- package/dist/utils/schema-sync.js +27 -21
- package/dist/utils/schema-sync.js.map +1 -1
- package/package.json +3 -2
- package/templates/vercel-ai-quickstart/.env.local.example +45 -0
- package/templates/vercel-ai-quickstart/README.md +280 -0
- package/templates/vercel-ai-quickstart/app/api/chat/route.ts +196 -0
- package/templates/vercel-ai-quickstart/app/api/facts/route.ts +39 -0
- package/templates/vercel-ai-quickstart/app/api/health/route.ts +99 -0
- package/templates/vercel-ai-quickstart/app/api/memories/route.ts +37 -0
- package/templates/vercel-ai-quickstart/app/globals.css +114 -0
- package/templates/vercel-ai-quickstart/app/layout.tsx +19 -0
- package/templates/vercel-ai-quickstart/app/page.tsx +131 -0
- package/templates/vercel-ai-quickstart/components/ChatInterface.tsx +237 -0
- package/templates/vercel-ai-quickstart/components/ConvexClientProvider.tsx +21 -0
- package/templates/vercel-ai-quickstart/components/DataPreview.tsx +57 -0
- package/templates/vercel-ai-quickstart/components/HealthStatus.tsx +214 -0
- package/templates/vercel-ai-quickstart/components/LayerCard.tsx +263 -0
- package/templates/vercel-ai-quickstart/components/LayerFlowDiagram.tsx +195 -0
- package/templates/vercel-ai-quickstart/components/MemorySpaceSwitcher.tsx +93 -0
- package/templates/vercel-ai-quickstart/convex/conversations.ts +67 -0
- package/templates/vercel-ai-quickstart/convex/facts.ts +131 -0
- package/templates/vercel-ai-quickstart/convex/health.ts +15 -0
- package/templates/vercel-ai-quickstart/convex/memories.ts +104 -0
- package/templates/vercel-ai-quickstart/convex/schema.ts +20 -0
- package/templates/vercel-ai-quickstart/convex/users.ts +105 -0
- package/templates/vercel-ai-quickstart/lib/animations.ts +146 -0
- package/templates/vercel-ai-quickstart/lib/layer-tracking.ts +214 -0
- package/templates/vercel-ai-quickstart/next.config.js +7 -0
- package/templates/vercel-ai-quickstart/package.json +41 -0
- package/templates/vercel-ai-quickstart/postcss.config.js +5 -0
- package/templates/vercel-ai-quickstart/tailwind.config.js +37 -0
- package/templates/vercel-ai-quickstart/tsconfig.json +33 -0
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { motion } from "framer-motion";
|
|
4
|
+
import { LayerCard } from "./LayerCard";
|
|
5
|
+
import type { LayerState } from "@/lib/layer-tracking";
|
|
6
|
+
|
|
7
|
+
interface LayerFlowDiagramProps {
|
|
8
|
+
layers: Record<string, LayerState>;
|
|
9
|
+
isOrchestrating: boolean;
|
|
10
|
+
memorySpaceId: string;
|
|
11
|
+
userId: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function LayerFlowDiagram({
|
|
15
|
+
layers,
|
|
16
|
+
isOrchestrating,
|
|
17
|
+
memorySpaceId,
|
|
18
|
+
userId,
|
|
19
|
+
}: LayerFlowDiagramProps) {
|
|
20
|
+
// Define the layer flow order
|
|
21
|
+
const topLayers = [
|
|
22
|
+
{ key: "memorySpace", name: "Memory Space", icon: "📦" },
|
|
23
|
+
{ key: "user", name: "User", icon: "👤" },
|
|
24
|
+
{ key: "agent", name: "Agent", icon: "🤖" },
|
|
25
|
+
];
|
|
26
|
+
|
|
27
|
+
const flowLayers = [
|
|
28
|
+
{ key: "conversation", name: "Conversation", icon: "💬" },
|
|
29
|
+
{ key: "vector", name: "Vector Store", icon: "🎯" },
|
|
30
|
+
{ key: "facts", name: "Facts", icon: "💡" },
|
|
31
|
+
{ key: "graph", name: "Graph", icon: "🕸️", optional: true },
|
|
32
|
+
];
|
|
33
|
+
|
|
34
|
+
return (
|
|
35
|
+
<div className="space-y-6">
|
|
36
|
+
{/* Title section */}
|
|
37
|
+
<div className="text-center">
|
|
38
|
+
<h3 className="text-lg font-semibold">Memory Orchestration Flow</h3>
|
|
39
|
+
<p className="text-sm text-gray-400 mt-1">
|
|
40
|
+
{isOrchestrating
|
|
41
|
+
? "Processing your message through Cortex layers..."
|
|
42
|
+
: "Send a message to see data flow through the system"}
|
|
43
|
+
</p>
|
|
44
|
+
</div>
|
|
45
|
+
|
|
46
|
+
{/* Top row: Context layers */}
|
|
47
|
+
<div className="flex justify-center gap-3">
|
|
48
|
+
{topLayers.map((layer, i) => (
|
|
49
|
+
<motion.div
|
|
50
|
+
key={layer.key}
|
|
51
|
+
initial={{ opacity: 0, y: -10 }}
|
|
52
|
+
animate={{ opacity: 1, y: 0 }}
|
|
53
|
+
transition={{ delay: i * 0.1 }}
|
|
54
|
+
>
|
|
55
|
+
<LayerCard
|
|
56
|
+
name={layer.name}
|
|
57
|
+
icon={layer.icon}
|
|
58
|
+
status={layers[layer.key]?.status || "pending"}
|
|
59
|
+
latencyMs={layers[layer.key]?.latencyMs}
|
|
60
|
+
data={layers[layer.key]?.data}
|
|
61
|
+
compact
|
|
62
|
+
/>
|
|
63
|
+
</motion.div>
|
|
64
|
+
))}
|
|
65
|
+
</div>
|
|
66
|
+
|
|
67
|
+
{/* Flow connector */}
|
|
68
|
+
<div className="flex justify-center">
|
|
69
|
+
<motion.div
|
|
70
|
+
className="w-0.5 h-8 bg-gradient-to-b from-white/30 to-white/10"
|
|
71
|
+
animate={{
|
|
72
|
+
opacity: isOrchestrating ? [0.3, 1, 0.3] : 0.3,
|
|
73
|
+
}}
|
|
74
|
+
transition={{
|
|
75
|
+
duration: 1.5,
|
|
76
|
+
repeat: Infinity,
|
|
77
|
+
ease: "easeInOut",
|
|
78
|
+
}}
|
|
79
|
+
/>
|
|
80
|
+
</div>
|
|
81
|
+
|
|
82
|
+
{/* Main flow: Processing layers */}
|
|
83
|
+
<div className="space-y-3">
|
|
84
|
+
{flowLayers.map((layer, i) => (
|
|
85
|
+
<motion.div
|
|
86
|
+
key={layer.key}
|
|
87
|
+
initial={{ opacity: 0, x: -20 }}
|
|
88
|
+
animate={{ opacity: 1, x: 0 }}
|
|
89
|
+
transition={{ delay: 0.3 + i * 0.1 }}
|
|
90
|
+
>
|
|
91
|
+
<LayerCard
|
|
92
|
+
name={layer.name}
|
|
93
|
+
icon={layer.icon}
|
|
94
|
+
status={
|
|
95
|
+
layers[layer.key]?.status ||
|
|
96
|
+
(layer.optional ? "skipped" : "pending")
|
|
97
|
+
}
|
|
98
|
+
latencyMs={layers[layer.key]?.latencyMs}
|
|
99
|
+
data={layers[layer.key]?.data}
|
|
100
|
+
optional={layer.optional}
|
|
101
|
+
// Belief revision info (v0.24.0+) - only for facts layer
|
|
102
|
+
revisionAction={
|
|
103
|
+
layer.key === "facts"
|
|
104
|
+
? layers[layer.key]?.revisionAction
|
|
105
|
+
: undefined
|
|
106
|
+
}
|
|
107
|
+
supersededFacts={
|
|
108
|
+
layer.key === "facts"
|
|
109
|
+
? layers[layer.key]?.supersededFacts
|
|
110
|
+
: undefined
|
|
111
|
+
}
|
|
112
|
+
/>
|
|
113
|
+
|
|
114
|
+
{/* Flow connector between layers */}
|
|
115
|
+
{i < flowLayers.length - 1 && (
|
|
116
|
+
<div className="flex justify-center py-2">
|
|
117
|
+
<motion.div
|
|
118
|
+
className="w-0.5 h-6"
|
|
119
|
+
style={{
|
|
120
|
+
background:
|
|
121
|
+
layers[layer.key]?.status === "complete"
|
|
122
|
+
? "linear-gradient(to bottom, rgb(34, 197, 94), rgba(255, 255, 255, 0.1))"
|
|
123
|
+
: "linear-gradient(to bottom, rgba(255, 255, 255, 0.2), rgba(255, 255, 255, 0.05))",
|
|
124
|
+
}}
|
|
125
|
+
animate={{
|
|
126
|
+
opacity:
|
|
127
|
+
isOrchestrating &&
|
|
128
|
+
layers[layer.key]?.status === "in_progress"
|
|
129
|
+
? [0.3, 1, 0.3]
|
|
130
|
+
: 1,
|
|
131
|
+
}}
|
|
132
|
+
transition={{
|
|
133
|
+
duration: 0.8,
|
|
134
|
+
repeat: Infinity,
|
|
135
|
+
ease: "easeInOut",
|
|
136
|
+
}}
|
|
137
|
+
/>
|
|
138
|
+
</div>
|
|
139
|
+
)}
|
|
140
|
+
</motion.div>
|
|
141
|
+
))}
|
|
142
|
+
</div>
|
|
143
|
+
|
|
144
|
+
{/* Legend */}
|
|
145
|
+
<div className="pt-4 border-t border-white/10 space-y-2">
|
|
146
|
+
{/* Status legend */}
|
|
147
|
+
<div className="flex justify-center gap-4 text-xs text-gray-500">
|
|
148
|
+
<div className="flex items-center gap-1.5">
|
|
149
|
+
<span className="w-2 h-2 rounded-full bg-gray-500" />
|
|
150
|
+
<span>Pending</span>
|
|
151
|
+
</div>
|
|
152
|
+
<div className="flex items-center gap-1.5">
|
|
153
|
+
<span className="w-2 h-2 rounded-full bg-yellow-500 animate-pulse" />
|
|
154
|
+
<span>Processing</span>
|
|
155
|
+
</div>
|
|
156
|
+
<div className="flex items-center gap-1.5">
|
|
157
|
+
<span className="w-2 h-2 rounded-full bg-green-500" />
|
|
158
|
+
<span>Complete</span>
|
|
159
|
+
</div>
|
|
160
|
+
<div className="flex items-center gap-1.5">
|
|
161
|
+
<span className="w-2 h-2 rounded-full bg-gray-700" />
|
|
162
|
+
<span>Skipped</span>
|
|
163
|
+
</div>
|
|
164
|
+
</div>
|
|
165
|
+
{/* Belief revision legend (v0.24.0+) */}
|
|
166
|
+
<div className="flex justify-center gap-3 text-xs text-gray-600">
|
|
167
|
+
<span className="text-gray-700">Belief Revision:</span>
|
|
168
|
+
<div className="flex items-center gap-1">
|
|
169
|
+
<span className="px-1 py-0.5 bg-green-500/20 text-green-400 rounded text-[9px]">
|
|
170
|
+
NEW
|
|
171
|
+
</span>
|
|
172
|
+
</div>
|
|
173
|
+
<div className="flex items-center gap-1">
|
|
174
|
+
<span className="px-1 py-0.5 bg-blue-500/20 text-blue-400 rounded text-[9px]">
|
|
175
|
+
UPDATED
|
|
176
|
+
</span>
|
|
177
|
+
</div>
|
|
178
|
+
<div className="flex items-center gap-1">
|
|
179
|
+
<span className="px-1 py-0.5 bg-orange-500/20 text-orange-400 rounded text-[9px]">
|
|
180
|
+
SUPERSEDED
|
|
181
|
+
</span>
|
|
182
|
+
</div>
|
|
183
|
+
</div>
|
|
184
|
+
</div>
|
|
185
|
+
|
|
186
|
+
{/* Context info */}
|
|
187
|
+
<div className="text-center text-xs text-gray-600">
|
|
188
|
+
<p>
|
|
189
|
+
Space: <code className="text-gray-400">{memorySpaceId}</code> • User:{" "}
|
|
190
|
+
<code className="text-gray-400">{userId}</code>
|
|
191
|
+
</p>
|
|
192
|
+
</div>
|
|
193
|
+
</div>
|
|
194
|
+
);
|
|
195
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useState } from "react";
|
|
4
|
+
import { motion, AnimatePresence } from "framer-motion";
|
|
5
|
+
|
|
6
|
+
interface MemorySpaceSwitcherProps {
|
|
7
|
+
value: string;
|
|
8
|
+
onChange: (value: string) => void;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const spaces = [
|
|
12
|
+
{ id: "quickstart-demo", name: "Demo", icon: "🧪" },
|
|
13
|
+
{ id: "personal", name: "Personal", icon: "👤" },
|
|
14
|
+
{ id: "work", name: "Work", icon: "💼" },
|
|
15
|
+
];
|
|
16
|
+
|
|
17
|
+
export function MemorySpaceSwitcher({
|
|
18
|
+
value,
|
|
19
|
+
onChange,
|
|
20
|
+
}: MemorySpaceSwitcherProps) {
|
|
21
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
22
|
+
const currentSpace = spaces.find((s) => s.id === value) || spaces[0];
|
|
23
|
+
|
|
24
|
+
return (
|
|
25
|
+
<div className="relative">
|
|
26
|
+
<button
|
|
27
|
+
onClick={() => setIsOpen(!isOpen)}
|
|
28
|
+
className="flex items-center gap-2 px-3 py-2 bg-white/5 hover:bg-white/10 border border-white/10 rounded-lg transition-colors"
|
|
29
|
+
>
|
|
30
|
+
<span>{currentSpace.icon}</span>
|
|
31
|
+
<span className="text-sm font-medium">{currentSpace.name}</span>
|
|
32
|
+
<motion.span
|
|
33
|
+
className="text-gray-400 text-xs"
|
|
34
|
+
animate={{ rotate: isOpen ? 180 : 0 }}
|
|
35
|
+
>
|
|
36
|
+
▼
|
|
37
|
+
</motion.span>
|
|
38
|
+
</button>
|
|
39
|
+
|
|
40
|
+
<AnimatePresence>
|
|
41
|
+
{isOpen && (
|
|
42
|
+
<>
|
|
43
|
+
{/* Backdrop */}
|
|
44
|
+
<div
|
|
45
|
+
className="fixed inset-0 z-10"
|
|
46
|
+
onClick={() => setIsOpen(false)}
|
|
47
|
+
/>
|
|
48
|
+
|
|
49
|
+
{/* Dropdown */}
|
|
50
|
+
<motion.div
|
|
51
|
+
initial={{ opacity: 0, y: -10 }}
|
|
52
|
+
animate={{ opacity: 1, y: 0 }}
|
|
53
|
+
exit={{ opacity: 0, y: -10 }}
|
|
54
|
+
className="absolute right-0 top-full mt-2 w-56 bg-gray-900 border border-white/10 rounded-lg shadow-xl z-20 overflow-hidden"
|
|
55
|
+
>
|
|
56
|
+
<div className="p-2">
|
|
57
|
+
<div className="text-xs text-gray-500 px-2 py-1 mb-1">
|
|
58
|
+
Memory Space
|
|
59
|
+
</div>
|
|
60
|
+
|
|
61
|
+
{spaces.map((space) => (
|
|
62
|
+
<button
|
|
63
|
+
key={space.id}
|
|
64
|
+
onClick={() => {
|
|
65
|
+
onChange(space.id);
|
|
66
|
+
setIsOpen(false);
|
|
67
|
+
}}
|
|
68
|
+
className={`w-full flex items-center gap-2 px-3 py-2 rounded-lg text-left transition-colors ${
|
|
69
|
+
value === space.id
|
|
70
|
+
? "bg-cortex-600/20 text-cortex-400"
|
|
71
|
+
: "hover:bg-white/5"
|
|
72
|
+
}`}
|
|
73
|
+
>
|
|
74
|
+
<span>{space.icon}</span>
|
|
75
|
+
<span className="flex-1 font-medium">{space.name}</span>
|
|
76
|
+
<code className="text-xs text-gray-500">{space.id}</code>
|
|
77
|
+
</button>
|
|
78
|
+
))}
|
|
79
|
+
</div>
|
|
80
|
+
|
|
81
|
+
<div className="border-t border-white/10 p-3 bg-white/5">
|
|
82
|
+
<p className="text-xs text-gray-400">
|
|
83
|
+
Switch memory spaces to demonstrate multi-tenant isolation.
|
|
84
|
+
Memories in one space don't appear in others.
|
|
85
|
+
</p>
|
|
86
|
+
</div>
|
|
87
|
+
</motion.div>
|
|
88
|
+
</>
|
|
89
|
+
)}
|
|
90
|
+
</AnimatePresence>
|
|
91
|
+
</div>
|
|
92
|
+
);
|
|
93
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Convex queries for real-time conversation updates
|
|
3
|
+
*
|
|
4
|
+
* These queries enable the LayerFlowDiagram to show live updates
|
|
5
|
+
* as data flows through the Cortex memory system.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { query } from "./_generated/server";
|
|
9
|
+
import { v } from "convex/values";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Get recent conversations for a memory space
|
|
13
|
+
*
|
|
14
|
+
* Used by the demo to watch for new conversations being created.
|
|
15
|
+
*/
|
|
16
|
+
export const getRecent = query({
|
|
17
|
+
args: {
|
|
18
|
+
memorySpaceId: v.string(),
|
|
19
|
+
limit: v.optional(v.number()),
|
|
20
|
+
},
|
|
21
|
+
handler: async (ctx, args) => {
|
|
22
|
+
const limit = args.limit ?? 10;
|
|
23
|
+
|
|
24
|
+
// Query conversations table (from Cortex SDK schema)
|
|
25
|
+
const conversations = await ctx.db
|
|
26
|
+
.query("conversations")
|
|
27
|
+
.filter((q) => q.eq(q.field("memorySpaceId"), args.memorySpaceId))
|
|
28
|
+
.order("desc")
|
|
29
|
+
.take(limit);
|
|
30
|
+
|
|
31
|
+
return conversations;
|
|
32
|
+
},
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Get a specific conversation by ID
|
|
37
|
+
*/
|
|
38
|
+
export const get = query({
|
|
39
|
+
args: {
|
|
40
|
+
conversationId: v.string(),
|
|
41
|
+
},
|
|
42
|
+
handler: async (ctx, args) => {
|
|
43
|
+
const conversation = await ctx.db
|
|
44
|
+
.query("conversations")
|
|
45
|
+
.filter((q) => q.eq(q.field("conversationId"), args.conversationId))
|
|
46
|
+
.first();
|
|
47
|
+
|
|
48
|
+
return conversation;
|
|
49
|
+
},
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Get conversation count for a memory space
|
|
54
|
+
*/
|
|
55
|
+
export const count = query({
|
|
56
|
+
args: {
|
|
57
|
+
memorySpaceId: v.string(),
|
|
58
|
+
},
|
|
59
|
+
handler: async (ctx, args) => {
|
|
60
|
+
const conversations = await ctx.db
|
|
61
|
+
.query("conversations")
|
|
62
|
+
.filter((q) => q.eq(q.field("memorySpaceId"), args.memorySpaceId))
|
|
63
|
+
.collect();
|
|
64
|
+
|
|
65
|
+
return conversations.length;
|
|
66
|
+
},
|
|
67
|
+
});
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Convex queries for real-time fact updates
|
|
3
|
+
*
|
|
4
|
+
* These queries enable the LayerFlowDiagram to show live updates
|
|
5
|
+
* as facts are extracted from conversations.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { query } from "./_generated/server";
|
|
9
|
+
import { v } from "convex/values";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Get recent facts for a memory space
|
|
13
|
+
*
|
|
14
|
+
* Used by the demo to watch for new facts being extracted.
|
|
15
|
+
*/
|
|
16
|
+
export const getRecent = query({
|
|
17
|
+
args: {
|
|
18
|
+
memorySpaceId: v.string(),
|
|
19
|
+
limit: v.optional(v.number()),
|
|
20
|
+
},
|
|
21
|
+
handler: async (ctx, args) => {
|
|
22
|
+
const limit = args.limit ?? 20;
|
|
23
|
+
|
|
24
|
+
// Query facts table (from Cortex SDK schema)
|
|
25
|
+
const facts = await ctx.db
|
|
26
|
+
.query("facts")
|
|
27
|
+
.filter((q) => q.eq(q.field("memorySpaceId"), args.memorySpaceId))
|
|
28
|
+
.order("desc")
|
|
29
|
+
.take(limit);
|
|
30
|
+
|
|
31
|
+
return facts;
|
|
32
|
+
},
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Get facts for a specific user
|
|
37
|
+
*/
|
|
38
|
+
export const getByUser = query({
|
|
39
|
+
args: {
|
|
40
|
+
memorySpaceId: v.string(),
|
|
41
|
+
userId: v.string(),
|
|
42
|
+
limit: v.optional(v.number()),
|
|
43
|
+
},
|
|
44
|
+
handler: async (ctx, args) => {
|
|
45
|
+
const limit = args.limit ?? 50;
|
|
46
|
+
|
|
47
|
+
const facts = await ctx.db
|
|
48
|
+
.query("facts")
|
|
49
|
+
.filter((q) =>
|
|
50
|
+
q.and(
|
|
51
|
+
q.eq(q.field("memorySpaceId"), args.memorySpaceId),
|
|
52
|
+
q.eq(q.field("userId"), args.userId),
|
|
53
|
+
),
|
|
54
|
+
)
|
|
55
|
+
.order("desc")
|
|
56
|
+
.take(limit);
|
|
57
|
+
|
|
58
|
+
return facts;
|
|
59
|
+
},
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Get facts by type
|
|
64
|
+
*/
|
|
65
|
+
export const getByType = query({
|
|
66
|
+
args: {
|
|
67
|
+
memorySpaceId: v.string(),
|
|
68
|
+
factType: v.string(),
|
|
69
|
+
limit: v.optional(v.number()),
|
|
70
|
+
},
|
|
71
|
+
handler: async (ctx, args) => {
|
|
72
|
+
const limit = args.limit ?? 20;
|
|
73
|
+
|
|
74
|
+
const facts = await ctx.db
|
|
75
|
+
.query("facts")
|
|
76
|
+
.filter((q) =>
|
|
77
|
+
q.and(
|
|
78
|
+
q.eq(q.field("memorySpaceId"), args.memorySpaceId),
|
|
79
|
+
q.eq(q.field("factType"), args.factType),
|
|
80
|
+
),
|
|
81
|
+
)
|
|
82
|
+
.order("desc")
|
|
83
|
+
.take(limit);
|
|
84
|
+
|
|
85
|
+
return facts;
|
|
86
|
+
},
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Get fact count for a memory space
|
|
91
|
+
*/
|
|
92
|
+
export const count = query({
|
|
93
|
+
args: {
|
|
94
|
+
memorySpaceId: v.string(),
|
|
95
|
+
},
|
|
96
|
+
handler: async (ctx, args) => {
|
|
97
|
+
const facts = await ctx.db
|
|
98
|
+
.query("facts")
|
|
99
|
+
.filter((q) => q.eq(q.field("memorySpaceId"), args.memorySpaceId))
|
|
100
|
+
.collect();
|
|
101
|
+
|
|
102
|
+
return facts.length;
|
|
103
|
+
},
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Get fact type summary for a user
|
|
108
|
+
*/
|
|
109
|
+
export const typeSummary = query({
|
|
110
|
+
args: {
|
|
111
|
+
memorySpaceId: v.string(),
|
|
112
|
+
userId: v.optional(v.string()),
|
|
113
|
+
},
|
|
114
|
+
handler: async (ctx, args) => {
|
|
115
|
+
let factsQuery = ctx.db
|
|
116
|
+
.query("facts")
|
|
117
|
+
.filter((q) => q.eq(q.field("memorySpaceId"), args.memorySpaceId));
|
|
118
|
+
|
|
119
|
+
const facts = await factsQuery.collect();
|
|
120
|
+
|
|
121
|
+
// Group by type
|
|
122
|
+
const summary: Record<string, number> = {};
|
|
123
|
+
for (const fact of facts) {
|
|
124
|
+
if (args.userId && fact.userId !== args.userId) continue;
|
|
125
|
+
const type = fact.factType || "unknown";
|
|
126
|
+
summary[type] = (summary[type] || 0) + 1;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
return summary;
|
|
130
|
+
},
|
|
131
|
+
});
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { query } from "./_generated/server";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Simple health check query to verify Convex backend is reachable
|
|
5
|
+
*/
|
|
6
|
+
export const ping = query({
|
|
7
|
+
args: {},
|
|
8
|
+
handler: async () => {
|
|
9
|
+
return {
|
|
10
|
+
status: "ok",
|
|
11
|
+
timestamp: Date.now(),
|
|
12
|
+
backend: "convex",
|
|
13
|
+
};
|
|
14
|
+
},
|
|
15
|
+
});
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Convex queries for real-time memory updates
|
|
3
|
+
*
|
|
4
|
+
* These queries enable the LayerFlowDiagram to show live updates
|
|
5
|
+
* as vector memories are created in the Cortex system.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { query } from "./_generated/server";
|
|
9
|
+
import { v } from "convex/values";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Get recent memories for a memory space
|
|
13
|
+
*
|
|
14
|
+
* Used by the demo to watch for new memories being created.
|
|
15
|
+
*/
|
|
16
|
+
export const getRecent = query({
|
|
17
|
+
args: {
|
|
18
|
+
memorySpaceId: v.string(),
|
|
19
|
+
limit: v.optional(v.number()),
|
|
20
|
+
},
|
|
21
|
+
handler: async (ctx, args) => {
|
|
22
|
+
const limit = args.limit ?? 10;
|
|
23
|
+
|
|
24
|
+
// Query memories table (from Cortex SDK schema)
|
|
25
|
+
const memories = await ctx.db
|
|
26
|
+
.query("memories")
|
|
27
|
+
.filter((q) => q.eq(q.field("memorySpaceId"), args.memorySpaceId))
|
|
28
|
+
.order("desc")
|
|
29
|
+
.take(limit);
|
|
30
|
+
|
|
31
|
+
return memories;
|
|
32
|
+
},
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Get memories for a specific user
|
|
37
|
+
*/
|
|
38
|
+
export const getByUser = query({
|
|
39
|
+
args: {
|
|
40
|
+
memorySpaceId: v.string(),
|
|
41
|
+
userId: v.string(),
|
|
42
|
+
limit: v.optional(v.number()),
|
|
43
|
+
},
|
|
44
|
+
handler: async (ctx, args) => {
|
|
45
|
+
const limit = args.limit ?? 20;
|
|
46
|
+
|
|
47
|
+
const memories = await ctx.db
|
|
48
|
+
.query("memories")
|
|
49
|
+
.filter((q) =>
|
|
50
|
+
q.and(
|
|
51
|
+
q.eq(q.field("memorySpaceId"), args.memorySpaceId),
|
|
52
|
+
q.eq(q.field("userId"), args.userId),
|
|
53
|
+
),
|
|
54
|
+
)
|
|
55
|
+
.order("desc")
|
|
56
|
+
.take(limit);
|
|
57
|
+
|
|
58
|
+
return memories;
|
|
59
|
+
},
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Get memory count for a memory space
|
|
64
|
+
*/
|
|
65
|
+
export const count = query({
|
|
66
|
+
args: {
|
|
67
|
+
memorySpaceId: v.string(),
|
|
68
|
+
},
|
|
69
|
+
handler: async (ctx, args) => {
|
|
70
|
+
const memories = await ctx.db
|
|
71
|
+
.query("memories")
|
|
72
|
+
.filter((q) => q.eq(q.field("memorySpaceId"), args.memorySpaceId))
|
|
73
|
+
.collect();
|
|
74
|
+
|
|
75
|
+
return memories.length;
|
|
76
|
+
},
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Search memories by content (simple text search for demo)
|
|
81
|
+
*/
|
|
82
|
+
export const search = query({
|
|
83
|
+
args: {
|
|
84
|
+
memorySpaceId: v.string(),
|
|
85
|
+
query: v.string(),
|
|
86
|
+
limit: v.optional(v.number()),
|
|
87
|
+
},
|
|
88
|
+
handler: async (ctx, args) => {
|
|
89
|
+
const limit = args.limit ?? 5;
|
|
90
|
+
const queryLower = args.query.toLowerCase();
|
|
91
|
+
|
|
92
|
+
// Simple text search (in production, use vector search)
|
|
93
|
+
const allMemories = await ctx.db
|
|
94
|
+
.query("memories")
|
|
95
|
+
.filter((q) => q.eq(q.field("memorySpaceId"), args.memorySpaceId))
|
|
96
|
+
.collect();
|
|
97
|
+
|
|
98
|
+
const matching = allMemories
|
|
99
|
+
.filter((m) => m.content?.toLowerCase().includes(queryLower))
|
|
100
|
+
.slice(0, limit);
|
|
101
|
+
|
|
102
|
+
return matching;
|
|
103
|
+
},
|
|
104
|
+
});
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Convex schema for the quickstart demo
|
|
3
|
+
*
|
|
4
|
+
* This extends the Cortex SDK schema to add any demo-specific tables.
|
|
5
|
+
* The actual memory tables (conversations, memories, facts) come from @cortexmemory/sdk.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { defineSchema, defineTable } from "convex/server";
|
|
9
|
+
import { v } from "convex/values";
|
|
10
|
+
|
|
11
|
+
export default defineSchema({
|
|
12
|
+
// Demo session tracking (optional, for demo purposes)
|
|
13
|
+
demoSessions: defineTable({
|
|
14
|
+
sessionId: v.string(),
|
|
15
|
+
userId: v.string(),
|
|
16
|
+
memorySpaceId: v.string(),
|
|
17
|
+
startedAt: v.number(),
|
|
18
|
+
messageCount: v.number(),
|
|
19
|
+
}).index("by_session", ["sessionId"]),
|
|
20
|
+
});
|