@luanpoppe/ai 1.1.0 → 1.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -25,12 +25,12 @@ describe("AI E2E Tests", () => {
25
25
 
26
26
  const messages = [
27
27
  AIMessages.human(
28
- "Olá! Responda apenas com 'Teste E2E GPT funcionando'"
28
+ "Olá! Responda apenas com 'Teste E2E GPT funcionando'",
29
29
  ),
30
30
  ];
31
31
 
32
32
  const result = await ai.call({
33
- aiModel: "gpt-4o",
33
+ aiModel: "gpt-5-nano",
34
34
  messages,
35
35
  });
36
36
 
@@ -39,7 +39,7 @@ describe("AI E2E Tests", () => {
39
39
  expect(result.messages).toBeDefined();
40
40
  expect(result.messages.length).toBeGreaterThan(0);
41
41
  expect(result.text.toLowerCase()).toContain("teste");
42
- }
42
+ },
43
43
  );
44
44
 
45
45
  it(
@@ -56,7 +56,7 @@ describe("AI E2E Tests", () => {
56
56
 
57
57
  const messages = [
58
58
  AIMessages.human(
59
- "Olá! Responda apenas com 'Teste E2E Gemini funcionando'"
59
+ "Olá! Responda apenas com 'Teste E2E Gemini funcionando'",
60
60
  ),
61
61
  ];
62
62
 
@@ -70,7 +70,7 @@ describe("AI E2E Tests", () => {
70
70
  expect(result.messages).toBeDefined();
71
71
  expect(result.messages.length).toBeGreaterThan(0);
72
72
  expect(result.text.toLowerCase()).toContain("teste");
73
- }
73
+ },
74
74
  );
75
75
 
76
76
  it(
@@ -87,12 +87,12 @@ describe("AI E2E Tests", () => {
87
87
 
88
88
  const messages = [
89
89
  AIMessages.human(
90
- "Olá! Responda apenas com 'Teste E2E OpenRouter funcionando'"
90
+ "Olá! Responda apenas com 'Teste E2E OpenRouter funcionando'",
91
91
  ),
92
92
  ];
93
93
 
94
94
  const result = await ai.call({
95
- aiModel: "openrouter/openai/gpt-5",
95
+ aiModel: "openrouter/openai/gpt-5-nano",
96
96
  messages,
97
97
  });
98
98
 
@@ -101,7 +101,7 @@ describe("AI E2E Tests", () => {
101
101
  expect(result.messages).toBeDefined();
102
102
  expect(result.messages.length).toBeGreaterThan(0);
103
103
  expect(result.text.toLowerCase()).toContain("teste");
104
- }
104
+ },
105
105
  );
106
106
  });
107
107
 
@@ -119,21 +119,21 @@ describe("AI E2E Tests", () => {
119
119
  });
120
120
 
121
121
  const systemMessage = AIMessages.system(
122
- "Você é um assistente útil que responde de forma concisa."
122
+ "Você é um assistente útil que responde de forma concisa.",
123
123
  );
124
124
  const humanMessage = AIMessages.human("Qual é a capital do Brasil?");
125
125
 
126
126
  const messages = [systemMessage, humanMessage];
127
127
 
128
128
  const result = await ai.call({
129
- aiModel: "gpt-4o",
129
+ aiModel: "gpt-5-nano",
130
130
  messages,
131
131
  });
132
132
 
133
133
  expect(result.text).toBeDefined();
134
134
  expect(result.text.length).toBeGreaterThan(0);
135
135
  expect(result.text.toLowerCase()).toContain("brasília");
136
- }
136
+ },
137
137
  );
138
138
 
139
139
  it(
@@ -161,7 +161,7 @@ describe("AI E2E Tests", () => {
161
161
  expect(result.text).toBeDefined();
162
162
  expect(result.text.length).toBeGreaterThan(0);
163
163
  expect(result.text).toMatch(/\b4\b/);
164
- }
164
+ },
165
165
  );
166
166
 
