chore(biome): removed prettier, added biome (#407)

* chore: replace prettier with biome and add linting

* chore: update devcontainer settings to use biome for linting and remove eslint, prettier

* chore: update docker-compose to use Postgres 17-alpine and standardize quotes

* chore: fixed more BUT disabled most rules due to limit

* added additional rules, fixed linting & ts errors

* added additional rules

* rebased & linted

* fixed oauth

* updated biome & minor modifications

---------

Co-authored-by: Aditya Tripathi <aditya@climactic.co>
This commit is contained in:
Waleed Latif
2025-05-24 03:11:38 -07:00
committed by GitHub
parent 0c8a773e56
commit 8c268e23dd
717 changed files with 13333 additions and 11985 deletions

View File

@@ -1,5 +1,5 @@
import { NextRequest, NextResponse } from 'next/server'
import { and, eq, isNull } from 'drizzle-orm'
import { type NextRequest, NextResponse } from 'next/server'
import { createLogger } from '@/lib/logs/console-logger'
import { db } from '@/db'
import { memory } from '@/db/schema'
@@ -12,20 +12,17 @@ export const runtime = 'nodejs'
/**
* GET handler for retrieving a specific memory by ID
*/
export async function GET(
request: NextRequest,
{ params }: { params: Promise<{ id: string }> }
) {
export async function GET(request: NextRequest, { params }: { params: Promise<{ id: string }> }) {
const requestId = crypto.randomUUID().slice(0, 8)
const { id } = await params
try {
logger.info(`[${requestId}] Processing memory get request for ID: ${id}`)
// Get workflowId from query parameter (required)
const url = new URL(request.url)
const workflowId = url.searchParams.get('workflowId')
if (!workflowId) {
logger.warn(`[${requestId}] Missing required parameter: workflowId`)
return NextResponse.json(
@@ -38,18 +35,12 @@ export async function GET(
{ status: 400 }
)
}
// Query the database for the memory
const memories = await db
.select()
.from(memory)
.where(
and(
eq(memory.key, id),
eq(memory.workflowId, workflowId),
isNull(memory.deletedAt)
)
)
.where(and(eq(memory.key, id), eq(memory.workflowId, workflowId), isNull(memory.deletedAt)))
.orderBy(memory.createdAt)
.limit(1)
@@ -74,7 +65,6 @@ export async function GET(
},
{ status: 200 }
)
} catch (error: any) {
return NextResponse.json(
{
@@ -97,14 +87,14 @@ export async function DELETE(
) {
const requestId = crypto.randomUUID().slice(0, 8)
const { id } = await params
try {
logger.info(`[${requestId}] Processing memory delete request for ID: ${id}`)
// Get workflowId from query parameter (required)
const url = new URL(request.url)
const workflowId = url.searchParams.get('workflowId')
if (!workflowId) {
logger.warn(`[${requestId}] Missing required parameter: workflowId`)
return NextResponse.json(
@@ -117,20 +107,14 @@ export async function DELETE(
{ status: 400 }
)
}
// Verify memory exists before attempting to delete
const existingMemory = await db
.select({ id: memory.id })
.from(memory)
.where(
and(
eq(memory.key, id),
eq(memory.workflowId, workflowId),
isNull(memory.deletedAt)
)
)
.where(and(eq(memory.key, id), eq(memory.workflowId, workflowId), isNull(memory.deletedAt)))
.limit(1)
if (existingMemory.length === 0) {
logger.warn(`[${requestId}] Memory not found: ${id} for workflow: ${workflowId}`)
return NextResponse.json(
@@ -143,21 +127,16 @@ export async function DELETE(
{ status: 404 }
)
}
// Soft delete by setting deletedAt timestamp
await db
.update(memory)
.set({
.set({
deletedAt: new Date(),
updatedAt: new Date()
updatedAt: new Date(),
})
.where(
and(
eq(memory.key, id),
eq(memory.workflowId, workflowId)
)
)
.where(and(eq(memory.key, id), eq(memory.workflowId, workflowId)))
logger.info(`[${requestId}] Memory deleted successfully: ${id} for workflow: ${workflowId}`)
return NextResponse.json(
{
@@ -166,7 +145,6 @@ export async function DELETE(
},
{ status: 200 }
)
} catch (error: any) {
return NextResponse.json(
{
@@ -183,20 +161,17 @@ export async function DELETE(
/**
* PUT handler for updating a specific memory
*/
export async function PUT(
request: NextRequest,
{ params }: { params: Promise<{ id: string }> }
) {
export async function PUT(request: NextRequest, { params }: { params: Promise<{ id: string }> }) {
const requestId = crypto.randomUUID().slice(0, 8)
const { id } = await params
try {
logger.info(`[${requestId}] Processing memory update request for ID: ${id}`)
// Parse request body
const body = await request.json()
const { data, workflowId } = body
if (!data) {
logger.warn(`[${requestId}] Missing required field: data`)
return NextResponse.json(
@@ -209,7 +184,7 @@ export async function PUT(
{ status: 400 }
)
}
if (!workflowId) {
logger.warn(`[${requestId}] Missing required field: workflowId`)
return NextResponse.json(
@@ -222,20 +197,14 @@ export async function PUT(
{ status: 400 }
)
}
// Verify memory exists before attempting to update
const existingMemories = await db
.select()
.from(memory)
.where(
and(
eq(memory.key, id),
eq(memory.workflowId, workflowId),
isNull(memory.deletedAt)
)
)
.where(and(eq(memory.key, id), eq(memory.workflowId, workflowId), isNull(memory.deletedAt)))
.limit(1)
if (existingMemories.length === 0) {
logger.warn(`[${requestId}] Memory not found: ${id} for workflow: ${workflowId}`)
return NextResponse.json(
@@ -248,9 +217,9 @@ export async function PUT(
{ status: 404 }
)
}
const existingMemory = existingMemories[0]
// Validate memory data based on the existing memory type
if (existingMemory.type === 'agent') {
if (!data.role || !data.content) {
@@ -265,7 +234,7 @@ export async function PUT(
{ status: 400 }
)
}
if (!['user', 'assistant', 'system'].includes(data.role)) {
logger.warn(`[${requestId}] Invalid agent role: ${data.role}`)
return NextResponse.json(
@@ -279,33 +248,23 @@ export async function PUT(
)
}
}
// Update the memory with new data
await db
.update(memory)
.set({
.set({
data,
updatedAt: new Date()
updatedAt: new Date(),
})
.where(
and(
eq(memory.key, id),
eq(memory.workflowId, workflowId)
)
)
.where(and(eq(memory.key, id), eq(memory.workflowId, workflowId)))
// Fetch the updated memory
const updatedMemories = await db
.select()
.from(memory)
.where(
and(
eq(memory.key, id),
eq(memory.workflowId, workflowId)
)
)
.where(and(eq(memory.key, id), eq(memory.workflowId, workflowId)))
.limit(1)
logger.info(`[${requestId}] Memory updated successfully: ${id} for workflow: ${workflowId}`)
return NextResponse.json(
{
@@ -314,7 +273,6 @@ export async function PUT(
},
{ status: 200 }
)
} catch (error: any) {
return NextResponse.json(
{
@@ -326,4 +284,4 @@ export async function PUT(
{ status: 500 }
)
}
}
}

View File

@@ -1,8 +1,8 @@
import { NextRequest, NextResponse } from 'next/server'
import { and, eq, like, isNull } from 'drizzle-orm'
import { and, eq, isNull, like } from 'drizzle-orm'
import { type NextRequest, NextResponse } from 'next/server'
import { createLogger } from '@/lib/logs/console-logger'
import { db } from '@/db'
import { memory } from '@/db/schema'
import { createLogger } from '@/lib/logs/console-logger'
const logger = createLogger('MemoryAPI')
@@ -19,17 +19,17 @@ export const runtime = 'nodejs'
*/
export async function GET(request: NextRequest) {
const requestId = crypto.randomUUID().slice(0, 8)
try {
logger.info(`[${requestId}] Processing memory search request`)
// Extract workflowId from query parameters
const url = new URL(request.url)
const workflowId = url.searchParams.get('workflowId')
const searchQuery = url.searchParams.get('query')
const type = url.searchParams.get('type')
const limit = parseInt(url.searchParams.get('limit') || '50')
const limit = Number.parseInt(url.searchParams.get('limit') || '50')
// Require workflowId for security
if (!workflowId) {
logger.warn(`[${requestId}] Missing required parameter: workflowId`)
@@ -43,26 +43,26 @@ export async function GET(request: NextRequest) {
{ status: 400 }
)
}
// Build query conditions
const conditions = []
// Only include non-deleted memories
conditions.push(isNull(memory.deletedAt))
// Filter by workflow ID (required)
conditions.push(eq(memory.workflowId, workflowId))
// Add type filter if provided
if (type) {
conditions.push(eq(memory.type, type))
}
// Add search query if provided (leverages index on key field)
if (searchQuery) {
conditions.push(like(memory.key, `%${searchQuery}%`))
}
// Execute the query
const memories = await db
.select()
@@ -70,16 +70,15 @@ export async function GET(request: NextRequest) {
.where(and(...conditions))
.orderBy(memory.createdAt)
.limit(limit)
logger.info(`[${requestId}] Found ${memories.length} memories for workflow: ${workflowId}`)
return NextResponse.json(
{
success: true,
data: { memories }
data: { memories },
},
{ status: 200 }
)
} catch (error: any) {
return NextResponse.json(
{
@@ -103,14 +102,14 @@ export async function GET(request: NextRequest) {
*/
export async function POST(request: NextRequest) {
const requestId = crypto.randomUUID().slice(0, 8)
try {
logger.info(`[${requestId}] Processing memory creation request`)
// Parse request body
const body = await request.json()
const { key, type, data, workflowId } = body
// Validate required fields
if (!key) {
logger.warn(`[${requestId}] Missing required field: key`)
@@ -124,7 +123,7 @@ export async function POST(request: NextRequest) {
{ status: 400 }
)
}
if (!type || !['agent', 'raw'].includes(type)) {
logger.warn(`[${requestId}] Invalid memory type: ${type}`)
return NextResponse.json(
@@ -137,7 +136,7 @@ export async function POST(request: NextRequest) {
{ status: 400 }
)
}
if (!data) {
logger.warn(`[${requestId}] Missing required field: data`)
return NextResponse.json(
@@ -150,7 +149,7 @@ export async function POST(request: NextRequest) {
{ status: 400 }
)
}
if (!workflowId) {
logger.warn(`[${requestId}] Missing required field: workflowId`)
return NextResponse.json(
@@ -163,7 +162,7 @@ export async function POST(request: NextRequest) {
{ status: 400 }
)
}
// Additional validation for agent type
if (type === 'agent') {
if (!data.role || !data.content) {
@@ -178,7 +177,7 @@ export async function POST(request: NextRequest) {
{ status: 400 }
)
}
if (!['user', 'assistant', 'system'].includes(data.role)) {
logger.warn(`[${requestId}] Invalid agent role: ${data.role}`)
return NextResponse.json(
@@ -192,28 +191,24 @@ export async function POST(request: NextRequest) {
)
}
}
// Check if memory with the same key already exists for this workflow
const existingMemory = await db
.select()
.from(memory)
.where(
and(
eq(memory.key, key),
eq(memory.workflowId, workflowId),
isNull(memory.deletedAt)
)
)
.where(and(eq(memory.key, key), eq(memory.workflowId, workflowId), isNull(memory.deletedAt)))
.limit(1)
let statusCode = 201 // Default status code for new memory
if (existingMemory.length > 0) {
logger.info(`[${requestId}] Memory with key ${key} exists, checking if we can append`)
// Check if types match
if (existingMemory[0].type !== type) {
logger.warn(`[${requestId}] Memory type mismatch: existing=${existingMemory[0].type}, new=${type}`)
logger.warn(
`[${requestId}] Memory type mismatch: existing=${existingMemory[0].type}, new=${type}`
)
return NextResponse.json(
{
success: false,
@@ -224,47 +219,42 @@ export async function POST(request: NextRequest) {
{ status: 400 }
)
}
// Handle appending based on memory type
let updatedData;
let updatedData
if (type === 'agent') {
// For agent type
const newMessage = data;
const existingData = existingMemory[0].data;
const newMessage = data
const existingData = existingMemory[0].data
// If existing data is an array, append to it
if (Array.isArray(existingData)) {
updatedData = [...existingData, newMessage];
}
updatedData = [...existingData, newMessage]
}
// If existing data is a single message object, convert to array
else {
updatedData = [existingData, newMessage];
updatedData = [existingData, newMessage]
}
} else {
// For raw type
// Merge objects if they're objects, otherwise use the new data
if (typeof existingMemory[0].data === 'object' && typeof data === 'object') {
updatedData = { ...existingMemory[0].data, ...data };
updatedData = { ...existingMemory[0].data, ...data }
} else {
updatedData = data;
updatedData = data
}
}
// Update the existing memory with appended data
await db
.update(memory)
.set({
.set({
data: updatedData,
updatedAt: new Date()
updatedAt: new Date(),
})
.where(
and(
eq(memory.key, key),
eq(memory.workflowId, workflowId)
)
)
.where(and(eq(memory.key, key), eq(memory.workflowId, workflowId)))
statusCode = 200 // Status code for updated memory
} else {
// Insert the new memory
@@ -273,28 +263,22 @@ export async function POST(request: NextRequest) {
workflowId,
key,
type,
data: type === 'agent' ? Array.isArray(data) ? data : [data] : data,
data: type === 'agent' ? (Array.isArray(data) ? data : [data]) : data,
createdAt: new Date(),
updatedAt: new Date()
updatedAt: new Date(),
}
await db.insert(memory).values(newMemory)
logger.info(`[${requestId}] Memory created successfully: ${key} for workflow: ${workflowId}`)
}
// Fetch all memories with the same key for this workflow to return the complete list
const allMemories = await db
.select()
.from(memory)
.where(
and(
eq(memory.key, key),
eq(memory.workflowId, workflowId),
isNull(memory.deletedAt)
)
)
.where(and(eq(memory.key, key), eq(memory.workflowId, workflowId), isNull(memory.deletedAt)))
.orderBy(memory.createdAt)
if (allMemories.length === 0) {
// This shouldn't happen but handle it just in case
logger.warn(`[${requestId}] No memories found after creating/updating memory: ${key}`)
@@ -308,19 +292,18 @@ export async function POST(request: NextRequest) {
{ status: 500 }
)
}
// Get the memory object to return
const memoryRecord = allMemories[0];
const memoryRecord = allMemories[0]
logger.info(`[${requestId}] Memory operation successful: ${key} for workflow: ${workflowId}`)
return NextResponse.json(
{
success: true,
data: memoryRecord
data: memoryRecord,
},
{ status: statusCode }
)
} catch (error: any) {
// Handle unique constraint violation
if (error.code === '23505') {
@@ -335,7 +318,7 @@ export async function POST(request: NextRequest) {
{ status: 409 }
)
}
return NextResponse.json(
{
success: false,
@@ -346,4 +329,4 @@ export async function POST(request: NextRequest) {
{ status: 500 }
)
}
}
}