@librechat/agents 2.4.52 → 2.4.54

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.
Files changed (64) hide show
  1. package/dist/cjs/graphs/Graph.cjs +8 -7
  2. package/dist/cjs/graphs/Graph.cjs.map +1 -1
  3. package/dist/cjs/llm/anthropic/index.cjs +8 -8
  4. package/dist/cjs/llm/anthropic/index.cjs.map +1 -1
  5. package/dist/cjs/llm/anthropic/types.cjs.map +1 -1
  6. package/dist/cjs/llm/anthropic/utils/message_inputs.cjs +15 -0
  7. package/dist/cjs/llm/anthropic/utils/message_inputs.cjs.map +1 -1
  8. package/dist/cjs/main.cjs +2 -0
  9. package/dist/cjs/main.cjs.map +1 -1
  10. package/dist/cjs/stream.cjs +8 -0
  11. package/dist/cjs/stream.cjs.map +1 -1
  12. package/dist/cjs/tools/ToolNode.cjs +8 -2
  13. package/dist/cjs/tools/ToolNode.cjs.map +1 -1
  14. package/dist/cjs/tools/handlers.cjs +107 -1
  15. package/dist/cjs/tools/handlers.cjs.map +1 -1
  16. package/dist/cjs/tools/search/anthropic.cjs +40 -0
  17. package/dist/cjs/tools/search/anthropic.cjs.map +1 -0
  18. package/dist/cjs/tools/search/search.cjs +2 -2
  19. package/dist/cjs/tools/search/search.cjs.map +1 -1
  20. package/dist/esm/graphs/Graph.mjs +8 -7
  21. package/dist/esm/graphs/Graph.mjs.map +1 -1
  22. package/dist/esm/llm/anthropic/index.mjs +8 -8
  23. package/dist/esm/llm/anthropic/index.mjs.map +1 -1
  24. package/dist/esm/llm/anthropic/types.mjs.map +1 -1
  25. package/dist/esm/llm/anthropic/utils/message_inputs.mjs +15 -0
  26. package/dist/esm/llm/anthropic/utils/message_inputs.mjs.map +1 -1
  27. package/dist/esm/main.mjs +1 -1
  28. package/dist/esm/stream.mjs +9 -1
  29. package/dist/esm/stream.mjs.map +1 -1
  30. package/dist/esm/tools/ToolNode.mjs +8 -2
  31. package/dist/esm/tools/ToolNode.mjs.map +1 -1
  32. package/dist/esm/tools/handlers.mjs +107 -3
  33. package/dist/esm/tools/handlers.mjs.map +1 -1
  34. package/dist/esm/tools/search/anthropic.mjs +37 -0
  35. package/dist/esm/tools/search/anthropic.mjs.map +1 -0
  36. package/dist/esm/tools/search/search.mjs +2 -2
  37. package/dist/esm/tools/search/search.mjs.map +1 -1
  38. package/dist/types/graphs/Graph.d.ts +3 -1
  39. package/dist/types/llm/anthropic/types.d.ts +2 -0
  40. package/dist/types/llm/anthropic/utils/message_inputs.d.ts +1 -1
  41. package/dist/types/llm/anthropic/utils/output_parsers.d.ts +3 -3
  42. package/dist/types/scripts/ant_web_search.d.ts +1 -0
  43. package/dist/types/tools/CodeExecutor.d.ts +2 -2
  44. package/dist/types/tools/ToolNode.d.ts +1 -1
  45. package/dist/types/tools/handlers.d.ts +11 -0
  46. package/dist/types/tools/search/anthropic.d.ts +16 -0
  47. package/dist/types/types/llm.d.ts +3 -2
  48. package/dist/types/types/stream.d.ts +9 -1
  49. package/package.json +5 -3
  50. package/src/graphs/Graph.ts +9 -7
  51. package/src/llm/anthropic/Jacob_Lee_Resume_2023.pdf +0 -0
  52. package/src/llm/anthropic/index.ts +7 -12
  53. package/src/llm/anthropic/llm.spec.ts +447 -115
  54. package/src/llm/anthropic/types.ts +16 -0
  55. package/src/llm/anthropic/utils/message_inputs.ts +17 -2
  56. package/src/llm/anthropic/utils/output_parsers.ts +4 -4
  57. package/src/scripts/ant_web_search.ts +158 -0
  58. package/src/stream.ts +16 -5
  59. package/src/tools/ToolNode.ts +17 -3
  60. package/src/tools/handlers.ts +170 -1
  61. package/src/tools/search/anthropic.ts +51 -0
  62. package/src/tools/search/search.ts +2 -1
  63. package/src/types/llm.ts +4 -2
  64. package/src/types/stream.ts +14 -0
@@ -1,6 +1,11 @@
1
+ /* eslint-disable no-process-env */
2
+ /* eslint-disable @typescript-eslint/no-explicit-any */
3
+ import { config } from 'dotenv';
4
+ config();
1
5
  import { expect, test } from '@jest/globals';
2
6
  import * as fs from 'fs/promises';
