Memory System

Prisma Setup
Liz uses Prisma as its ORM, supporting both SQLite and PostgreSQL databases. The schema defines the structure for storing memories and tweets.
// prisma/schema.prisma
datasource db {
  provider = "sqlite" // or "postgresql"
  url      = env("DATABASE_URL")
}
generator client {
  provider = "prisma-client-js"
}
model Memory {
  id          String   @id @default(uuid())
  userId      String
  agentId     String
  roomId      String
  content     String   // Stores JSON as string
  type        String
  generator   String   // "llm" or "external"
  createdAt   DateTime @default(now())
  @@index([roomId])
  @@index([userId, agentId])
  @@index([type])
}
model Tweet {
  id             String    @id
  text           String
  userId         String
  username       String
  conversationId String?
  inReplyToId    String?
  createdAt      DateTime  @default(now())
  permanentUrl   String?
  @@index([userId])
  @@index([conversationId])
}Loading Memories
The loadMemories middleware retrieves relevant conversation history for each request:
// src/middleware/load-memories.ts
export function createLoadMemoriesMiddleware(
	options: LoadMemoriesOptions = {}
): AgentMiddleware {
	const { limit = 100 } = options;
	return async (req, res, next) => {
		const memories = await prisma.memory.findMany({
			where: {
				userId: req.input.userId,
			},
			orderBy: {
				createdAt: "desc",
			},
			take: limit,
		});
		req.memories = memories.map((memory) => ({
			id: memory.id,
			userId: memory.userId,
			agentId: memory.agentId,
			roomId: memory.roomId,
			type: memory.type,
			createdAt: memory.createdAt,
			generator: memory.generator,
			content: JSON.parse(memory.content),
		}));
		await next();
	};
}Creating Memories
The createMemoryFromInput middleware stores new interactions in the database:
// src/middleware/create-memory.ts
export const createMemoryFromInput: AgentMiddleware = async (
	req,
	res,
	next
) => {
	await prisma.memory.create({
		data: {
			userId: req.input.userId,
			agentId: req.input.agentId,
			roomId: req.input.roomId,
			type: req.input.type,
			generator: "external",
			content: JSON.stringify(req.input),
		},
	});
	await next();
};
// Creating LLM response memories
await prisma.memory.create({
	data: {
		userId: req.input.userId,
		agentId: req.input.agentId,
		roomId: req.input.roomId,
		type: "agent",
		generator: "llm",
		content: JSON.stringify({ text: response }),
	},
});Memory Context
The wrapContext middleware formats memories into a structured context for LLM interactions:
// src/middleware/wrap-context.ts
function formatMemories(memories: Memory[]): string {
  return memories
    .reverse()
    .map((memory) => {
      const content = memory.content;
      if (memory.generator === "external") {
        return `[${memory.createdAt}] User ${memory.userId}: ${content.text}`;
      } else if (memory.generator === "llm") {
        return `[${memory.createdAt}] You: ${content.text}`;
      }
    })
    .join("\n\n");
}
// Final context structure
<PREVIOUS_CONVERSATION>
${memories}
</PREVIOUS_CONVERSATION>
<AGENT_CONTEXT>
${agentContext}
</AGENT_CONTEXT>
<CURRENT_USER_INPUT>
${currentInput}
</CURRENT_USER_INPUT>Performance Considerations
Memory Limits
Default limit of 100 recent memories
Configurable through middleware options
Consider token limits of your LLM
Use indexes for faster queries
Database Tips
SQLite for development/small apps
PostgreSQL for production/scale
Regular database maintenance
Monitor memory table growth
Last updated