1
/** @jest-environment node */
3
import { v4 as uuidv4 } from "uuid";
5
import { makeAPICall, pruneDatabase } from "@/src/__tests__/test-utils";
6
import { ModelUsageUnit } from "@/src/constants";
7
import { prisma } from "@/src/server/db";
9
describe("/api/public/generations API Endpoint", () => {
10
beforeEach(async () => await pruneDatabase());
18
unit: ModelUsageUnit.Characters,
20
expectedUnit: ModelUsageUnit.Characters,
21
expectedPromptTokens: 100,
22
expectedCompletionTokens: 200,
23
expectedTotalTokens: 100,
28
unit: ModelUsageUnit.Characters,
30
expectedUnit: ModelUsageUnit.Characters,
31
expectedPromptTokens: 0,
32
expectedCompletionTokens: 0,
33
expectedTotalTokens: 100,
40
expectedPromptTokens: 0,
41
expectedCompletionTokens: 0,
42
expectedTotalTokens: 100,
47
completionTokens: 200,
50
expectedPromptTokens: 100,
51
expectedCompletionTokens: 200,
52
expectedTotalTokens: 100,
53
expectedUnit: ModelUsageUnit.Tokens,
59
expectedPromptTokens: 0,
60
expectedCompletionTokens: 0,
61
expectedTotalTokens: 100,
62
expectedUnit: ModelUsageUnit.Tokens,
66
expectedPromptTokens: 0,
67
expectedCompletionTokens: 0,
68
expectedTotalTokens: 0,
73
expectedPromptTokens: 0,
74
expectedCompletionTokens: 0,
75
expectedTotalTokens: 0,
80
expectedPromptTokens: 0,
81
expectedCompletionTokens: 0,
82
expectedTotalTokens: 0,
85
].forEach((testConfig) => {
86
it(`should create generation after trace 1 ${JSON.stringify(
89
await pruneDatabase();
91
const traceId = uuidv4();
93
await makeAPICall("POST", "/api/public/traces", {
97
projectId: "7a88fb47-b4e2-43b8-a06c-a5ce950dc53a",
98
metadata: { key: "value" },
103
const dbTrace = await prisma.trace.findMany({
109
expect(dbTrace.length).toBeGreaterThan(0);
110
expect(dbTrace[0]?.id).toBe(traceId);
112
const generationId = uuidv4();
113
const createGeneration = await makeAPICall(
115
"/api/public/generations",
119
name: "generation-name",
120
startTime: "2021-01-01T00:00:00.000Z",
121
endTime: "2021-01-01T00:00:00.000Z",
123
modelParameters: { key: "value" },
124
prompt: { key: "value" },
125
metadata: { key: "value" },
127
usage: testConfig.usage,
131
expect(createGeneration.status).toBe(200);
132
const dbGeneration = await prisma.observation.findUnique({
138
expect(dbGeneration?.id).toBe(generationId);
139
expect(dbGeneration?.traceId).toBe(traceId);
140
expect(dbGeneration?.name).toBe("generation-name");
141
expect(dbGeneration?.startTime).toEqual(
142
new Date("2021-01-01T00:00:00.000Z"),
144
expect(dbGeneration?.endTime).toEqual(
145
new Date("2021-01-01T00:00:00.000Z"),
147
expect(dbGeneration?.model).toBe("model-name");
148
expect(dbGeneration?.modelParameters).toEqual({ key: "value" });
149
expect(dbGeneration?.input).toEqual({ key: "value" });
150
expect(dbGeneration?.metadata).toEqual({ key: "value" });
151
expect(dbGeneration?.version).toBe("2.0.0");
152
expect(dbGeneration?.unit).toBe(testConfig.expectedUnit);
153
expect(dbGeneration?.promptTokens).toBe(testConfig.expectedPromptTokens);
154
expect(dbGeneration?.completionTokens).toBe(
155
testConfig.expectedCompletionTokens,
157
expect(dbGeneration?.totalTokens).toBe(testConfig.expectedTotalTokens);
161
it("should create generation before trace", async () => {
162
await pruneDatabase();
164
const traceId = uuidv4();
166
const generationId = uuidv4();
167
const createGeneration = await makeAPICall(
169
"/api/public/generations",
173
name: "generation-name",
174
startTime: "2021-01-01T00:00:00.000Z",
175
endTime: "2021-01-01T00:00:00.000Z",
177
modelParameters: { key: "value" },
178
prompt: { key: "value" },
179
metadata: { key: "value" },
184
expect(createGeneration.status).toBe(200);
185
const dbGeneration = await prisma.observation.findUnique({
191
expect(dbGeneration?.id).toBe(generationId);
192
expect(dbGeneration?.traceId).toBe(traceId);
193
expect(dbGeneration?.name).toBe("generation-name");
194
expect(dbGeneration?.startTime).toEqual(
195
new Date("2021-01-01T00:00:00.000Z"),
197
expect(dbGeneration?.endTime).toEqual(new Date("2021-01-01T00:00:00.000Z"));
198
expect(dbGeneration?.model).toBe("model-name");
199
expect(dbGeneration?.modelParameters).toEqual({ key: "value" });
200
expect(dbGeneration?.input).toEqual({ key: "value" });
201
expect(dbGeneration?.metadata).toEqual({ key: "value" });
202
expect(dbGeneration?.version).toBe("2.0.0");
204
await makeAPICall("POST", "/api/public/traces", {
208
projectId: "7a88fb47-b4e2-43b8-a06c-a5ce950dc53a",
209
metadata: { key: "value" },
214
const dbTrace = await prisma.trace.findMany({
220
expect(dbTrace.length).toBeGreaterThan(0);
221
expect(dbTrace[0]?.id).toBe(traceId);
224
it("should create generation after trace ignoring externalId", async () => {
225
const traceId = uuidv4();
227
const response = await makeAPICall("POST", "/api/public/traces", {
231
projectId: "7a88fb47-b4e2-43b8-a06c-a5ce950dc53a",
232
metadata: { key: "value" },
237
expect(response.status).toBe(200);
239
const dbTrace = await prisma.trace.findMany({
245
expect(dbTrace.length).toBeGreaterThan(0);
246
expect(dbTrace[0]?.externalId).toBeNull();
247
expect(dbTrace[0]?.id).not.toBe(traceId);
249
const generationId = uuidv4();
250
const createGeneration = await makeAPICall(
252
"/api/public/generations",
255
traceIdType: "EXTERNAL",
257
name: "generation-name",
258
startTime: "2021-01-01T00:00:00.000Z",
259
endTime: "2021-01-01T00:00:00.000Z",
261
modelParameters: { key: "value" },
262
prompt: { key: "value" },
263
metadata: { key: "value" },
268
expect(createGeneration.status).toBe(200);
269
const dbGeneration = await prisma.observation.findUnique({
275
expect(dbGeneration?.id).toBe(generationId);
276
expect(dbGeneration?.traceId).toBe(traceId);
277
expect(dbGeneration?.name).toBe("generation-name");
278
expect(dbGeneration?.startTime).toEqual(
279
new Date("2021-01-01T00:00:00.000Z"),
281
expect(dbGeneration?.endTime).toEqual(new Date("2021-01-01T00:00:00.000Z"));
282
expect(dbGeneration?.model).toBe("model-name");
283
expect(dbGeneration?.modelParameters).toEqual({ key: "value" });
284
expect(dbGeneration?.input).toEqual({ key: "value" });
285
expect(dbGeneration?.metadata).toEqual({ key: "value" });
286
expect(dbGeneration?.version).toBe("2.0.0");
289
it("should create trace when creating generation without existing trace", async () => {
290
const generationName = uuidv4();
292
const generationId = uuidv4();
293
const createGeneration = await makeAPICall(
295
"/api/public/generations",
298
name: generationName,
299
startTime: "2021-01-01T00:00:00.000Z",
300
endTime: "2021-01-01T00:00:00.000Z",
302
modelParameters: { key: "value" },
303
prompt: { key: "value" },
304
metadata: { key: "value" },
309
const dbTrace = await prisma.trace.findMany({
311
name: generationName,
315
expect(dbTrace.length).toBe(1);
316
expect(dbTrace[0]?.name).toBe(generationName);
318
expect(createGeneration.status).toBe(200);
319
const dbGeneration = await prisma.observation.findUnique({
325
expect(dbGeneration?.id).toBe(generationId);
326
expect(dbGeneration?.traceId).toBe(dbTrace[0]?.id);
327
expect(dbGeneration?.name).toBe(generationName);
328
expect(dbGeneration?.startTime).toEqual(
329
new Date("2021-01-01T00:00:00.000Z"),
331
expect(dbGeneration?.endTime).toEqual(new Date("2021-01-01T00:00:00.000Z"));
332
expect(dbGeneration?.model).toBe("model-name");
333
expect(dbGeneration?.modelParameters).toEqual({ key: "value" });
334
expect(dbGeneration?.input).toEqual({ key: "value" });
335
expect(dbGeneration?.metadata).toEqual({ key: "value" });
336
expect(dbGeneration?.version).toBe("2.0.0");
339
it("should create nested generations", async () => {
340
const generationName = uuidv4();
342
const generationId = uuidv4();
343
const createGeneration = await makeAPICall(
345
"/api/public/generations",
348
name: generationName,
349
startTime: "2021-01-01T00:00:00.000Z",
350
endTime: "2021-01-01T00:00:00.000Z",
352
modelParameters: { key: "value" },
353
prompt: { key: "value" },
354
metadata: { key: "value" },
359
expect(createGeneration.status).toBe(200);
360
const dbGeneration = await prisma.observation.findUnique({
366
expect(dbGeneration?.id).toBe(generationId);
368
const generationId2 = uuidv4();
369
const generationName2 = uuidv4();
371
const createGeneration2 = await makeAPICall(
373
"/api/public/generations",
376
name: generationName2,
377
startTime: "2021-01-01T00:00:00.000Z",
378
endTime: "2021-01-01T00:00:00.000Z",
380
modelParameters: { key: "value" },
381
prompt: { key: "value" },
382
metadata: { key: "value" },
384
parentObservationId: generationId,
387
expect(createGeneration2.status).toBe(200);
389
const dbGeneration2 = await prisma.observation.findUnique({
395
expect(dbGeneration2?.id).toBe(generationId2);
396
expect(dbGeneration2?.parentObservationId).toBe(generationId);
399
it("should not create trace when creating generation without existing trace with externalId", async () => {
400
const generationName = uuidv4();
402
const generationId = uuidv4();
403
const externalTraceId = uuidv4();
404
const createGeneration = await makeAPICall(
406
"/api/public/generations",
409
traceIdType: "EXTERNAL",
410
traceId: externalTraceId,
411
name: generationName,
412
startTime: "2021-01-01T00:00:00.000Z",
413
endTime: "2021-01-01T00:00:00.000Z",
415
modelParameters: { key: "value" },
416
prompt: { key: "value" },
417
metadata: { key: "value" },
422
expect(createGeneration.status).toBe(200);
424
const dbGeneration = await prisma.observation.findFirstOrThrow({
426
name: generationName,
429
expect(dbGeneration.id).toBe(generationId);
430
expect(dbGeneration.traceId).toBe(externalTraceId);
432
const dbTraces = await prisma.trace.findMany();
433
expect(dbTraces.length).toBe(0);
436
it("should create trace when creating generation without existing trace without traceId", async () => {
437
const generationName = uuidv4();
439
const generationId = uuidv4();
440
const createGeneration = await makeAPICall(
442
"/api/public/generations",
445
name: generationName,
446
startTime: "2021-01-01T00:00:00.000Z",
447
endTime: "2021-01-01T00:00:00.000Z",
449
modelParameters: { key: "value" },
450
prompt: { key: "value" },
451
metadata: { key: "value" },
456
const dbGeneration = await prisma.observation.findFirstOrThrow({
458
name: generationName,
462
const dbTrace = await prisma.trace.findMany({
464
id: dbGeneration.traceId!,
468
expect(dbTrace.length).toBe(1);
469
expect(dbTrace[0]?.name).toBe(generationName);
471
expect(createGeneration.status).toBe(200);
473
expect(dbGeneration.id).toBe(generationId);
474
expect(dbGeneration.traceId).toBe(dbTrace[0]?.id);
475
expect(dbGeneration.name).toBe(generationName);
476
expect(dbGeneration.startTime).toEqual(
477
new Date("2021-01-01T00:00:00.000Z"),
479
expect(dbGeneration.endTime).toEqual(new Date("2021-01-01T00:00:00.000Z"));
480
expect(dbGeneration.model).toBe("model-name");
481
expect(dbGeneration.modelParameters).toEqual({ key: "value" });
482
expect(dbGeneration.input).toEqual({ key: "value" });
483
expect(dbGeneration.metadata).toEqual({ key: "value" });
484
expect(dbGeneration.version).toBe("2.0.0");
487
it("should update generation", async () => {
488
const generationName = uuidv4();
490
const generationId = uuidv4();
491
const createGeneration = await makeAPICall(
493
"/api/public/generations",
496
name: generationName,
497
startTime: "2021-01-01T00:00:00.000Z",
498
endTime: "2021-01-01T00:00:00.000Z",
500
modelParameters: { key: "value" },
501
prompt: { key: "value" },
502
metadata: { key: "value" },
507
expect(createGeneration.status).toBe(200);
509
const updateGeneration = await makeAPICall(
511
"/api/public/generations",
513
generationId: generationId,
514
completion: "this is a great gpt response",
517
expect(updateGeneration.status).toBe(200);
519
const dbGeneration = await prisma.observation.findUnique({
525
expect(dbGeneration?.id).toBe(generationId);
526
expect(dbGeneration?.name).toBe(generationName);
527
expect(dbGeneration?.startTime).toEqual(
528
new Date("2021-01-01T00:00:00.000Z"),
530
expect(dbGeneration?.endTime).toEqual(new Date("2021-01-01T00:00:00.000Z"));
531
expect(dbGeneration?.model).toBe("model-name");
532
expect(dbGeneration?.modelParameters).toEqual({ key: "value" });
533
expect(dbGeneration?.input).toEqual({ key: "value" });
534
expect(dbGeneration?.output).toEqual("this is a great gpt response");
535
expect(dbGeneration?.metadata).toEqual({ key: "value" });
536
expect(dbGeneration?.version).toBe("2.0.0");
539
it("should accept objects as i/o", async () => {
540
const generationName = uuidv4();
542
const generationId = uuidv4();
543
const createGeneration = await makeAPICall(
545
"/api/public/generations",
548
name: generationName,
549
prompt: { key: "value" },
557
tags: ["example tag", "second tag"],
563
expect(createGeneration.status).toBe(200);
565
const dbGeneration = await prisma.observation.findUnique({
571
expect(dbGeneration?.id).toBe(generationId);
572
expect(dbGeneration?.name).toBe(generationName);
573
expect(dbGeneration?.input).toEqual({ key: "value" });
574
expect(dbGeneration?.output).toEqual([
579
expect(dbGeneration?.metadata).toEqual([
581
tags: ["example tag", "second tag"],
586
it("should not succeed update if generation does not exist", async () => {
587
const generationId = uuidv4();
589
const updateGeneration = await makeAPICall(
591
"/api/public/generations",
593
generationId: generationId,
594
completion: "this is a great gpt response",
597
expect(updateGeneration.status).toBe(404);
599
const dbGeneration = await prisma.observation.findUnique({
605
expect(dbGeneration).toBeNull();