Skip to content

Commit 02d9fed

Browse files
feat(agent): messages array, memory (#2023)
* feat(agent): messages array, memory options * feat(messages-input): re-order messages * backend for new memory setup, backwards compatibility in loadWorkflowsFromNormalizedTable from old agent block to new format * added memories all conversation sliding token window, standardized modals * lint * fix build * reorder popover for output selector for chat * add internal auth, finish memories * fix rebase * fix failing test --------- Co-authored-by: waleed <walif6@gmail.com>
1 parent a8a693f commit 02d9fed

53 files changed

Lines changed: 11533 additions & 1383 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

apps/sim/.cursorrules

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -563,6 +563,32 @@ logger.error('Operation failed', { error })
563563

564564
---
565565

566+
## Linting and Formatting
567+
568+
### Automated Linting
569+
570+
**Do not manually fix linting errors.** The project uses automated linting tools that should handle formatting and style issues.
571+
572+
### Rules
573+
574+
1. **No Manual Fixes**: Do not attempt to manually reorder CSS classes, fix formatting, or address linter warnings
575+
2. **Use Automated Tools**: If linting errors need to be fixed, run `bun run lint` to let the automated tools handle it
576+
3. **Focus on Logic**: Concentrate on functionality, TypeScript correctness, and architectural patterns
577+
4. **Let Tools Handle Style**: Biome and other linters will automatically format code according to project standards
578+
579+
### When Linting Matters
580+
581+
- **Syntax Errors**: Fix actual syntax errors that prevent compilation
582+
- **Type Errors**: Address TypeScript type errors that indicate logic issues
583+
- **Ignore Style Warnings**: CSS class order, formatting preferences, etc. will be handled by tooling
584+
585+
```bash
586+
# If linting is required
587+
bun run lint
588+
```
589+
590+
---
591+
566592
## Code Quality Checklist
567593

568594
Before considering a component/hook complete, verify:

apps/sim/app/api/memory/[id]/route.ts

Lines changed: 106 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { db } from '@sim/db'
2-
import { memory } from '@sim/db/schema'
2+
import { memory, workflowBlocks } from '@sim/db/schema'
33
import { and, eq } from 'drizzle-orm'
44
import { type NextRequest, NextResponse } from 'next/server'
55
import { z } from 'zod'
@@ -8,6 +8,43 @@ import { generateRequestId } from '@/lib/utils'
88

99
const logger = createLogger('MemoryByIdAPI')
1010

11+
/**
12+
* Parse memory key into conversationId and blockId
13+
* Key format: conversationId:blockId
14+
*/
15+
function parseMemoryKey(key: string): { conversationId: string; blockId: string } | null {
16+
const parts = key.split(':')
17+
if (parts.length !== 2) {
18+
return null
19+
}
20+
return {
21+
conversationId: parts[0],
22+
blockId: parts[1],
23+
}
24+
}
25+
26+
/**
27+
* Lookup block name from block ID
28+
*/
29+
async function getBlockName(blockId: string, workflowId: string): Promise<string | undefined> {
30+
try {
31+
const result = await db
32+
.select({ name: workflowBlocks.name })
33+
.from(workflowBlocks)
34+
.where(and(eq(workflowBlocks.id, blockId), eq(workflowBlocks.workflowId, workflowId)))
35+
.limit(1)
36+
37+
if (result.length === 0) {
38+
return undefined
39+
}
40+
41+
return result[0].name
42+
} catch (error) {
43+
logger.error('Error looking up block name', { error, blockId, workflowId })
44+
return undefined
45+
}
46+
}
47+
1148
const memoryQuerySchema = z.object({
1249
workflowId: z.string().uuid('Invalid workflow ID format'),
1350
})
@@ -41,7 +78,6 @@ export async function GET(request: NextRequest, { params }: { params: Promise<{
4178
try {
4279
logger.info(`[${requestId}] Processing memory get request for ID: ${id}`)
4380

44-
// Get workflowId from query parameter (required)
4581
const url = new URL(request.url)
4682
const workflowId = url.searchParams.get('workflowId')
4783

@@ -65,7 +101,6 @@ export async function GET(request: NextRequest, { params }: { params: Promise<{
65101

66102
const { workflowId: validatedWorkflowId } = validation.data
67103

68-
// Query the database for the memory
69104
const memories = await db
70105
.select()
71106
.from(memory)
@@ -86,13 +121,36 @@ export async function GET(request: NextRequest, { params }: { params: Promise<{
86121
)
87122
}
88123

124+
const mem = memories[0]
125+
const parsed = parseMemoryKey(mem.key)
126+
127+
let enrichedMemory
128+
if (!parsed) {
129+
enrichedMemory = {
130+
conversationId: mem.key,
131+
blockId: 'unknown',
132+
blockName: 'unknown',
133+
data: mem.data,
134+
}
135+
} else {
136+
const { conversationId, blockId } = parsed
137+
const blockName = (await getBlockName(blockId, validatedWorkflowId)) || 'unknown'
138+
139+
enrichedMemory = {
140+
conversationId,
141+
blockId,
142+
blockName,
143+
data: mem.data,
144+
}
145+
}
146+
89147
logger.info(
90148
`[${requestId}] Memory retrieved successfully: ${id} for workflow: ${validatedWorkflowId}`
91149
)
92150
return NextResponse.json(
93151
{
94152
success: true,
95-
data: memories[0],
153+
data: enrichedMemory,
96154
},
97155
{ status: 200 }
98156
)
@@ -122,7 +180,6 @@ export async function DELETE(
122180
try {
123181
logger.info(`[${requestId}] Processing memory delete request for ID: ${id}`)
124182

125-
// Get workflowId from query parameter (required)
126183
const url = new URL(request.url)
127184
const workflowId = url.searchParams.get('workflowId')
128185

@@ -146,7 +203,6 @@ export async function DELETE(
146203

147204
const { workflowId: validatedWorkflowId } = validation.data
148205

149-
// Verify memory exists before attempting to delete
150206
const existingMemory = await db
151207
.select({ id: memory.id })
152208
.from(memory)
@@ -166,7 +222,6 @@ export async function DELETE(
166222
)
167223
}
168224

169-
// Hard delete the memory
170225
await db
171226
.delete(memory)
172227
.where(and(eq(memory.key, id), eq(memory.workflowId, validatedWorkflowId)))
@@ -241,7 +296,6 @@ export async function PUT(request: NextRequest, { params }: { params: Promise<{
241296
)
242297
}
243298

244-
// Verify memory exists before attempting to update
245299
const existingMemories = await db
246300
.select()
247301
.from(memory)
@@ -261,47 +315,68 @@ export async function PUT(request: NextRequest, { params }: { params: Promise<{
261315
)
262316
}
263317

264-
const existingMemory = existingMemories[0]
265-
266-
// Additional validation for agent memory type
267-
if (existingMemory.type === 'agent') {
268-
const agentValidation = agentMemoryDataSchema.safeParse(validatedData)
269-
if (!agentValidation.success) {
270-
const errorMessage = agentValidation.error.errors
271-
.map((err) => `${err.path.join('.')}: ${err.message}`)
272-
.join(', ')
273-
logger.warn(`[${requestId}] Agent memory validation error: ${errorMessage}`)
274-
return NextResponse.json(
275-
{
276-
success: false,
277-
error: {
278-
message: `Invalid agent memory data: ${errorMessage}`,
279-
},
318+
const agentValidation = agentMemoryDataSchema.safeParse(validatedData)
319+
if (!agentValidation.success) {
320+
const errorMessage = agentValidation.error.errors
321+
.map((err) => `${err.path.join('.')}: ${err.message}`)
322+
.join(', ')
323+
logger.warn(`[${requestId}] Agent memory validation error: ${errorMessage}`)
324+
return NextResponse.json(
325+
{
326+
success: false,
327+
error: {
328+
message: `Invalid agent memory data: ${errorMessage}`,
280329
},
281-
{ status: 400 }
282-
)
283-
}
330+
},
331+
{ status: 400 }
332+
)
284333
}
285334

286-
// Update the memory with new data
335+
const now = new Date()
287336
await db
288-
.delete(memory)
337+
.update(memory)
338+
.set({
339+
data: validatedData,
340+
updatedAt: now,
341+
})
289342
.where(and(eq(memory.key, id), eq(memory.workflowId, validatedWorkflowId)))
290343

291-
// Fetch the updated memory
292344
const updatedMemories = await db
293345
.select()
294346
.from(memory)
295347
.where(and(eq(memory.key, id), eq(memory.workflowId, validatedWorkflowId)))
296348
.limit(1)
297349

350+
const mem = updatedMemories[0]
351+
const parsed = parseMemoryKey(mem.key)
352+
353+
let enrichedMemory
354+
if (!parsed) {
355+
enrichedMemory = {
356+
conversationId: mem.key,
357+
blockId: 'unknown',
358+
blockName: 'unknown',
359+
data: mem.data,
360+
}
361+
} else {
362+
const { conversationId, blockId } = parsed
363+
const blockName = (await getBlockName(blockId, validatedWorkflowId)) || 'unknown'
364+
365+
enrichedMemory = {
366+
conversationId,
367+
blockId,
368+
blockName,
369+
data: mem.data,
370+
}
371+
}
372+
298373
logger.info(
299374
`[${requestId}] Memory updated successfully: ${id} for workflow: ${validatedWorkflowId}`
300375
)
301376
return NextResponse.json(
302377
{
303378
success: true,
304-
data: updatedMemories[0],
379+
data: enrichedMemory,
305380
},
306381
{ status: 200 }
307382
)

0 commit comments

Comments
 (0)