Prisma Adapter
Production-ready Prisma adapter for NodeLLM Monitor. Store and query LLM metrics in your database.
Table of contents
- Installation
- Schema Setup
- Basic Usage
- Configuration Options
- Querying Events
- Dashboard Integration
- Best Practices
- Troubleshooting
Installation
npm install @node-llm/monitor @prisma/client prisma
Schema Setup
Add the monitoring events table to your Prisma schema:
// prisma/schema.prisma
model monitoring_events {
id String @id @default(uuid())
eventType String // request.start, request.end, tool.start, etc.
requestId String @map("requestId")
sessionId String? @map("sessionId")
transactionId String? @map("transactionId")
time DateTime @default(now())
duration Int? // duration in ms
cost Float?
cpuTime Float?
gcTime Float?
allocations Int?
payload Json // Stores metadata, tokens and optional content
createdAt DateTime @default(now())
provider String
model String
@@index([requestId])
@@index([sessionId])
@@index([transactionId])
@@index([time])
}
Run the migration:
npx prisma migrate dev --name add_monitoring_events
Note: For non-Prisma users, a raw SQL migration is available at migrations/001_create_monitoring_events.sql.
Basic Usage
import { PrismaClient } from "@prisma/client";
import { createLLM } from "@node-llm/core";
import { createPrismaMonitor } from "@node-llm/monitor";
const prisma = new PrismaClient();
const monitor = createPrismaMonitor(prisma);
const llm = createLLM({
provider: "openai",
model: "gpt-4o-mini",
openaiApiKey: process.env.OPENAI_API_KEY,
middlewares: [monitor],
});
// Now all LLM calls are persisted to your database
const chat = llm.chat();
await chat.ask("What is the weather today?");
Configuration Options
const monitor = createPrismaMonitor(prisma, {
// Capture full request/response content (default: false)
captureContent: true,
// Enable content scrubbing for PII protection
// (automatically enabled when captureContent is true)
scrubbing: {
pii: true, // Scrub emails, phone numbers, SSNs
secrets: true, // Scrub API keys, passwords
},
// Error handling callback
onError: (error, event) => {
console.error("Monitor error:", error, "Event:", event.eventType);
},
});
Querying Events
Since the Prisma Adapter stores data in your database, the best way to query events is using the Prisma Client directly.
Direct Prisma Queries
// Get all events for a specific request
const requestEvents = await prisma.monitoring_events.findMany({
where: { requestId: "req_123" },
orderBy: { time: "asc" },
});
// Calculate total cost for a time period
const result = await prisma.monitoring_events.aggregate({
where: {
eventType: "request.end",
time: {
gte: new Date("2024-01-01"),
lte: new Date("2024-01-31"),
},
},
_sum: { cost: true },
});
console.log(`January cost: $${result._sum.cost}`);
// Get usage by provider
const byProvider = await prisma.monitoring_events.groupBy({
by: ["provider", "model"],
where: { eventType: "request.end" },
_count: true,
_sum: { cost: true, duration: true },
});
Dashboard Integration
To view your Prisma data in the visual dashboard, you can use the ergonomic monitor.api() shorthand for Express-based applications:
import express from "express";
import { PrismaClient } from "@prisma/client";
import { createPrismaMonitor } from "@node-llm/monitor";
const app = express();
const prisma = new PrismaClient();
const monitor = createPrismaMonitor(prisma);
// Dashboard handles its own routing under basePath
app.use(monitor.api({ basePath: "/monitor" }));
app.listen(3001, () => {
console.log("Dashboard at http://localhost:3001/monitor");
});
For non-Express environments or manual routing, you can use the createMonitorMiddleware factory directly:
import { createMonitorMiddleware, PrismaAdapter } from "@node-llm/monitor";
const adapter = new PrismaAdapter(prisma);
app.use(createMonitorMiddleware(adapter, { basePath: "/monitor" }));
The dashboard provides:
- Real-time event stream
- Cost analysis charts
- Provider/model breakdown
- Request detail viewer
- Error tracking
Best Practices
1. Index Optimization
Add indexes for your most common query patterns:
@@index([time, provider])
@@index([sessionId, time])
@@index([eventType, time])
2. Data Retention
Set up a cleanup job for old events:
// Delete events older than 90 days
await prisma.monitoring_events.deleteMany({
where: {
time: {
lt: new Date(Date.now() - 90 * 24 * 60 * 60 * 1000),
},
},
});
3. Separate Database
For high-volume applications, consider a separate database for monitoring:
datasource monitorDb {
provider = "postgresql"
url = env("MONITOR_DATABASE_URL")
}
Troubleshooting
Table Not Found
If you get “monitoring_events table not found”, ensure:
- You’ve added the schema to
prisma/schema.prisma - You’ve run
npx prisma migrate dev - Your Prisma client is regenerated:
npx prisma generate
Type Errors
If using custom table names, ensure your Prisma client types match:
// Generate types after schema changes
npx prisma generate