167
167
  it(
@@ -177,14 +177,14 @@ describe("AI E2E Tests", () => {
177
177
  });
178
178
 
179
179
  const systemMessage = AIMessages.system(
180
- "Você é um assistente útil que responde de forma concisa."
180
+ "Você é um assistente útil que responde de forma concisa.",
181
181
  );
182
182
  const humanMessage = AIMessages.human("Qual é a capital da França?");
183
183
 
184
184
  const messages = [systemMessage, humanMessage];
185
185
 
186
186
  const result = await ai.call({
187
- aiModel: "openrouter/openai/gpt-5",
187
+ aiModel: "openrouter/openai/gpt-5-nano",
188
188
  messages,
189
189
  });
190
190
 
@@ -194,9 +194,9 @@ describe("AI E2E Tests", () => {
194
194
  expect(
195
195
  lowerText.includes("paris") ||
196
196
  lowerText.includes("paris,") ||
197
- lowerText.includes("paris.")
197
+ lowerText.includes("paris."),
198
198
  ).toBe(true);
199
- }
199
+ },
200
200
  );
201
201
  });
202
202
 
@@ -215,7 +215,7 @@ describe("AI E2E Tests", () => {
215
215
  ];
216
216
 
217
217
  const result = await ai.call({
218
- aiModel: "gpt-4o",
218
+ aiModel: "gpt-5-nano",
219
219
  messages,
220
220
  systemPrompt:
221
221
  "Você é um especialista em programação. Responda sempre em português brasileiro.",
@@ -232,7 +232,7 @@ describe("AI E2E Tests", () => {
232
232
  lowerText.includes("code") ||
233
233
  lowerText.includes("software") ||
234
234
  lowerText.includes("linguagem") ||
235
- lowerText.includes("desenvolvimento")
235
+ lowerText.includes("desenvolvimento"),
236
236
  ).toBe(true);
237
237
  });
238
238
 
@@ -253,7 +253,7 @@ describe("AI E2E Tests", () => {
253
253
  ];
254
254
 
255
255
  const result = await ai.call({
256
- aiModel: "openrouter/openai/gpt-5",
256
+ aiModel: "openrouter/openai/gpt-5-nano",
257
257
  messages,
258
258
  systemPrompt:
259
259
  "Você é um cientista. Responda sempre em português brasileiro.",
@@ -303,7 +303,7 @@ describe("AI E2E Tests", () => {
303
303
  // Se não encontrou palavras específicas de ciência, pelo menos verifica que a resposta existe
304
304
  expect(result.text.length).toBeGreaterThan(10);
305
305
  // Se encontrou palavras relacionadas a ciência, ótimo, mas não é obrigatório para passar o teste
306
- }
306
+ },
307
307
  );
308
308
  });
309
309
 
@@ -328,12 +328,12 @@ describe("AI E2E Tests", () => {
328
328
 
329
329
  const messages = [
330
330
  AIMessages.human(
331
- "Crie uma pessoa fictícia. Nome: João, Idade: 30, Cidade: São Paulo"
331
+ "Crie uma pessoa fictícia. Nome: João, Idade: 30, Cidade: São Paulo",
332
332
  ),
333
333
  ];
334
334
 
335
335
  const result = await ai.callStructuredOutput({
336
- aiModel: "gpt-4o",
336
+ aiModel: "gpt-5-nano",
337
337
  messages,
338
338
  outputSchema,
339
339
  });
@@ -342,7 +342,7 @@ describe("AI E2E Tests", () => {
342
342
  expect(result.response.name).toBe("João");
343
343
  expect(result.response.age).toBe(30);
344
344
  expect(result.response.city).toBe("São Paulo");
345
- }
345
+ },
346
346
  );
347
347
 
348
348
  it(
@@ -364,7 +364,7 @@ describe("AI E2E Tests", () => {
364
364
 
365
365
  const messages = [
366
366
  AIMessages.human(
367
- "Calcule a soma e o produto de 5 e 3. A soma de 5 e 3 é 8, e o produto é 15."
367
+ "Calcule a soma e o produto de 5 e 3. A soma de 5 e 3 é 8, e o produto é 15.",
368
368
  ),
369
369
  ];
370
370
 
@@ -380,7 +380,7 @@ describe("AI E2E Tests", () => {
380
380
  expect(result.response.sum).toBeLessThanOrEqual(9);
381
381
  expect(result.response.product).toBeGreaterThanOrEqual(14);
382
382
  expect(result.response.product).toBeLessThanOrEqual(16);
383
- }
383
+ },
384
384
  );
385
385
 
386
386
  it(
@@ -405,7 +405,7 @@ describe("AI E2E Tests", () => {
405
405
  ];
406
406
 
407
407
  const result = await ai.callStructuredOutput({
408
- aiModel: "openrouter/openai/gpt-5",
408
+ aiModel: "openrouter/openai/gpt-5-nano",
409
409
  messages,
410
410
  outputSchema,
411
411
  });
@@ -413,7 +413,7 @@ describe("AI E2E Tests", () => {
413
413
  expect(result.response).toBeDefined();
414
414
  expect(result.response.sum).toBe(11);
415
415
  expect(result.response.product).toBe(28);
416
- }
416
+ },
417
417
  );
418
418
 
419
419
  it(
@@ -441,12 +441,12 @@ describe("AI E2E Tests", () => {
441
441
 
442
442
  const messages = [
443
443
  AIMessages.human(
444
- "Crie um prontuário médico formal para um paciente chamado João, 30 anos, com diagnóstico de gripe. A data da consulta foi 25/01/2026."
444
+ "Crie um prontuário médico formal para um paciente chamado João, 30 anos, com diagnóstico de gripe. A data da consulta foi 25/01/2026.",
445
445
  ),
446
446
  ];
447
447
 
448
448
  const result = await ai.callStructuredOutput({
449
- aiModel: "openrouter/openai/gpt-5",
449
+ aiModel: "openrouter/openai/gpt-5-nano",
450
450
  messages,
451
451
  outputSchema,
452
452
  });
@@ -460,7 +460,7 @@ describe("AI E2E Tests", () => {
460
460
  if (result.response.dataConsulta) {
461
461
  expect(typeof result.response.dataConsulta).toBe("string");
462
462
  }
463
- }
463
+ },
464
464
  );
465
465
  });
466
466
 
@@ -482,7 +482,7 @@ describe("AI E2E Tests", () => {
482
482
  ];
483
483
 
484
484
  const result = await ai.call({
485
- aiModel: "gpt-4o",
485
+ aiModel: "gpt-5-nano",
486
486
  messages,
487
487
  modelConfig: {
488
488
  maxTokens: 50,
@@ -493,7 +493,7 @@ describe("AI E2E Tests", () => {
493
493
  expect(result.text).toBeDefined();
494
494
  // maxTokens pode não ser exatamente respeitado, então verificamos apenas que existe resposta
495
495
  expect(result.text.length).toBeGreaterThan(0);
496
- }
496
+ },
497
497
  );
498
498
 
499
499
  it(
@@ -524,17 +524,104 @@ describe("AI E2E Tests", () => {
524
524
 
525
525
  if (result.text === "Empty response from the model") {
526
526
  throw new Error(
527
- "OpenRouter retornou resposta vazia para maxTokens/temperature"
527
+ "OpenRouter retornou resposta vazia para maxTokens/temperature",
528
528
  );
529
529
  }
530
530
  expect(
531
531
  (result.messages.at(-1)?.response_metadata?.tokenUsage as any)
532
- ?.totalTokens
532
+ ?.totalTokens,
533
533
  ).toBeLessThanOrEqual(maxTokens);
534
534
  expect(result.text).toBeDefined();
535
535
  // maxTokens pode não ser exatamente respeitado, então verificamos apenas que existe resposta
536
536
  expect(result.text.length).toBeGreaterThan(0);
537
- }
537
+ },
538
+ );
539
+ });
540
+
541
+ describe("Persistência de histórico (memory/checkpointer)", () => {
542
+ it(
543
+ "deve manter histórico entre chamadas com threadId e MemorySaver",
544
+ { timeout },
545
+ async () => {
546
+ if (!openAIApiKey) {
547
+ console.log("OPENAI_API_KEY não está configurada");
548
+ return;
549
+ }
550
+ const ai = new AI({
551
+ openAIApiKey: openAIApiKey!,
552
+ memory: { type: "memory" },
553
+ });
554
+
555
+ const threadId = "e2e-memory-thread-1";
556
+
557
+ // Primeira mensagem: informar nome
558
+ await ai.call({
559
+ aiModel: "gpt-5-nano",
560
+ messages: [AIMessages.human("Meu nome é Carlos. Lembre-se disso.")],
561
+ threadId,
562
+ });
563
+
564
+ // Segunda mensagem: perguntar o nome (deve lembrar do contexto)
565
+ const result = await ai.call({
566
+ aiModel: "gpt-5-nano",
567
+ messages: [AIMessages.human("Qual é o meu nome?")],
568
+ threadId,
569
+ });
570
+
571
+ expect(result.text).toBeDefined();
572
+ expect(result.text.toLowerCase()).toContain("carlos");
573
+ },
574
+ );
575
+
576
+ it(
577
+ "deve retornar histórico de mensagens via AIMemory.getHistory",
578
+ { timeout },
579
+ async () => {
580
+ if (!openAIApiKey) {
581
+ console.log("OPENAI_API_KEY não está configurada");
582
+ return;
583
+ }
584
+ const ai = new AI({
585
+ openAIApiKey: openAIApiKey!,
586
+ memory: { type: "memory" },
587
+ });
588
+
589
+ const threadId = "e2e-aimemory-history-thread";
590
+
591
+ // Primeira mensagem
592
+ await ai.call({
593
+ aiModel: "gpt-5-nano",
594
+ messages: [AIMessages.human("Diga apenas: ok, recebi.")],
595
+ threadId,
596
+ });
597
+
598
+ // Segunda mensagem
599
+ await ai.call({
600
+ aiModel: "gpt-5-nano",
601
+ messages: [AIMessages.human("O que eu disse na mensagem anterior?")],
602
+ threadId,
603
+ });
604
+
605
+ // Buscar histórico via ai.memory.getHistory (agent definido em getRawAgent)
606
+
607
+ const { fullHistory, messages } = await ai.memory.getHistory(threadId);
608
+
609
+ console.log({ messages });
610
+
611
+ expect(fullHistory.length).toBeGreaterThan(0);
612
+ expect(messages.length).toBeGreaterThanOrEqual(2);
613
+
614
+ const allContent = messages.map((m) => m.content).join(" ");
615
+ expect(allContent).toContain("Diga apenas");
616
+ expect(allContent).toContain("O que eu disse");
617
+
618
+ expect(
619
+ messages.every((m) => ["human", "ai", "tool"].includes(m.role)),
620
+ ).toBe(true);
621
+ expect(messages.every((m) => typeof m.createdAt === "string")).toBe(
622
+ true,
623
+ );
624
+ },
538
625
  );
539
626
  });
540
627
 
@@ -555,7 +642,7 @@ describe("AI E2E Tests", () => {
555
642
  ];
556
643
 
557
644
  const result = await ai.call({
558
- aiModel: "gpt-4o",
645
+ aiModel: "gpt-5-nano",
559
646
  messages,
560
647
  });
561
648
 
@@ -582,13 +669,13 @@ describe("AI E2E Tests", () => {
582
669
  ];
583
670
 
584
671
  const result = await ai.call({
585
- aiModel: "openrouter/openai/gpt-5",
672
+ aiModel: "openrouter/openai/gpt-5-nano",
586
673
  messages,
587
674
  });
588
675
 
589
676
  expect(result.text).toBeDefined();
590
677
  expect(result.text.toLowerCase()).toContain("joão");
591
- }
678
+ },
592
679
  );
593
680
  });
594
681
  });