with-inngest-structured
A complete Next.js chat application that demonstrates Recall Structured with Inngest for schema-based memory extraction and CRUD operations.
Features
Section titled “Features”- Chat interface with OpenAI GPT
- Schema-based memory with Zod validation
- Full CRUD operations (insert, update, delete, query)
- Multi-hop agent for complex operations
- Two-phase architecture (extraction + consolidation)
- SQLite persistence
Prerequisites
Section titled “Prerequisites”- Node.js 18+
- pnpm
- OpenAI API key
- Inngest CLI (for local development)
1. Install dependencies
Section titled “1. Install dependencies”cd examples/with-inngest-structuredpnpm install2. Configure environment
Section titled “2. Configure environment”cp .env.example .envAdd your OpenAI API key to .env:
OPENAI_API_KEY=sk-proj-your-key-here3. Start the development server
Section titled “3. Start the development server”In one terminal, start the Next.js app:
pnpm devIn another terminal, start the Inngest dev server:
pnpm dev:inngest4. Open the app
Section titled “4. Open the app”Visit http://localhost:3000
Project Structure
Section titled “Project Structure”src/├── app/│ ├── api/│ │ ├── chat/ # Chat endpoint│ │ ├── inngest/ # Inngest webhook│ │ └── records/ # List records endpoint│ └── page.tsx├── components/│ ├── chat-interface.tsx│ └── records-panel.tsx└── lib/ ├── structured-memory.ts # Schema definitions └── inngest/ ├── client.ts └── functions.ts # Two-phase processingHow It Works
Section titled “How It Works”Define Schemas
Section titled “Define Schemas”import { createStructuredMemory } from '@youcraft/recall-structured'import { z } from 'zod'
export const structuredMemory = createStructuredMemory({ db: 'structured.db', llm: { provider: 'openai', apiKey: process.env.OPENAI_API_KEY }, schemas: { payments: { description: 'Financial transactions and payments made by the user', schema: z.object({ recipient: z.string().describe('Who received the payment'), amount: z.number().describe('Payment amount in dollars'), description: z.string().optional().describe('What the payment was for'), }), }, workouts: { description: 'Exercise and fitness activities', schema: z.object({ type: z.string().describe('Type of workout'), duration: z.number().optional().describe('Duration in minutes'), notes: z.string().optional(), }), }, },})Two-Phase Processing
Section titled “Two-Phase Processing”The Inngest function implements a two-phase approach:
export const processStructuredMemory = inngest.createFunction( { id: 'process-structured-memory' }, { event: 'chat/message.completed' }, async ({ event, step }) => { const { userId, message } = event.data
// PHASE 1: Extract structured data const extraction = await step.run('extract', async () => { return structuredMemory.process(message, { userId }) })
// For INSERT/QUERY - extraction handles it if (extraction.action === 'insert' || extraction.action === 'query') { return extraction }
// PHASE 2: For UPDATE/DELETE - use multi-hop agent if (extraction.action === 'update' || extraction.action === 'delete') { const agent = createStructuredMemoryAgent({ structuredMemory }) const result = await step.run('agent-process', async () => { return agent.process(model, message, { userId, extractedContext: extraction, }) }) return result } })Example Interactions
Section titled “Example Interactions”Insert:
User: "Paid Jayden $150 for MMA training"→ Detects: payments schema, insert action→ Extracts: { recipient: "Jayden", amount: 150, description: "MMA training" }→ Inserts recordUpdate:
User: "Actually, change that payment to $200"→ Detects: payments schema, update action→ Agent searches for recent payment→ Updates amount to 200Query:
User: "How much have I paid Jayden total?"→ Detects: payments schema, query action→ Runs SQL aggregation→ Returns: "You've paid Jayden $350 total"Delete:
User: "Delete my last workout"→ Detects: workouts schema, delete action→ Agent finds most recent workout→ Deletes the record