3
7
  import {
8
+ AIMessage,
4
9
  AIMessageChunk,
5
10
  BaseMessage,
6
11
  HumanMessage,
@@ -16,13 +21,53 @@ import {
16
21
  } from '@langchain/core/prompts';
17
22
  import { CallbackManager } from '@langchain/core/callbacks/manager';
18
23
  import { concat } from '@langchain/core/utils/stream';
24
+ import { AnthropicVertex } from '@anthropic-ai/vertex-sdk';
25
+ import { BaseLanguageModelInput } from '@langchain/core/language_models/base';
26
+ import { tool } from '@langchain/core/tools';
27
+ import { z } from 'zod';
19
28
  import { CustomAnthropic as ChatAnthropic } from './index';
20
- import { AnthropicMessageResponse } from './types';
29
+ import { AnthropicMessageResponse, ChatAnthropicContentBlock } from './types';
21
30
  jest.setTimeout(120000);
22
31
 
32
+ async function invoke(
33
+ chat: ChatAnthropic,
34
+ invocationType: string,
35
+ input: BaseLanguageModelInput
36
+ ): Promise<AIMessageChunk | AIMessage> {
37
+ if (invocationType === 'stream') {
38
+ let output: AIMessageChunk | undefined;
39
+
40
+ const stream = await chat.stream(input);
41
+
42
+ for await (const chunk of stream) {
43
+ if (!output) {
44
+ output = chunk;
45
+ } else {
46
+ output = output.concat(chunk);
47
+ }
48
+ }
49
+
50
+ return output!;
51
+ }
52
+
53
+ return chat.invoke(input);
54
+ }
55
+
56
+ // use this for tests involving "extended thinking"
57
+ const extendedThinkingModelName = 'claude-3-7-sonnet-20250219';
58
+
59
+ // use this for tests involving citations
60
+ const citationsModelName = 'claude-3-5-sonnet-20241022';
61
+
62
+ // use this for tests involving PDF documents
63
+ const pdfModelName = 'claude-3-5-haiku-20241022';
64
+
65
+ // Use this model for all other tests
66
+ const modelName = 'claude-3-haiku-20240307';
67
+
23
68
  test('Test ChatAnthropic', async () => {
24
69
  const chat = new ChatAnthropic({
25
- modelName: 'claude-3-sonnet-20240229',
70
+ modelName,
26
71
  maxRetries: 0,
27
72
  });
28
73
  const message = new HumanMessage('Hello!');
@@ -32,7 +77,7 @@ test('Test ChatAnthropic', async () => {
32
77
 
33
78
  test('Test ChatAnthropic with a bad API key throws appropriate error', async () => {
34
79
  const chat = new ChatAnthropic({
35
- modelName: 'claude-3-sonnet-20240229',
80
+ modelName,
36
81
  maxRetries: 0,
37
82
  apiKey: 'bad',
38
83
  });
@@ -65,7 +110,7 @@ test('Test ChatAnthropic with unknown model throws appropriate error', async ()
65
110
 
66
111
  test('Test ChatAnthropic Generate', async () => {
67
112
  const chat = new ChatAnthropic({
68
- modelName: 'claude-3-sonnet-20240229',
113
+ modelName,
69
114
  maxRetries: 0,
70
115
  });
71
116
  const message = new HumanMessage('Hello!');
@@ -73,7 +118,6 @@ test('Test ChatAnthropic Generate', async () => {
73
118
  expect(res.generations.length).toBe(2);
74
119
  for (const generation of res.generations) {
75
120
  expect(generation.length).toBe(1);
76
- // @eslint-disable-next-line/@typescript-eslint/ban-ts-comment
77
121
  for (const message of generation) {
78
122
  // console.log(message.text);
79
123
  }
@@ -83,7 +127,7 @@ test('Test ChatAnthropic Generate', async () => {
83
127
 
84
128
  test.skip('Test ChatAnthropic Generate w/ ClientOptions', async () => {
85
129
  const chat = new ChatAnthropic({
86
- modelName: 'claude-3-sonnet-20240229',
130
+ modelName,
87
131
  maxRetries: 0,
88
132
  clientOptions: {
89
133
  defaultHeaders: {
@@ -96,7 +140,6 @@ test.skip('Test ChatAnthropic Generate w/ ClientOptions', async () => {
96
140
  expect(res.generations.length).toBe(2);
97
141
  for (const generation of res.generations) {
98
142
  expect(generation.length).toBe(1);
99
- // @eslint-disable-next-line/@typescript-eslint/ban-ts-comment
100
143
  for (const message of generation) {
101
144
  // console.log(message.text);
102
145
  }
@@ -106,7 +149,7 @@ test.skip('Test ChatAnthropic Generate w/ ClientOptions', async () => {
106
149
 
107
150
  test('Test ChatAnthropic Generate with a signal in call options', async () => {
108
151
  const chat = new ChatAnthropic({
109
- modelName: 'claude-3-sonnet-20240229',
152
+ modelName,
110
153
  maxRetries: 0,
111
154
  });
112
155
  const controller = new AbortController();
@@ -128,9 +171,8 @@ test('Test ChatAnthropic tokenUsage with a batch', async () => {
128
171
  const model = new ChatAnthropic({
129
172
  temperature: 0,
130
173
  maxRetries: 0,
131
- modelName: 'claude-3-sonnet-20240229',
174
+ modelName,
132
175
  });
133
- // @eslint-disable-next-line/@typescript-eslint/ban-ts-comment
134
176
  const res = await model.generate([
135
177
  [new HumanMessage(`Hello!`)],
136
178
  [new HumanMessage(`Hi!`)],
@@ -143,7 +185,7 @@ test('Test ChatAnthropic in streaming mode', async () => {
143
185
  let streamedCompletion = '';
144
186
 
145
187
  const model = new ChatAnthropic({
146
- modelName: 'claude-3-sonnet-20240229',
188
+ modelName,
147
189
  maxRetries: 0,
148
190
  streaming: true,
149
191
  callbacks: CallbackManager.fromHandlers({
@@ -166,7 +208,7 @@ test('Test ChatAnthropic in streaming mode with a signal', async () => {
166
208
  let streamedCompletion = '';
167
209
 
168
210
  const model = new ChatAnthropic({
169
- modelName: 'claude-3-sonnet-20240229',
211
+ modelName,
170
212
  maxRetries: 0,
171
213
  streaming: true,
172
214
  callbacks: CallbackManager.fromHandlers({
@@ -195,14 +237,13 @@ test('Test ChatAnthropic in streaming mode with a signal', async () => {
195
237
 
196
238
  test.skip('Test ChatAnthropic prompt value', async () => {
197
239
  const chat = new ChatAnthropic({
198
- modelName: 'claude-3-sonnet-20240229',
240
+ modelName,
199
241
  maxRetries: 0,
200
242
  });
201
243
  const message = new HumanMessage('Hello!');
202
244
  const res = await chat.generatePrompt([new ChatPromptValue([message])]);
203
245
  expect(res.generations.length).toBe(1);
204
246
  for (const generation of res.generations) {
205
- // @eslint-disable-next-line/@typescript-eslint/ban-ts-comment
206
247
  for (const g of generation) {
207
248
  // console.log(g.text);
208
249
  }
@@ -212,7 +253,7 @@ test.skip('Test ChatAnthropic prompt value', async () => {
212
253
 
213
254
  test.skip('ChatAnthropic, docs, prompt templates', async () => {
214
255
  const chat = new ChatAnthropic({
215
- modelName: 'claude-3-sonnet-20240229',
256
+ modelName,
216
257
  maxRetries: 0,
217
258
  temperature: 0,
218
259
  });
@@ -226,7 +267,6 @@ test.skip('ChatAnthropic, docs, prompt templates', async () => {
226
267
  HumanMessagePromptTemplate.fromTemplate('{text}'),
227
268
  ]);
228
269
 
229
- // @eslint-disable-next-line/@typescript-eslint/ban-ts-comment
230
270
  const responseA = await chat.generatePrompt([
231
271
  await chatPrompt.formatPromptValue({
232
272
  input_language: 'English',
@@ -240,7 +280,7 @@ test.skip('ChatAnthropic, docs, prompt templates', async () => {
240
280
 
241
281
  test.skip('ChatAnthropic, longer chain of messages', async () => {
242
282
  const chat = new ChatAnthropic({
243
- modelName: 'claude-3-sonnet-20240229',
283
+ modelName,
244
284
  maxRetries: 0,
245
285
  temperature: 0,
246
286
  });
@@ -251,7 +291,6 @@ test.skip('ChatAnthropic, longer chain of messages', async () => {
251
291
  HumanMessagePromptTemplate.fromTemplate('{text}'),
252
292
  ]);
253
293
 
254
- // @eslint-disable-next-line/@typescript-eslint/ban-ts-comment
255
294
  const responseA = await chat.generatePrompt([
256
295
  await chatPrompt.formatPromptValue({
257
296
  text: 'What did I just say my name was?',
@@ -265,12 +304,11 @@ test.skip('ChatAnthropic, Anthropic apiUrl set manually via constructor', async
265
304
  // Pass the default URL through (should use this, and work as normal)
266
305
  const anthropicApiUrl = 'https://api.anthropic.com';
267
306
  const chat = new ChatAnthropic({
268
- modelName: 'claude-3-sonnet-20240229',
307
+ modelName,
269
308
  maxRetries: 0,
270
309
  anthropicApiUrl,
271
310
  });
272
311
  const message = new HumanMessage('Hello!');
273
- // @eslint-disable-next-line/@typescript-eslint/ban-ts-comment
274
312
  const res = await chat.call([message]);
275
313
  // console.log({ res });
276
314
  });
@@ -279,10 +317,10 @@ test('Test ChatAnthropic stream method', async () => {
279
317
  const model = new ChatAnthropic({
280
318
  maxTokens: 50,
281
319
  maxRetries: 0,
282
- modelName: 'claude-3-sonnet-20240229',
320
+ modelName,
283
321
  });
284
322
  const stream = await model.stream('Print hello world.');
285
- const chunks: any[] = [];
323
+ const chunks: AIMessageChunk[] = [];
286
324
  for await (const chunk of stream) {
287
325
  chunks.push(chunk);
288
326
  }
@@ -294,7 +332,7 @@ test('Test ChatAnthropic stream method with abort', async () => {
294
332
  const model = new ChatAnthropic({
295
333
  maxTokens: 500,
296
334
  maxRetries: 0,
297
- modelName: 'claude-3-sonnet-20240229',
335
+ modelName,
298
336
  });
299
337
  const stream = await model.stream(
300
338
  'How is your day going? Be extremely verbose.',
@@ -302,7 +340,6 @@ test('Test ChatAnthropic stream method with abort', async () => {
302
340
  signal: AbortSignal.timeout(1000),
303
341
  }
304
342
  );
305
- // @eslint-disable-next-line/@typescript-eslint/ban-ts-comment
306
343
  for await (const chunk of stream) {
307
344
  // console.log(chunk);
308
345
  }
@@ -313,13 +350,12 @@ test('Test ChatAnthropic stream method with early break', async () => {
313
350
  const model = new ChatAnthropic({
314
351
  maxTokens: 50,
315
352
  maxRetries: 0,
316
- modelName: 'claude-3-sonnet-20240229',
353
+ modelName,
317
354
  });
318
355
  const stream = await model.stream(
319
356
  'How is your day going? Be extremely verbose.'
320
357
  );
321
358
  let i = 0;
322
- // @eslint-disable-next-line/@typescript-eslint/ban-ts-comment
323
359
  for await (const chunk of stream) {
324
360
  // console.log(chunk);
325
361
  i += 1;
@@ -331,7 +367,7 @@ test('Test ChatAnthropic stream method with early break', async () => {
331
367
 
332
368
  test('Test ChatAnthropic headers passed through', async () => {
333
369
  const chat = new ChatAnthropic({
334
- modelName: 'claude-3-sonnet-20240229',
370
+ modelName,
335
371
  maxRetries: 0,
336
372
  apiKey: 'NOT_REAL',
337
373
  clientOptions: {
@@ -341,36 +377,142 @@ test('Test ChatAnthropic headers passed through', async () => {
341
377
  },
342
378
  });
343
379
  const message = new HumanMessage('Hello!');
344
- // @eslint-disable-next-line/@typescript-eslint/ban-ts-comment
345
380
  const res = await chat.invoke([message]);
346
381
  // console.log({ res });
347
382
  });
348
383
 
349
- test('Test ChatAnthropic multimodal', async () => {
350
- const chat = new ChatAnthropic({
351
- modelName: 'claude-3-sonnet-20240229',
352
- maxRetries: 0,
353
- });
354
- // @eslint-disable-next-line/@typescript-eslint/ban-ts-comment
355
- const res = await chat.invoke([
356
- new HumanMessage({
357
- content: [
358
- {
359
- type: 'image_url',
360
- image_url: {
361
- url: 'data:image/jpeg;base64,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',
362
- },
363
- },
364
- { type: 'text', text: 'What is this a logo for?' },
365
- ],
366
- }),
367
- ]);
368
- // console.log(res);
384
+ describe('ChatAnthropic image inputs', () => {
385
+ test.each(['invoke', 'stream'])(
386
+ 'Test ChatAnthropic image_url, %s',
387
+ async (invocationType: string) => {
388
+ const chat = new ChatAnthropic({
389
+ modelName,
390
+ maxRetries: 0,
391
+ });
392
+
393
+ const dataUrlRes = invoke(chat, invocationType, [
394
+ new HumanMessage({
395
+ content: [
396
+ {
397
+ type: 'image_url',
398
+ image_url: {
399
+ url: 'data:image/jpeg;base64,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',
400
+ },
401
+ },
402
+ { type: 'text', text: 'Describe this image.' },
403
+ ],
404
+ }),
405
+ ]);
406
+
407
+ await expect(dataUrlRes).resolves.toBeDefined();
408
+
409
+ const urlRes = invoke(chat, invocationType, [
410
+ new HumanMessage({
411
+ content: [
412
+ {
413
+ type: 'image_url',
414
+ image_url:
415
+ 'https://upload.wikimedia.org/wikipedia/commons/thumb/3/30/RedDisc.svg/24px-RedDisc.svg.png',
416
+ },
417
+ { type: 'text', text: 'Describe this image.' },
418
+ ],
419
+ }),
420
+ ]);
421
+
422
+ await expect(urlRes).resolves.toBeDefined();
423
+
424
+ const invalidSchemeRes = invoke(chat, invocationType, [
425
+ new HumanMessage({
426
+ content: [
427
+ {
428
+ type: 'image_url',
429
+ image_url: 'file:///path/to/image.png',
430
+ },
431
+ { type: 'text', text: 'Describe this image.' },
432
+ ],
433
+ }),
434
+ ]);
435
+
436
+ await expect(invalidSchemeRes).rejects.toThrow(
437
+ [
438
+ 'Invalid image URL protocol: "file:". Anthropic only supports images as http, https, or base64-encoded data URLs on \'image_url\' content blocks.',
439
+ 'Example: data:image/png;base64,/9j/4AAQSk...',
440
+ 'Example: https://example.com/image.jpg',
441
+ ].join('\n\n')
442
+ );
443
+
444
+ const invalidUrlRes = invoke(chat, invocationType, [
445
+ new HumanMessage({
446
+ content: [
447
+ {
448
+ type: 'image_url',
449
+ image_url: "this isn't a valid URL",
450
+ },
451
+ { type: 'text', text: 'Describe this image.' },
452
+ ],
453
+ }),
454
+ ]);
455
+
456
+ await expect(invalidUrlRes).rejects.toThrow(
457
+ [
458
+ `Malformed image URL: "this isn't a valid URL". Content blocks of type 'image_url' must be a valid http, https, or base64-encoded data URL.`,
459
+ 'Example: data:image/png;base64,/9j/4AAQSk...',
460
+ 'Example: https://example.com/image.jpg',
461
+ ].join('\n\n')
462
+ );
463
+ }
464
+ );
465
+
466
+ test.each(['invoke', 'stream'])(
467
+ 'Test ChatAnthropic Anthropic Image Block, %s',
468
+ async (invocationType: string) => {
469
+ const chat = new ChatAnthropic({
470
+ modelName,
471
+ maxRetries: 0,
472
+ });
473
+ const base64Res = invoke(chat, invocationType, [
474
+ new HumanMessage({
475
+ content: [
476
+ {
477
+ type: 'image',
478
+ source: {
479
+ type: 'base64',
480
+ media_type: 'image/jpeg',
481
+ data: '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',
482
+ },
483
+ },
484
+ {
485
+ type: 'text',
486
+ text: 'Describe this image',
487
+ },
488
+ ],
489
+ }),
490
+ ]);
491
+
492
+ await expect(base64Res).resolves.toBeDefined();
493
+
494
+ const urlRes = chat.invoke([
495
+ new HumanMessage({
496
+ content: [
497
+ {
498
+ type: 'image',
499
+ source: {
500
+ type: 'url',
501
+ url: 'https://upload.wikimedia.org/wikipedia/commons/thumb/3/30/RedDisc.svg/24px-RedDisc.svg.png',
502
+ },
503
+ },
504
+ ],
505
+ }),
506
+ ]);
507
+
508
+ await expect(urlRes).resolves.toBeDefined();
509
+ }
510
+ );
369
511
  });
370
512
 
371
513
  test('Stream tokens', async () => {
372
514
  const model = new ChatAnthropic({
373
- model: 'claude-3-haiku-20240307',
515
+ modelName,
374
516
  temperature: 0,
375
517
  maxTokens: 10,
376
518
  });
@@ -397,14 +539,14 @@ test('Stream tokens', async () => {
397
539
  });
398
540
 
399
541
  test('id is supplied when invoking', async () => {
400
- const model = new ChatAnthropic();
542
+ const model = new ChatAnthropic({ modelName });
401
543
  const result = await model.invoke('Hello');
402
544
  expect(result.id).toBeDefined();
403
545
  expect(result.id).not.toEqual('');
404
546
  });
405
547
 
406
548
  test('id is supplied when streaming', async () => {
407
- const model = new ChatAnthropic();
549
+ const model = new ChatAnthropic({ modelName });
408
550
  let finalChunk: AIMessageChunk | undefined;
409
551
  for await (const chunk of await model.stream('Hello')) {
410
552
  finalChunk = !finalChunk ? chunk : concat(finalChunk, chunk);
@@ -678,7 +820,7 @@ The current date is ${new Date().toISOString()}`;
678
820
 
679
821
  test('system prompt caching', async () => {
680
822
  const model = new ChatAnthropic({
681
- model: 'claude-3-haiku-20240307',
823
+ modelName,
682
824
  clientOptions: {
683
825
  defaultHeaders: {
684
826
  'anthropic-beta': 'prompt-caching-2024-07-31',
@@ -724,7 +866,7 @@ test('system prompt caching', async () => {
724
866
  // TODO: Add proper test with long tool content
725
867
  test.skip('tool caching', async () => {
726
868
  const model = new ChatAnthropic({
727
- model: 'claude-3-haiku-20240307',
869
+ modelName,
728
870
  clientOptions: {
729
871
  defaultHeaders: {
730
872
  'anthropic-beta': 'prompt-caching-2024-07-31',
@@ -769,14 +911,22 @@ test.skip('tool caching', async () => {
769
911
  );
770
912
  });
771
913
 
914
+ test.skip('Test ChatAnthropic with custom client', async () => {
915
+ const client = new AnthropicVertex();
916
+ const chat = new ChatAnthropic({
917
+ modelName,
918
+ maxRetries: 0,
919
+ createClient: () => client,
920
+ });
921
+ const message = new HumanMessage('Hello!');
922
+ const res = await chat.invoke([message]);
923
+ // console.log({ res });
924
+ expect(res.usage_metadata?.input_token_details).toBeDefined();
925
+ });
926
+
772
927
  test('human message caching', async () => {
773
928
  const model = new ChatAnthropic({
774
- model: 'claude-3-haiku-20240307',
775
- clientOptions: {
776
- defaultHeaders: {
777
- 'anthropic-beta': 'prompt-caching-2024-07-31',
778
- },
779
- },
929
+ modelName,
780
930
  });
781
931
 
782
932
  const messages = [
@@ -784,7 +934,7 @@ test('human message caching', async () => {
784
934
  content: [
785
935
  {
786
936
  type: 'text',
787
- text: `You are a pirate. Always respond in pirate dialect.\nUse the following as context when answering questions: ${CACHED_TEXT}`,
937
+ text: `You are a scotsman. Always respond in scotsman dialect.\nUse the following as context when answering questions: ${CACHED_TEXT}`,
788
938
  },
789
939
  ],
790
940
  }),
@@ -813,11 +963,10 @@ test('human message caching', async () => {
813
963
 
814
964
  test('Can accept PDF documents', async () => {
815
965
  const model = new ChatAnthropic({
816
- model: 'claude-3-5-sonnet-latest',
966
+ modelName: pdfModelName,
817
967
  });
818
968
 
819
- const pdfPath =
820
- '../langchain-community/src/document_loaders/tests/example_data/Jacob_Lee_Resume_2023.pdf';
969
+ const pdfPath = './src/llm/anthropic/Jacob_Lee_Resume_2023.pdf';
821
970
  const pdfBase64 = await fs.readFile(pdfPath, 'base64');
822
971
 
823
972
  const response = await model.invoke([
@@ -844,70 +993,253 @@ test('Can accept PDF documents', async () => {
844
993
  expect(response.content.length).toBeGreaterThan(10);
845
994
  });
846
995
 
847
- test('Citations', async () => {
848
- const citationsModel = new ChatAnthropic({
849
- model: 'claude-3-5-sonnet-latest',
996
+ describe('Citations', () => {
997
+ test('document blocks', async () => {
998
+ const citationsModel = new ChatAnthropic({
999
+ model: citationsModelName,
1000
+ });
1001
+ const messages = [
1002
+ {
1003
+ role: 'user',
1004
+ content: [
1005
+ {
1006
+ type: 'document',
1007
+ source: {
1008
+ type: 'text',
1009
+ media_type: 'text/plain',
1010
+ data: "The grass the user is asking about is bluegrass. The sky is orange because it's night.",
1011
+ },
1012
+ title: 'My Document',
1013
+ context: 'This is a trustworthy document.',
1014
+ citations: {
1015
+ enabled: true,
1016
+ },
1017
+ },
1018
+ {
1019
+ type: 'text',
1020
+ text: 'What color is the grass and sky?',
1021
+ },
1022
+ ],
1023
+ },
1024
+ ];
1025
+
1026
+ const response = await citationsModel.invoke(messages);
1027
+
1028
+ expect(response.content.length).toBeGreaterThan(2);
1029
+ expect(Array.isArray(response.content)).toBe(true);
1030
+ const blocksWithCitations = (response.content as any[]).filter(
1031
+ (block) => block.citations !== undefined
1032
+ );
1033
+ expect(blocksWithCitations.length).toEqual(2);
1034
+ expect(typeof blocksWithCitations[0].citations[0]).toEqual('object');
1035
+
1036
+ const stream = await citationsModel.stream(messages);
1037
+ let aggregated;
1038
+ let chunkHasCitation = false;
1039
+ for await (const chunk of stream) {
1040
+ aggregated = aggregated === undefined ? chunk : concat(aggregated, chunk);
1041
+ if (
1042
+ !chunkHasCitation &&
1043
+ Array.isArray(chunk.content) &&
1044
+ chunk.content.some((c: any) => c.citations !== undefined)
1045
+ ) {
1046
+ chunkHasCitation = true;
1047
+ }
1048
+ }
1049
+ expect(chunkHasCitation).toBe(true);
1050
+ expect(Array.isArray(aggregated?.content)).toBe(true);
1051
+ expect(aggregated?.content.length).toBeGreaterThan(2);
1052
+ expect(
1053
+ (aggregated?.content as any[]).some((c) => c.citations !== undefined)
1054
+ ).toBe(true);
850
1055
  });
1056
+ describe('search result blocks', () => {
1057
+ const citationsModel = new ChatAnthropic({
1058
+ model: citationsModelName,
1059
+ clientOptions: {
1060
+ defaultHeaders: {
1061
+ 'anthropic-beta': 'search-results-2025-06-09',
1062
+ },
1063
+ },
1064
+ });
851
1065
 
852
- const messages = [
853
- {
854
- role: 'user',
855
- content: [
856
- {
857
- type: 'document',
858
- source: {
859
- type: 'text',
860
- media_type: 'text/plain',
861
- data: "The grass the user is asking about is bluegrass. The sky is orange because it's night.",
1066
+ const messages = [
1067
+ {
1068
+ role: 'user',
1069
+ content: [
1070
+ {
1071
+ type: 'search_result',
1072
+ title: 'History of France',
1073
+ source: 'https://example.com/france-history',
1074
+ citations: { enabled: true },
1075
+ content: [
1076
+ {
1077
+ type: 'text',
1078
+ text: 'The capital of France is Paris.',
1079
+ },
1080
+ {
1081
+ type: 'text',
1082
+ text: 'The old capital of France was Lyon.',
1083
+ },
1084
+ ],
862
1085
  },
863
- title: 'My Document',
864
- context: 'This is a trustworthy document.',
865
- citations: {
866
- enabled: true,
1086
+ {
1087
+ type: 'search_result',
1088
+ title: 'Geography of France',
1089
+ source: 'https://example.com/france-geography',
1090
+ citations: { enabled: true },
1091
+ content: [
1092
+ {
1093
+ type: 'text',
1094
+ text: 'France is a country in Europe.',
1095
+ },
1096
+ {
1097
+ type: 'text',
1098
+ text: 'France borders Spain to the south.',
1099
+ },
1100
+ ],
867
1101
  },
1102
+ {
1103
+ type: 'text',
1104
+ text: 'What is the capital of France and where is it located? You must cite your sources.',
1105
+ },
1106
+ ],
1107
+ },
1108
+ ];
1109
+
1110
+ test('without streaming', async () => {
1111
+ const response = await citationsModel.invoke(messages);
1112
+
1113
+ expect(Array.isArray(response.content)).toBe(true);
1114
+ expect(response.content.length).toBeGreaterThan(0);
1115
+
1116
+ // Check that we have cited content
1117
+ const blocksWithCitations = (response.content as any[]).filter(
1118
+ (block) => block.citations !== undefined
1119
+ );
1120
+ expect(blocksWithCitations.length).toBeGreaterThan(0);
1121
+
1122
+ // Verify citation structure
1123
+ const citation = blocksWithCitations[0].citations[0];
1124
+ expect(typeof citation).toBe('object');
1125
+ expect(citation.type).toBe('search_result_location');
1126
+ expect(citation.source).toBeDefined();
1127
+ });
1128
+ test('with streaming', async () => {
1129
+ // Test streaming
1130
+ const stream = await citationsModel.stream(messages);
1131
+ let aggregated;
1132
+ let chunkHasCitation = false;
1133
+ for await (const chunk of stream) {
1134
+ aggregated =
1135
+ aggregated === undefined ? chunk : concat(aggregated, chunk);
1136
+ if (
1137
+ !chunkHasCitation &&
1138
+ Array.isArray(chunk.content) &&
1139
+ chunk.content.some((c: any) => c.citations !== undefined)
1140
+ ) {
1141
+ chunkHasCitation = true;
1142
+ }
1143
+ }
1144
+ expect(chunkHasCitation).toBe(true);
1145
+ expect(Array.isArray(aggregated?.content)).toBe(true);
1146
+ expect(
1147
+ (aggregated?.content as any[]).some((c) => c.citations !== undefined)
1148
+ ).toBe(true);
1149
+ });
1150
+ });
1151
+
1152
+ test('search result blocks from tool', async () => {
1153
+ const ragTool = tool(
1154
+ (): ChatAnthropicContentBlock[] => [
1155
+ {
1156
+ type: 'search_result',
1157
+ title: 'History of France',
1158
+ source: 'https://example.com/france-history',
1159
+ citations: { enabled: true },
1160
+ content: [
1161
+ {
1162
+ type: 'text',
1163
+ text: 'The capital of France is Paris.',
1164
+ },
1165
+ {
1166
+ type: 'text',
1167
+ text: 'France was established as a republic in 1792.',
1168
+ },
1169
+ ],
868
1170
  },
869
1171
  {
870
- type: 'text',
871
- text: 'What color is the grass and sky?',
1172
+ type: 'search_result',
1173
+ title: 'Geography of France',
1174
+ source: 'https://example.com/france-geography',
1175
+ citations: { enabled: true },
1176
+ content: [
1177
+ {
1178
+ type: 'text',
1179
+ text: 'France is located in Western Europe.',
1180
+ },
1181
+ {
1182
+ type: 'text',
1183
+ text: 'France has a population of approximately 67 million people.',
1184
+ },
1185
+ ],
872
1186
  },
873
1187
  ],
874
- },
875
- ];
1188
+ {
1189
+ name: 'search_knowledge_base',
1190
+ description: 'Search the knowledge base for information about France',
1191
+ schema: z.object({
1192
+ query: z.string().describe('The search query'),
1193
+ }),
1194
+ }
1195
+ );
876
1196
 
877
- const response = await citationsModel.invoke(messages);
1197
+ const citationsModel = new ChatAnthropic({
1198
+ model: citationsModelName,
1199
+ clientOptions: {
1200
+ defaultHeaders: {
1201
+ 'anthropic-beta': 'search-results-2025-06-09',
1202
+ },
1203
+ },
1204
+ }).bindTools([ragTool]);
878
1205
 
879
- expect(response.content.length).toBeGreaterThan(2);
880
- expect(Array.isArray(response.content)).toBe(true);
881
- const blocksWithCitations = (response.content as any[]).filter(
882
- (block) => block.citations !== undefined
883
- );
884
- expect(blocksWithCitations.length).toEqual(2);
885
- expect(typeof blocksWithCitations[0].citations[0]).toEqual('object');
1206
+ const messages = [
1207
+ new HumanMessage(
1208
+ 'Search for information about France and tell me what you find with proper citations.'
1209
+ ),
1210
+ ];
886
1211
 
887
- const stream = await citationsModel.stream(messages);
888
- let aggregated;
889
- let chunkHasCitation = false;
890
- for await (const chunk of stream) {
891
- aggregated = aggregated === undefined ? chunk : concat(aggregated, chunk);
892
- if (
893
- !chunkHasCitation &&
894
- Array.isArray(chunk.content) &&
895
- chunk.content.some((c: any) => c.citations !== undefined)
896
- ) {
897
- chunkHasCitation = true;
898
- }
899
- }
900
- expect(chunkHasCitation).toBe(true);
901
- expect(Array.isArray(aggregated?.content)).toBe(true);
902
- expect(aggregated?.content.length).toBeGreaterThan(2);
903
- expect(
904
- (aggregated?.content as any[]).some((c) => c.citations !== undefined)
905
- ).toBe(true);
1212
+ const response = await citationsModel.invoke(messages);
1213
+ messages.push(response);
1214
+
1215
+ expect(Array.isArray(response.content)).toBe(true);
1216
+ expect(response.content.length).toBeGreaterThan(0);
1217
+
1218
+ // Check that the model called the tool
1219
+ expect(response.tool_calls?.length).toBeGreaterThan(0);
1220
+ expect(response.tool_calls?.[0].name).toBe('search_knowledge_base');
1221
+
1222
+ const toolResponse = await ragTool.invoke(response.tool_calls![0]);
1223
+ messages.push(toolResponse);
1224
+
1225
+ const response2 = await citationsModel.invoke(messages);
1226
+
1227
+ expect(Array.isArray(response2.content)).toBe(true);
1228
+ expect(response2.content.length).toBeGreaterThan(0);
1229
+ // Make sure that a citation exists somewhere in the content list
1230
+ const citationBlock = (response2.content as any[]).find(
1231
+ (block: any) =>
1232
+ Array.isArray(block.citations) && block.citations.length > 0
1233
+ );
1234
+ expect(citationBlock).toBeDefined();
1235
+ expect(citationBlock.citations[0].type).toBe('search_result_location');
1236
+ expect(citationBlock.citations[0].source).toBeDefined();
1237
+ });
906
1238
  });
907
1239
 
908
1240
  test('Test thinking blocks multiturn invoke', async () => {
909
1241
  const model = new ChatAnthropic({
910
- model: 'claude-3-7-sonnet-latest',
1242
+ model: extendedThinkingModelName,
911
1243
  maxTokens: 5000,
912
1244
  thinking: { type: 'enabled', budget_tokens: 2000 },
913
1245
  });
@@ -945,7 +1277,7 @@ test('Test thinking blocks multiturn invoke', async () => {
945
1277
 
946
1278
  test('Test thinking blocks multiturn streaming', async () => {
947
1279
  const model = new ChatAnthropic({
948
- model: 'claude-3-7-sonnet-latest',
1280
+ model: extendedThinkingModelName,
949
1281
  maxTokens: 5000,
950
1282
  thinking: { type: 'enabled', budget_tokens: 2000 },
951
1283
  });
@@ -986,7 +1318,7 @@ test('Test thinking blocks multiturn streaming', async () => {
986
1318
 
987
1319
  test('Test redacted thinking blocks multiturn invoke', async () => {
988
1320
  const model = new ChatAnthropic({
989
- model: 'claude-3-7-sonnet-latest',
1321
+ model: extendedThinkingModelName,
990
1322
  maxTokens: 5000,
991
1323
  thinking: { type: 'enabled', budget_tokens: 2000 },
992
1324
  });
@@ -1023,7 +1355,7 @@ test('Test redacted thinking blocks multiturn invoke', async () => {
1023
1355
 
1024
1356
  test('Test redacted thinking blocks multiturn streaming', async () => {
1025
1357
  const model = new ChatAnthropic({
1026
- model: 'claude-3-7-sonnet-latest',
1358
+ model: extendedThinkingModelName,
1027
1359
  maxTokens: 5000,
1028
1360
  thinking: { type: 'enabled', budget_tokens: 2000 },
1029
1361
  });