diff --git a/app/prisma/schema.prisma b/app/prisma/schema.prisma index 32ce994..9c5c92e 100644 --- a/app/prisma/schema.prisma +++ b/app/prisma/schema.prisma @@ -254,14 +254,53 @@ model WorldChampEntrant { model LoggedCall { id String @id @default(uuid()) @db.Uuid + startTime DateTime + + // True if this call was served from the cache, false otherwise + cacheHit Boolean + + // A LoggedCall is always associated with a LoggedCallModelResponse. If this + // is a cache miss, it's a new LoggedCallModelResponse we created for this. + // If it's a cache hit, it's the existing LoggedCallModelResponse we served. + modelResponseId String @db.Uuid + modelResponse LoggedCallModelResponse @relation(fields: [modelResponseId], references: [id], onDelete: Cascade) + + // The response created by this LoggedCall. Will be null if this LoggedCall is a cache hit. + createdResponse LoggedCallModelResponse[] @relation(name: "ModelResponseCreatedBy") + + organizationId String @db.Uuid + organization Organization? @relation(fields: [organizationId], references: [id], onDelete: Cascade) + + tags LoggedCallTag[] + + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + @@index([startTime]) +} + +model LoggedCallModelResponse { + id String @id @default(uuid()) @db.Uuid + + reqPayload Json + + // The HTTP status returned by the model provider + respStatus Int? + respPayload Json? + + // Should be null if the request was successful, and some string if the request failed. + error String? + startTime DateTime endTime DateTime - reqPayload Json - respPayload Json? - respStatus Int? - error String? + // Note: the function to calculate the cacheKey should include the project + // ID so we don't share cached responses between projects, which could be an + // attack vector. Also, we should only set the cacheKey on the model if the + // request was successful. + cacheKey String? + // Derived fields durationMs Int? inputTokens Int? outputTokens Int? @@ -269,19 +308,15 @@ model LoggedCall { completionId String? totalCost Decimal? @db.Decimal(18, 12) - cacheParentId String? @db.Uuid - cacheParent LoggedCall? @relation(name: "CacheParentChild", fields: [cacheParentId], references: [id], onDelete: Cascade) + // The LoggedCall that created this LoggedCallModelResponse + createdById String @unique @db.Uuid + createdBy LoggedCall @relation(name: "ModelResponseCreatedBy", fields: [createdById], references: [id], onDelete: Cascade) - organizationId String @db.Uuid - organization Organization? @relation(fields: [organizationId], references: [id], onDelete: Cascade) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + loggedCalls LoggedCall[] - tags LoggedCallTag[] - cacheChildren LoggedCall[] @relation(name: "CacheParentChild") - - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - - @@index([startTime]) + @@index([cacheKey]) } model LoggedCallTag { diff --git a/app/src/server/scripts/test-queries.ts b/app/src/server/scripts/test-queries.ts new file mode 100644 index 0000000..e69d230 --- /dev/null +++ b/app/src/server/scripts/test-queries.ts @@ -0,0 +1,63 @@ +import dayjs from "dayjs"; +import { prisma } from "../db"; + +const projectId = "1234"; + +// Find all calls in the last 24 hours +const responses = await prisma.loggedCall.findMany({ + where: { + organizationId: projectId, + startTime: { + gt: dayjs() + .subtract(24 * 3600) + .toDate(), + }, + }, + include: { + modelResponse: true, + }, + orderBy: { + startTime: "desc", + }, +}); + +// Find all calls in the last 24 hours with promptId 'hello-world' +const helloWorld = await prisma.loggedCall.findMany({ + where: { + organizationId: projectId, + startTime: { + gt: dayjs() + .subtract(24 * 3600) + .toDate(), + }, + tags: { + some: { + name: "promptId", + value: "hello-world", + }, + }, + }, + include: { + modelResponse: true, + }, + orderBy: { + startTime: "desc", + }, +}); + +// Total spent on OpenAI in the last month +const totalSpent = await prisma.loggedCallModelResponse.aggregate({ + _sum: { + totalCost: true, + }, + where: { + createdBy: { + organizationId: projectId, + }, + startTime: { + gt: dayjs() + .subtract(30 * 24 * 3600) + .toDate(), + }, + }, +});