@librechat/agents 2.4.21 → 2.4.30
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.
- package/dist/cjs/llm/anthropic/index.cjs +1 -1
- package/dist/cjs/llm/anthropic/index.cjs.map +1 -1
- package/dist/cjs/llm/anthropic/types.cjs +50 -0
- package/dist/cjs/llm/anthropic/types.cjs.map +1 -0
- package/dist/cjs/llm/anthropic/utils/message_inputs.cjs +227 -21
- package/dist/cjs/llm/anthropic/utils/message_inputs.cjs.map +1 -1
- package/dist/cjs/llm/anthropic/utils/message_outputs.cjs +1 -0
- package/dist/cjs/llm/anthropic/utils/message_outputs.cjs.map +1 -1
- package/dist/cjs/llm/openai/index.cjs.map +1 -1
- package/dist/cjs/messages/core.cjs +91 -51
- package/dist/cjs/messages/core.cjs.map +1 -1
- package/dist/cjs/run.cjs.map +1 -1
- package/dist/esm/llm/anthropic/index.mjs +1 -1
- package/dist/esm/llm/anthropic/index.mjs.map +1 -1
- package/dist/esm/llm/anthropic/types.mjs +48 -0
- package/dist/esm/llm/anthropic/types.mjs.map +1 -0
- package/dist/esm/llm/anthropic/utils/message_inputs.mjs +228 -22
- package/dist/esm/llm/anthropic/utils/message_inputs.mjs.map +1 -1
- package/dist/esm/llm/anthropic/utils/message_outputs.mjs +1 -0
- package/dist/esm/llm/anthropic/utils/message_outputs.mjs.map +1 -1
- package/dist/esm/llm/openai/index.mjs.map +1 -1
- package/dist/esm/messages/core.mjs +91 -51
- package/dist/esm/messages/core.mjs.map +1 -1
- package/dist/esm/run.mjs.map +1 -1
- package/dist/types/llm/anthropic/index.d.ts +3 -4
- package/dist/types/llm/anthropic/types.d.ts +4 -35
- package/dist/types/llm/anthropic/utils/message_inputs.d.ts +2 -2
- package/dist/types/llm/anthropic/utils/message_outputs.d.ts +1 -3
- package/dist/types/llm/anthropic/utils/output_parsers.d.ts +22 -0
- package/dist/types/llm/openai/index.d.ts +3 -2
- package/dist/types/messages/core.d.ts +1 -1
- package/dist/types/tools/example.d.ts +21 -3
- package/package.json +9 -9
- package/src/llm/anthropic/index.ts +6 -5
- package/src/llm/anthropic/llm.spec.ts +176 -179
- package/src/llm/anthropic/types.ts +64 -39
- package/src/llm/anthropic/utils/message_inputs.ts +275 -37
- package/src/llm/anthropic/utils/message_outputs.ts +4 -21
- package/src/llm/anthropic/utils/output_parsers.ts +114 -0
- package/src/llm/openai/index.ts +7 -6
- package/src/messages/core.ts +197 -96
- package/src/run.ts +1 -1
|
@@ -1,77 +1,74 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
import { expect, test } from "@jest/globals";
|
|
5
|
-
import * as fs from "fs/promises";
|
|
1
|
+
import { expect, test } from '@jest/globals';
|
|
2
|
+
import * as fs from 'fs/promises';
|
|
6
3
|
import {
|
|
7
4
|
AIMessageChunk,
|
|
8
5
|
BaseMessage,
|
|
9
6
|
HumanMessage,
|
|
10
7
|
SystemMessage,
|
|
11
|
-
} from
|
|
12
|
-
import { ChatPromptValue } from
|
|
8
|
+
} from '@langchain/core/messages';
|
|
9
|
+
import { ChatPromptValue } from '@langchain/core/prompt_values';
|
|
13
10
|
import {
|
|
14
11
|
PromptTemplate,
|
|
15
12
|
ChatPromptTemplate,
|
|
16
13
|
AIMessagePromptTemplate,
|
|
17
14
|
HumanMessagePromptTemplate,
|
|
18
15
|
SystemMessagePromptTemplate,
|
|
19
|
-
} from
|
|
20
|
-
import { CallbackManager } from
|
|
21
|
-
import { concat } from
|
|
22
|
-
import { CustomAnthropic as ChatAnthropic } from
|
|
23
|
-
import { AnthropicMessageResponse } from
|
|
16
|
+
} from '@langchain/core/prompts';
|
|
17
|
+
import { CallbackManager } from '@langchain/core/callbacks/manager';
|
|
18
|
+
import { concat } from '@langchain/core/utils/stream';
|
|
19
|
+
import { CustomAnthropic as ChatAnthropic } from './index';
|
|
20
|
+
import { AnthropicMessageResponse } from './types';
|
|
24
21
|
jest.setTimeout(120000);
|
|
25
22
|
|
|
26
|
-
test(
|
|
23
|
+
test('Test ChatAnthropic', async () => {
|
|
27
24
|
const chat = new ChatAnthropic({
|
|
28
|
-
modelName:
|
|
25
|
+
modelName: 'claude-3-sonnet-20240229',
|
|
29
26
|
maxRetries: 0,
|
|
30
27
|
});
|
|
31
|
-
const message = new HumanMessage(
|
|
28
|
+
const message = new HumanMessage('Hello!');
|
|
32
29
|
const res = await chat.invoke([message]);
|
|
33
30
|
expect(res.response_metadata.usage).toBeDefined();
|
|
34
31
|
});
|
|
35
32
|
|
|
36
|
-
test(
|
|
33
|
+
test('Test ChatAnthropic with a bad API key throws appropriate error', async () => {
|
|
37
34
|
const chat = new ChatAnthropic({
|
|
38
|
-
modelName:
|
|
35
|
+
modelName: 'claude-3-sonnet-20240229',
|
|
39
36
|
maxRetries: 0,
|
|
40
|
-
apiKey:
|
|
37
|
+
apiKey: 'bad',
|
|
41
38
|
});
|
|
42
39
|
let error;
|
|
43
40
|
try {
|
|
44
|
-
const message = new HumanMessage(
|
|
41
|
+
const message = new HumanMessage('Hello!');
|
|
45
42
|
await chat.invoke([message]);
|
|
46
43
|
} catch (e) {
|
|
47
44
|
error = e;
|
|
48
45
|
}
|
|
49
46
|
expect(error).toBeDefined();
|
|
50
|
-
expect((error as any).lc_error_code).toEqual(
|
|
47
|
+
expect((error as any).lc_error_code).toEqual('MODEL_AUTHENTICATION');
|
|
51
48
|
});
|
|
52
49
|
|
|
53
|
-
test(
|
|
50
|
+
test('Test ChatAnthropic with unknown model throws appropriate error', async () => {
|
|
54
51
|
const chat = new ChatAnthropic({
|
|
55
|
-
modelName:
|
|
52
|
+
modelName: 'badbad',
|
|
56
53
|
maxRetries: 0,
|
|
57
54
|
});
|
|
58
55
|
let error;
|
|
59
56
|
try {
|
|
60
|
-
const message = new HumanMessage(
|
|
57
|
+
const message = new HumanMessage('Hello!');
|
|
61
58
|
await chat.invoke([message]);
|
|
62
59
|
} catch (e) {
|
|
63
60
|
error = e;
|
|
64
61
|
}
|
|
65
62
|
expect(error).toBeDefined();
|
|
66
|
-
expect((error as any).lc_error_code).toEqual(
|
|
63
|
+
expect((error as any).lc_error_code).toEqual('MODEL_NOT_FOUND');
|
|
67
64
|
});
|
|
68
65
|
|
|
69
|
-
test(
|
|
66
|
+
test('Test ChatAnthropic Generate', async () => {
|
|
70
67
|
const chat = new ChatAnthropic({
|
|
71
|
-
modelName:
|
|
68
|
+
modelName: 'claude-3-sonnet-20240229',
|
|
72
69
|
maxRetries: 0,
|
|
73
70
|
});
|
|
74
|
-
const message = new HumanMessage(
|
|
71
|
+
const message = new HumanMessage('Hello!');
|
|
75
72
|
const res = await chat.generate([[message], [message]]);
|
|
76
73
|
expect(res.generations.length).toBe(2);
|
|
77
74
|
for (const generation of res.generations) {
|
|
@@ -84,17 +81,17 @@ test("Test ChatAnthropic Generate", async () => {
|
|
|
84
81
|
// console.log({ res });
|
|
85
82
|
});
|
|
86
83
|
|
|
87
|
-
test.skip(
|
|
84
|
+
test.skip('Test ChatAnthropic Generate w/ ClientOptions', async () => {
|
|
88
85
|
const chat = new ChatAnthropic({
|
|
89
|
-
modelName:
|
|
86
|
+
modelName: 'claude-3-sonnet-20240229',
|
|
90
87
|
maxRetries: 0,
|
|
91
88
|
clientOptions: {
|
|
92
89
|
defaultHeaders: {
|
|
93
|
-
|
|
90
|
+
'Helicone-Auth': 'HELICONE_API_KEY',
|
|
94
91
|
},
|
|
95
92
|
},
|
|
96
93
|
});
|
|
97
|
-
const message = new HumanMessage(
|
|
94
|
+
const message = new HumanMessage('Hello!');
|
|
98
95
|
const res = await chat.generate([[message], [message]]);
|
|
99
96
|
expect(res.generations.length).toBe(2);
|
|
100
97
|
for (const generation of res.generations) {
|
|
@@ -107,14 +104,14 @@ test.skip("Test ChatAnthropic Generate w/ ClientOptions", async () => {
|
|
|
107
104
|
// console.log({ res });
|
|
108
105
|
});
|
|
109
106
|
|
|
110
|
-
test(
|
|
107
|
+
test('Test ChatAnthropic Generate with a signal in call options', async () => {
|
|
111
108
|
const chat = new ChatAnthropic({
|
|
112
|
-
modelName:
|
|
109
|
+
modelName: 'claude-3-sonnet-20240229',
|
|
113
110
|
maxRetries: 0,
|
|
114
111
|
});
|
|
115
112
|
const controller = new AbortController();
|
|
116
113
|
const message = new HumanMessage(
|
|
117
|
-
|
|
114
|
+
'How is your day going? Be extremely verbose!'
|
|
118
115
|
);
|
|
119
116
|
await expect(() => {
|
|
120
117
|
const res = chat.generate([[message], [message]], {
|
|
@@ -127,11 +124,11 @@ test("Test ChatAnthropic Generate with a signal in call options", async () => {
|
|
|
127
124
|
}).rejects.toThrow();
|
|
128
125
|
}, 10000);
|
|
129
126
|
|
|
130
|
-
test(
|
|
127
|
+
test('Test ChatAnthropic tokenUsage with a batch', async () => {
|
|
131
128
|
const model = new ChatAnthropic({
|
|
132
129
|
temperature: 0,
|
|
133
130
|
maxRetries: 0,
|
|
134
|
-
modelName:
|
|
131
|
+
modelName: 'claude-3-sonnet-20240229',
|
|
135
132
|
});
|
|
136
133
|
// @eslint-disable-next-line/@typescript-eslint/ban-ts-comment
|
|
137
134
|
const res = await model.generate([
|
|
@@ -141,12 +138,12 @@ test("Test ChatAnthropic tokenUsage with a batch", async () => {
|
|
|
141
138
|
// console.log({ res });
|
|
142
139
|
});
|
|
143
140
|
|
|
144
|
-
test(
|
|
141
|
+
test('Test ChatAnthropic in streaming mode', async () => {
|
|
145
142
|
let nrNewTokens = 0;
|
|
146
|
-
let streamedCompletion =
|
|
143
|
+
let streamedCompletion = '';
|
|
147
144
|
|
|
148
145
|
const model = new ChatAnthropic({
|
|
149
|
-
modelName:
|
|
146
|
+
modelName: 'claude-3-sonnet-20240229',
|
|
150
147
|
maxRetries: 0,
|
|
151
148
|
streaming: true,
|
|
152
149
|
callbacks: CallbackManager.fromHandlers({
|
|
@@ -156,7 +153,7 @@ test("Test ChatAnthropic in streaming mode", async () => {
|
|
|
156
153
|
},
|
|
157
154
|
}),
|
|
158
155
|
});
|
|
159
|
-
const message = new HumanMessage(
|
|
156
|
+
const message = new HumanMessage('Hello!');
|
|
160
157
|
const res = await model.invoke([message]);
|
|
161
158
|
// console.log({ res });
|
|
162
159
|
|
|
@@ -164,12 +161,12 @@ test("Test ChatAnthropic in streaming mode", async () => {
|
|
|
164
161
|
expect(res.content).toBe(streamedCompletion);
|
|
165
162
|
});
|
|
166
163
|
|
|
167
|
-
test(
|
|
164
|
+
test('Test ChatAnthropic in streaming mode with a signal', async () => {
|
|
168
165
|
let nrNewTokens = 0;
|
|
169
|
-
let streamedCompletion =
|
|
166
|
+
let streamedCompletion = '';
|
|
170
167
|
|
|
171
168
|
const model = new ChatAnthropic({
|
|
172
|
-
modelName:
|
|
169
|
+
modelName: 'claude-3-sonnet-20240229',
|
|
173
170
|
maxRetries: 0,
|
|
174
171
|
streaming: true,
|
|
175
172
|
callbacks: CallbackManager.fromHandlers({
|
|
@@ -181,7 +178,7 @@ test("Test ChatAnthropic in streaming mode with a signal", async () => {
|
|
|
181
178
|
});
|
|
182
179
|
const controller = new AbortController();
|
|
183
180
|
const message = new HumanMessage(
|
|
184
|
-
|
|
181
|
+
'Hello! Give me an extremely verbose response'
|
|
185
182
|
);
|
|
186
183
|
await expect(() => {
|
|
187
184
|
const res = model.invoke([message], {
|
|
@@ -196,12 +193,12 @@ test("Test ChatAnthropic in streaming mode with a signal", async () => {
|
|
|
196
193
|
// console.log({ nrNewTokens, streamedCompletion });
|
|
197
194
|
}, 5000);
|
|
198
195
|
|
|
199
|
-
test.skip(
|
|
196
|
+
test.skip('Test ChatAnthropic prompt value', async () => {
|
|
200
197
|
const chat = new ChatAnthropic({
|
|
201
|
-
modelName:
|
|
198
|
+
modelName: 'claude-3-sonnet-20240229',
|
|
202
199
|
maxRetries: 0,
|
|
203
200
|
});
|
|
204
|
-
const message = new HumanMessage(
|
|
201
|
+
const message = new HumanMessage('Hello!');
|
|
205
202
|
const res = await chat.generatePrompt([new ChatPromptValue([message])]);
|
|
206
203
|
expect(res.generations.length).toBe(1);
|
|
207
204
|
for (const generation of res.generations) {
|
|
@@ -213,37 +210,37 @@ test.skip("Test ChatAnthropic prompt value", async () => {
|
|
|
213
210
|
// console.log({ res });
|
|
214
211
|
});
|
|
215
212
|
|
|
216
|
-
test.skip(
|
|
213
|
+
test.skip('ChatAnthropic, docs, prompt templates', async () => {
|
|
217
214
|
const chat = new ChatAnthropic({
|
|
218
|
-
modelName:
|
|
215
|
+
modelName: 'claude-3-sonnet-20240229',
|
|
219
216
|
maxRetries: 0,
|
|
220
217
|
temperature: 0,
|
|
221
218
|
});
|
|
222
219
|
|
|
223
220
|
const systemPrompt = PromptTemplate.fromTemplate(
|
|
224
|
-
|
|
221
|
+
'You are a helpful assistant that translates {input_language} to {output_language}.'
|
|
225
222
|
);
|
|
226
223
|
|
|
227
224
|
const chatPrompt = ChatPromptTemplate.fromMessages([
|
|
228
225
|
new SystemMessagePromptTemplate(systemPrompt),
|
|
229
|
-
HumanMessagePromptTemplate.fromTemplate(
|
|
226
|
+
HumanMessagePromptTemplate.fromTemplate('{text}'),
|
|
230
227
|
]);
|
|
231
228
|
|
|
232
229
|
// @eslint-disable-next-line/@typescript-eslint/ban-ts-comment
|
|
233
230
|
const responseA = await chat.generatePrompt([
|
|
234
231
|
await chatPrompt.formatPromptValue({
|
|
235
|
-
input_language:
|
|
236
|
-
output_language:
|
|
237
|
-
text:
|
|
232
|
+
input_language: 'English',
|
|
233
|
+
output_language: 'French',
|
|
234
|
+
text: 'I love programming.',
|
|
238
235
|
}),
|
|
239
236
|
]);
|
|
240
237
|
|
|
241
238
|
// console.log(responseA.generations);
|
|
242
239
|
});
|
|
243
240
|
|
|
244
|
-
test.skip(
|
|
241
|
+
test.skip('ChatAnthropic, longer chain of messages', async () => {
|
|
245
242
|
const chat = new ChatAnthropic({
|
|
246
|
-
modelName:
|
|
243
|
+
modelName: 'claude-3-sonnet-20240229',
|
|
247
244
|
maxRetries: 0,
|
|
248
245
|
temperature: 0,
|
|
249
246
|
});
|
|
@@ -251,40 +248,40 @@ test.skip("ChatAnthropic, longer chain of messages", async () => {
|
|
|
251
248
|
const chatPrompt = ChatPromptTemplate.fromMessages([
|
|
252
249
|
HumanMessagePromptTemplate.fromTemplate(`Hi, my name is Joe!`),
|
|
253
250
|
AIMessagePromptTemplate.fromTemplate(`Nice to meet you, Joe!`),
|
|
254
|
-
HumanMessagePromptTemplate.fromTemplate(
|
|
251
|
+
HumanMessagePromptTemplate.fromTemplate('{text}'),
|
|
255
252
|
]);
|
|
256
253
|
|
|
257
254
|
// @eslint-disable-next-line/@typescript-eslint/ban-ts-comment
|
|
258
255
|
const responseA = await chat.generatePrompt([
|
|
259
256
|
await chatPrompt.formatPromptValue({
|
|
260
|
-
text:
|
|
257
|
+
text: 'What did I just say my name was?',
|
|
261
258
|
}),
|
|
262
259
|
]);
|
|
263
260
|
|
|
264
261
|
// console.log(responseA.generations);
|
|
265
262
|
});
|
|
266
263
|
|
|
267
|
-
test.skip(
|
|
264
|
+
test.skip('ChatAnthropic, Anthropic apiUrl set manually via constructor', async () => {
|
|
268
265
|
// Pass the default URL through (should use this, and work as normal)
|
|
269
|
-
const anthropicApiUrl =
|
|
266
|
+
const anthropicApiUrl = 'https://api.anthropic.com';
|
|
270
267
|
const chat = new ChatAnthropic({
|
|
271
|
-
modelName:
|
|
268
|
+
modelName: 'claude-3-sonnet-20240229',
|
|
272
269
|
maxRetries: 0,
|
|
273
270
|
anthropicApiUrl,
|
|
274
271
|
});
|
|
275
|
-
const message = new HumanMessage(
|
|
272
|
+
const message = new HumanMessage('Hello!');
|
|
276
273
|
// @eslint-disable-next-line/@typescript-eslint/ban-ts-comment
|
|
277
274
|
const res = await chat.call([message]);
|
|
278
275
|
// console.log({ res });
|
|
279
276
|
});
|
|
280
277
|
|
|
281
|
-
test(
|
|
278
|
+
test('Test ChatAnthropic stream method', async () => {
|
|
282
279
|
const model = new ChatAnthropic({
|
|
283
280
|
maxTokens: 50,
|
|
284
281
|
maxRetries: 0,
|
|
285
|
-
modelName:
|
|
282
|
+
modelName: 'claude-3-sonnet-20240229',
|
|
286
283
|
});
|
|
287
|
-
const stream = await model.stream(
|
|
284
|
+
const stream = await model.stream('Print hello world.');
|
|
288
285
|
const chunks: any[] = [];
|
|
289
286
|
for await (const chunk of stream) {
|
|
290
287
|
chunks.push(chunk);
|
|
@@ -292,15 +289,15 @@ test("Test ChatAnthropic stream method", async () => {
|
|
|
292
289
|
expect(chunks.length).toBeGreaterThan(1);
|
|
293
290
|
});
|
|
294
291
|
|
|
295
|
-
test(
|
|
292
|
+
test('Test ChatAnthropic stream method with abort', async () => {
|
|
296
293
|
await expect(async () => {
|
|
297
294
|
const model = new ChatAnthropic({
|
|
298
295
|
maxTokens: 500,
|
|
299
296
|
maxRetries: 0,
|
|
300
|
-
modelName:
|
|
297
|
+
modelName: 'claude-3-sonnet-20240229',
|
|
301
298
|
});
|
|
302
299
|
const stream = await model.stream(
|
|
303
|
-
|
|
300
|
+
'How is your day going? Be extremely verbose.',
|
|
304
301
|
{
|
|
305
302
|
signal: AbortSignal.timeout(1000),
|
|
306
303
|
}
|
|
@@ -312,14 +309,14 @@ test("Test ChatAnthropic stream method with abort", async () => {
|
|
|
312
309
|
}).rejects.toThrow();
|
|
313
310
|
});
|
|
314
311
|
|
|
315
|
-
test(
|
|
312
|
+
test('Test ChatAnthropic stream method with early break', async () => {
|
|
316
313
|
const model = new ChatAnthropic({
|
|
317
314
|
maxTokens: 50,
|
|
318
315
|
maxRetries: 0,
|
|
319
|
-
modelName:
|
|
316
|
+
modelName: 'claude-3-sonnet-20240229',
|
|
320
317
|
});
|
|
321
318
|
const stream = await model.stream(
|
|
322
|
-
|
|
319
|
+
'How is your day going? Be extremely verbose.'
|
|
323
320
|
);
|
|
324
321
|
let i = 0;
|
|
325
322
|
// @eslint-disable-next-line/@typescript-eslint/ban-ts-comment
|
|
@@ -332,26 +329,26 @@ test("Test ChatAnthropic stream method with early break", async () => {
|
|
|
332
329
|
}
|
|
333
330
|
});
|
|
334
331
|
|
|
335
|
-
test(
|
|
332
|
+
test('Test ChatAnthropic headers passed through', async () => {
|
|
336
333
|
const chat = new ChatAnthropic({
|
|
337
|
-
modelName:
|
|
334
|
+
modelName: 'claude-3-sonnet-20240229',
|
|
338
335
|
maxRetries: 0,
|
|
339
|
-
apiKey:
|
|
336
|
+
apiKey: 'NOT_REAL',
|
|
340
337
|
clientOptions: {
|
|
341
338
|
defaultHeaders: {
|
|
342
|
-
|
|
339
|
+
'X-Api-Key': process.env.ANTHROPIC_API_KEY,
|
|
343
340
|
},
|
|
344
341
|
},
|
|
345
342
|
});
|
|
346
|
-
const message = new HumanMessage(
|
|
343
|
+
const message = new HumanMessage('Hello!');
|
|
347
344
|
// @eslint-disable-next-line/@typescript-eslint/ban-ts-comment
|
|
348
345
|
const res = await chat.invoke([message]);
|
|
349
346
|
// console.log({ res });
|
|
350
347
|
});
|
|
351
348
|
|
|
352
|
-
test(
|
|
349
|
+
test('Test ChatAnthropic multimodal', async () => {
|
|
353
350
|
const chat = new ChatAnthropic({
|
|
354
|
-
modelName:
|
|
351
|
+
modelName: 'claude-3-sonnet-20240229',
|
|
355
352
|
maxRetries: 0,
|
|
356
353
|
});
|
|
357
354
|
// @eslint-disable-next-line/@typescript-eslint/ban-ts-comment
|
|
@@ -359,27 +356,27 @@ test("Test ChatAnthropic multimodal", async () => {
|
|
|
359
356
|
new HumanMessage({
|
|
360
357
|
content: [
|
|
361
358
|
{
|
|
362
|
-
type:
|
|
359
|
+
type: 'image_url',
|
|
363
360
|
image_url: {
|
|
364
|
-
url:
|
|
361
|
+
url: 'data:image/jpeg;base64,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',
|
|
365
362
|
},
|
|
366
363
|
},
|
|
367
|
-
{ type:
|
|
364
|
+
{ type: 'text', text: 'What is this a logo for?' },
|
|
368
365
|
],
|
|
369
366
|
}),
|
|
370
367
|
]);
|
|
371
368
|
// console.log(res);
|
|
372
369
|
});
|
|
373
370
|
|
|
374
|
-
test(
|
|
371
|
+
test('Stream tokens', async () => {
|
|
375
372
|
const model = new ChatAnthropic({
|
|
376
|
-
model:
|
|
373
|
+
model: 'claude-3-haiku-20240307',
|
|
377
374
|
temperature: 0,
|
|
378
375
|
maxTokens: 10,
|
|
379
376
|
});
|
|
380
377
|
let res: AIMessageChunk | null = null;
|
|
381
378
|
for await (const chunk of await model.stream(
|
|
382
|
-
|
|
379
|
+
'Why is the sky blue? Be concise.'
|
|
383
380
|
)) {
|
|
384
381
|
if (!res) {
|
|
385
382
|
res = chunk;
|
|
@@ -399,23 +396,23 @@ test("Stream tokens", async () => {
|
|
|
399
396
|
);
|
|
400
397
|
});
|
|
401
398
|
|
|
402
|
-
test(
|
|
399
|
+
test('id is supplied when invoking', async () => {
|
|
403
400
|
const model = new ChatAnthropic();
|
|
404
|
-
const result = await model.invoke(
|
|
401
|
+
const result = await model.invoke('Hello');
|
|
405
402
|
expect(result.id).toBeDefined();
|
|
406
|
-
expect(result.id).not.toEqual(
|
|
403
|
+
expect(result.id).not.toEqual('');
|
|
407
404
|
});
|
|
408
405
|
|
|
409
|
-
test(
|
|
406
|
+
test('id is supplied when streaming', async () => {
|
|
410
407
|
const model = new ChatAnthropic();
|
|
411
408
|
let finalChunk: AIMessageChunk | undefined;
|
|
412
|
-
for await (const chunk of await model.stream(
|
|
409
|
+
for await (const chunk of await model.stream('Hello')) {
|
|
413
410
|
finalChunk = !finalChunk ? chunk : concat(finalChunk, chunk);
|
|
414
411
|
}
|
|
415
412
|
expect(finalChunk).toBeDefined();
|
|
416
413
|
if (!finalChunk) return;
|
|
417
414
|
expect(finalChunk.id).toBeDefined();
|
|
418
|
-
expect(finalChunk.id).not.toEqual(
|
|
415
|
+
expect(finalChunk.id).not.toEqual('');
|
|
419
416
|
});
|
|
420
417
|
|
|
421
418
|
const CACHED_TEXT = `## Components
|
|
@@ -679,12 +676,12 @@ LangChain has many different types of output parsers. This is a list of output p
|
|
|
679
676
|
|
|
680
677
|
The current date is ${new Date().toISOString()}`;
|
|
681
678
|
|
|
682
|
-
test(
|
|
679
|
+
test('system prompt caching', async () => {
|
|
683
680
|
const model = new ChatAnthropic({
|
|
684
|
-
model:
|
|
681
|
+
model: 'claude-3-haiku-20240307',
|
|
685
682
|
clientOptions: {
|
|
686
683
|
defaultHeaders: {
|
|
687
|
-
|
|
684
|
+
'anthropic-beta': 'prompt-caching-2024-07-31',
|
|
688
685
|
},
|
|
689
686
|
},
|
|
690
687
|
});
|
|
@@ -692,14 +689,14 @@ test("system prompt caching", async () => {
|
|
|
692
689
|
new SystemMessage({
|
|
693
690
|
content: [
|
|
694
691
|
{
|
|
695
|
-
type:
|
|
692
|
+
type: 'text',
|
|
696
693
|
text: `You are a pirate. Always respond in pirate dialect.\nUse the following as context when answering questions: ${CACHED_TEXT}`,
|
|
697
|
-
cache_control: { type:
|
|
694
|
+
cache_control: { type: 'ephemeral' },
|
|
698
695
|
},
|
|
699
696
|
],
|
|
700
697
|
}),
|
|
701
698
|
new HumanMessage({
|
|
702
|
-
content:
|
|
699
|
+
content: 'What types of messages are supported in LangChain?',
|
|
703
700
|
}),
|
|
704
701
|
];
|
|
705
702
|
const res = await model.invoke(messages);
|
|
@@ -725,38 +722,38 @@ test("system prompt caching", async () => {
|
|
|
725
722
|
});
|
|
726
723
|
|
|
727
724
|
// TODO: Add proper test with long tool content
|
|
728
|
-
test.skip(
|
|
725
|
+
test.skip('tool caching', async () => {
|
|
729
726
|
const model = new ChatAnthropic({
|
|
730
|
-
model:
|
|
727
|
+
model: 'claude-3-haiku-20240307',
|
|
731
728
|
clientOptions: {
|
|
732
729
|
defaultHeaders: {
|
|
733
|
-
|
|
730
|
+
'anthropic-beta': 'prompt-caching-2024-07-31',
|
|
734
731
|
},
|
|
735
732
|
},
|
|
736
733
|
}).bindTools([
|
|
737
734
|
{
|
|
738
|
-
name:
|
|
739
|
-
description:
|
|
735
|
+
name: 'get_weather',
|
|
736
|
+
description: 'Get the weather for a specific location',
|
|
740
737
|
input_schema: {
|
|
741
|
-
type:
|
|
738
|
+
type: 'object',
|
|
742
739
|
properties: {
|
|
743
740
|
location: {
|
|
744
|
-
type:
|
|
745
|
-
description:
|
|
741
|
+
type: 'string',
|
|
742
|
+
description: 'Location to get the weather for',
|
|
746
743
|
},
|
|
747
744
|
unit: {
|
|
748
|
-
type:
|
|
749
|
-
description:
|
|
745
|
+
type: 'string',
|
|
746
|
+
description: 'Temperature unit to return',
|
|
750
747
|
},
|
|
751
748
|
},
|
|
752
|
-
required: [
|
|
749
|
+
required: ['location'],
|
|
753
750
|
},
|
|
754
|
-
cache_control: { type:
|
|
751
|
+
cache_control: { type: 'ephemeral' },
|
|
755
752
|
},
|
|
756
753
|
]);
|
|
757
754
|
const messages = [
|
|
758
755
|
new HumanMessage({
|
|
759
|
-
content:
|
|
756
|
+
content: 'What is the weather in Regensburg?',
|
|
760
757
|
}),
|
|
761
758
|
];
|
|
762
759
|
const res = await model.invoke(messages);
|
|
@@ -772,12 +769,12 @@ test.skip("tool caching", async () => {
|
|
|
772
769
|
);
|
|
773
770
|
});
|
|
774
771
|
|
|
775
|
-
test(
|
|
772
|
+
test('human message caching', async () => {
|
|
776
773
|
const model = new ChatAnthropic({
|
|
777
|
-
model:
|
|
774
|
+
model: 'claude-3-haiku-20240307',
|
|
778
775
|
clientOptions: {
|
|
779
776
|
defaultHeaders: {
|
|
780
|
-
|
|
777
|
+
'anthropic-beta': 'prompt-caching-2024-07-31',
|
|
781
778
|
},
|
|
782
779
|
},
|
|
783
780
|
});
|
|
@@ -786,7 +783,7 @@ test("human message caching", async () => {
|
|
|
786
783
|
new SystemMessage({
|
|
787
784
|
content: [
|
|
788
785
|
{
|
|
789
|
-
type:
|
|
786
|
+
type: 'text',
|
|
790
787
|
text: `You are a pirate. Always respond in pirate dialect.\nUse the following as context when answering questions: ${CACHED_TEXT}`,
|
|
791
788
|
},
|
|
792
789
|
],
|
|
@@ -794,9 +791,9 @@ test("human message caching", async () => {
|
|
|
794
791
|
new HumanMessage({
|
|
795
792
|
content: [
|
|
796
793
|
{
|
|
797
|
-
type:
|
|
798
|
-
text:
|
|
799
|
-
cache_control: { type:
|
|
794
|
+
type: 'text',
|
|
795
|
+
text: 'What types of messages are supported in LangChain?',
|
|
796
|
+
cache_control: { type: 'ephemeral' },
|
|
800
797
|
},
|
|
801
798
|
],
|
|
802
799
|
}),
|
|
@@ -814,31 +811,31 @@ test("human message caching", async () => {
|
|
|
814
811
|
);
|
|
815
812
|
});
|
|
816
813
|
|
|
817
|
-
test(
|
|
814
|
+
test('Can accept PDF documents', async () => {
|
|
818
815
|
const model = new ChatAnthropic({
|
|
819
|
-
model:
|
|
816
|
+
model: 'claude-3-5-sonnet-latest',
|
|
820
817
|
});
|
|
821
818
|
|
|
822
819
|
const pdfPath =
|
|
823
|
-
|
|
824
|
-
const pdfBase64 = await fs.readFile(pdfPath,
|
|
820
|
+
'../langchain-community/src/document_loaders/tests/example_data/Jacob_Lee_Resume_2023.pdf';
|
|
821
|
+
const pdfBase64 = await fs.readFile(pdfPath, 'base64');
|
|
825
822
|
|
|
826
823
|
const response = await model.invoke([
|
|
827
|
-
[
|
|
824
|
+
['system', 'Use the provided documents to answer the question'],
|
|
828
825
|
[
|
|
829
|
-
|
|
826
|
+
'user',
|
|
830
827
|
[
|
|
831
828
|
{
|
|
832
|
-
type:
|
|
829
|
+
type: 'document',
|
|
833
830
|
source: {
|
|
834
|
-
type:
|
|
835
|
-
media_type:
|
|
831
|
+
type: 'base64',
|
|
832
|
+
media_type: 'application/pdf',
|
|
836
833
|
data: pdfBase64,
|
|
837
834
|
},
|
|
838
835
|
},
|
|
839
836
|
{
|
|
840
|
-
type:
|
|
841
|
-
text:
|
|
837
|
+
type: 'text',
|
|
838
|
+
text: 'Summarize the contents of this PDF',
|
|
842
839
|
},
|
|
843
840
|
],
|
|
844
841
|
],
|
|
@@ -847,31 +844,31 @@ test("Can accept PDF documents", async () => {
|
|
|
847
844
|
expect(response.content.length).toBeGreaterThan(10);
|
|
848
845
|
});
|
|
849
846
|
|
|
850
|
-
test(
|
|
847
|
+
test('Citations', async () => {
|
|
851
848
|
const citationsModel = new ChatAnthropic({
|
|
852
|
-
model:
|
|
849
|
+
model: 'claude-3-5-sonnet-latest',
|
|
853
850
|
});
|
|
854
851
|
|
|
855
852
|
const messages = [
|
|
856
853
|
{
|
|
857
|
-
role:
|
|
854
|
+
role: 'user',
|
|
858
855
|
content: [
|
|
859
856
|
{
|
|
860
|
-
type:
|
|
857
|
+
type: 'document',
|
|
861
858
|
source: {
|
|
862
|
-
type:
|
|
863
|
-
media_type:
|
|
859
|
+
type: 'text',
|
|
860
|
+
media_type: 'text/plain',
|
|
864
861
|
data: "The grass the user is asking about is bluegrass. The sky is orange because it's night.",
|
|
865
862
|
},
|
|
866
|
-
title:
|
|
867
|
-
context:
|
|
863
|
+
title: 'My Document',
|
|
864
|
+
context: 'This is a trustworthy document.',
|
|
868
865
|
citations: {
|
|
869
866
|
enabled: true,
|
|
870
867
|
},
|
|
871
868
|
},
|
|
872
869
|
{
|
|
873
|
-
type:
|
|
874
|
-
text:
|
|
870
|
+
type: 'text',
|
|
871
|
+
text: 'What color is the grass and sky?',
|
|
875
872
|
},
|
|
876
873
|
],
|
|
877
874
|
},
|
|
@@ -885,7 +882,7 @@ test("Citations", async () => {
|
|
|
885
882
|
(block) => block.citations !== undefined
|
|
886
883
|
);
|
|
887
884
|
expect(blocksWithCitations.length).toEqual(2);
|
|
888
|
-
expect(typeof blocksWithCitations[0].citations[0]).toEqual(
|
|
885
|
+
expect(typeof blocksWithCitations[0].citations[0]).toEqual('object');
|
|
889
886
|
|
|
890
887
|
const stream = await citationsModel.stream(messages);
|
|
891
888
|
let aggregated;
|
|
@@ -908,11 +905,11 @@ test("Citations", async () => {
|
|
|
908
905
|
).toBe(true);
|
|
909
906
|
});
|
|
910
907
|
|
|
911
|
-
test(
|
|
908
|
+
test('Test thinking blocks multiturn invoke', async () => {
|
|
912
909
|
const model = new ChatAnthropic({
|
|
913
|
-
model:
|
|
910
|
+
model: 'claude-3-7-sonnet-latest',
|
|
914
911
|
maxTokens: 5000,
|
|
915
|
-
thinking: { type:
|
|
912
|
+
thinking: { type: 'enabled', budget_tokens: 2000 },
|
|
916
913
|
});
|
|
917
914
|
|
|
918
915
|
async function doInvoke(messages: BaseMessage[]) {
|
|
@@ -920,37 +917,37 @@ test("Test thinking blocks multiturn invoke", async () => {
|
|
|
920
917
|
|
|
921
918
|
expect(Array.isArray(response.content)).toBe(true);
|
|
922
919
|
const content = response.content as AnthropicMessageResponse[];
|
|
923
|
-
expect(content.some((block) =>
|
|
920
|
+
expect(content.some((block) => 'thinking' in (block as any))).toBe(true);
|
|
924
921
|
|
|
925
922
|
for (const block of response.content) {
|
|
926
|
-
expect(typeof block).toBe(
|
|
927
|
-
if ((block as any).type ===
|
|
923
|
+
expect(typeof block).toBe('object');
|
|
924
|
+
if ((block as any).type === 'thinking') {
|
|
928
925
|
expect(Object.keys(block).sort()).toEqual(
|
|
929
|
-
[
|
|
926
|
+
['type', 'thinking', 'signature'].sort()
|
|
930
927
|
);
|
|
931
928
|
expect((block as any).thinking).toBeTruthy();
|
|
932
|
-
expect(typeof (block as any).thinking).toBe(
|
|
929
|
+
expect(typeof (block as any).thinking).toBe('string');
|
|
933
930
|
expect((block as any).signature).toBeTruthy();
|
|
934
|
-
expect(typeof (block as any).signature).toBe(
|
|
931
|
+
expect(typeof (block as any).signature).toBe('string');
|
|
935
932
|
}
|
|
936
933
|
}
|
|
937
934
|
return response;
|
|
938
935
|
}
|
|
939
936
|
|
|
940
|
-
const invokeMessages = [new HumanMessage(
|
|
937
|
+
const invokeMessages = [new HumanMessage('Hello')];
|
|
941
938
|
|
|
942
939
|
invokeMessages.push(await doInvoke(invokeMessages));
|
|
943
|
-
invokeMessages.push(new HumanMessage(
|
|
940
|
+
invokeMessages.push(new HumanMessage('What is 42+7?'));
|
|
944
941
|
|
|
945
942
|
// test a second time to make sure that we've got input translation working correctly
|
|
946
943
|
await model.invoke(invokeMessages);
|
|
947
944
|
});
|
|
948
945
|
|
|
949
|
-
test(
|
|
946
|
+
test('Test thinking blocks multiturn streaming', async () => {
|
|
950
947
|
const model = new ChatAnthropic({
|
|
951
|
-
model:
|
|
948
|
+
model: 'claude-3-7-sonnet-latest',
|
|
952
949
|
maxTokens: 5000,
|
|
953
|
-
thinking: { type:
|
|
950
|
+
thinking: { type: 'enabled', budget_tokens: 2000 },
|
|
954
951
|
});
|
|
955
952
|
|
|
956
953
|
async function doStreaming(messages: BaseMessage[]) {
|
|
@@ -961,37 +958,37 @@ test("Test thinking blocks multiturn streaming", async () => {
|
|
|
961
958
|
expect(full).toBeInstanceOf(AIMessageChunk);
|
|
962
959
|
expect(Array.isArray(full?.content)).toBe(true);
|
|
963
960
|
const content3 = full?.content as AnthropicMessageResponse[];
|
|
964
|
-
expect(content3.some((block) =>
|
|
961
|
+
expect(content3.some((block) => 'thinking' in (block as any))).toBe(true);
|
|
965
962
|
|
|
966
963
|
for (const block of full?.content || []) {
|
|
967
|
-
expect(typeof block).toBe(
|
|
968
|
-
if ((block as any).type ===
|
|
964
|
+
expect(typeof block).toBe('object');
|
|
965
|
+
if ((block as any).type === 'thinking') {
|
|
969
966
|
expect(Object.keys(block).sort()).toEqual(
|
|
970
|
-
[
|
|
967
|
+
['type', 'thinking', 'signature', 'index'].sort()
|
|
971
968
|
);
|
|
972
969
|
expect((block as any).thinking).toBeTruthy();
|
|
973
|
-
expect(typeof (block as any).thinking).toBe(
|
|
970
|
+
expect(typeof (block as any).thinking).toBe('string');
|
|
974
971
|
expect((block as any).signature).toBeTruthy();
|
|
975
|
-
expect(typeof (block as any).signature).toBe(
|
|
972
|
+
expect(typeof (block as any).signature).toBe('string');
|
|
976
973
|
}
|
|
977
974
|
}
|
|
978
975
|
return full as AIMessageChunk;
|
|
979
976
|
}
|
|
980
977
|
|
|
981
|
-
const streamingMessages = [new HumanMessage(
|
|
978
|
+
const streamingMessages = [new HumanMessage('Hello')];
|
|
982
979
|
|
|
983
980
|
streamingMessages.push(await doStreaming(streamingMessages));
|
|
984
|
-
streamingMessages.push(new HumanMessage(
|
|
981
|
+
streamingMessages.push(new HumanMessage('What is 42+7?'));
|
|
985
982
|
|
|
986
983
|
// test a second time to make sure that we've got input translation working correctly
|
|
987
984
|
await doStreaming(streamingMessages);
|
|
988
985
|
});
|
|
989
986
|
|
|
990
|
-
test(
|
|
987
|
+
test('Test redacted thinking blocks multiturn invoke', async () => {
|
|
991
988
|
const model = new ChatAnthropic({
|
|
992
|
-
model:
|
|
989
|
+
model: 'claude-3-7-sonnet-latest',
|
|
993
990
|
maxTokens: 5000,
|
|
994
|
-
thinking: { type:
|
|
991
|
+
thinking: { type: 'enabled', budget_tokens: 2000 },
|
|
995
992
|
});
|
|
996
993
|
|
|
997
994
|
async function doInvoke(messages: BaseMessage[]) {
|
|
@@ -999,12 +996,12 @@ test("Test redacted thinking blocks multiturn invoke", async () => {
|
|
|
999
996
|
let hasReasoning = false;
|
|
1000
997
|
|
|
1001
998
|
for (const block of response.content) {
|
|
1002
|
-
expect(typeof block).toBe(
|
|
1003
|
-
if ((block as any).type ===
|
|
999
|
+
expect(typeof block).toBe('object');
|
|
1000
|
+
if ((block as any).type === 'redacted_thinking') {
|
|
1004
1001
|
hasReasoning = true;
|
|
1005
|
-
expect(Object.keys(block).sort()).toEqual([
|
|
1002
|
+
expect(Object.keys(block).sort()).toEqual(['type', 'data'].sort());
|
|
1006
1003
|
expect((block as any).data).toBeTruthy();
|
|
1007
|
-
expect(typeof (block as any).data).toBe(
|
|
1004
|
+
expect(typeof (block as any).data).toBe('string');
|
|
1008
1005
|
}
|
|
1009
1006
|
}
|
|
1010
1007
|
expect(hasReasoning).toBe(true);
|
|
@@ -1013,22 +1010,22 @@ test("Test redacted thinking blocks multiturn invoke", async () => {
|
|
|
1013
1010
|
|
|
1014
1011
|
const invokeMessages = [
|
|
1015
1012
|
new HumanMessage(
|
|
1016
|
-
|
|
1013
|
+
'ANTHROPIC_MAGIC_STRING_TRIGGER_REDACTED_THINKING_46C9A13E193C177646C7398A98432ECCCE4C1253D5E2D82641AC0E52CC2876CB'
|
|
1017
1014
|
),
|
|
1018
1015
|
];
|
|
1019
1016
|
|
|
1020
1017
|
invokeMessages.push(await doInvoke(invokeMessages));
|
|
1021
|
-
invokeMessages.push(new HumanMessage(
|
|
1018
|
+
invokeMessages.push(new HumanMessage('What is 42+7?'));
|
|
1022
1019
|
|
|
1023
1020
|
// test a second time to make sure that we've got input translation working correctly
|
|
1024
1021
|
await doInvoke(invokeMessages);
|
|
1025
1022
|
});
|
|
1026
1023
|
|
|
1027
|
-
test(
|
|
1024
|
+
test('Test redacted thinking blocks multiturn streaming', async () => {
|
|
1028
1025
|
const model = new ChatAnthropic({
|
|
1029
|
-
model:
|
|
1026
|
+
model: 'claude-3-7-sonnet-latest',
|
|
1030
1027
|
maxTokens: 5000,
|
|
1031
|
-
thinking: { type:
|
|
1028
|
+
thinking: { type: 'enabled', budget_tokens: 2000 },
|
|
1032
1029
|
});
|
|
1033
1030
|
|
|
1034
1031
|
async function doStreaming(messages: BaseMessage[]) {
|
|
@@ -1041,14 +1038,14 @@ test("Test redacted thinking blocks multiturn streaming", async () => {
|
|
|
1041
1038
|
let streamHasReasoning = false;
|
|
1042
1039
|
|
|
1043
1040
|
for (const block of full?.content || []) {
|
|
1044
|
-
expect(typeof block).toBe(
|
|
1045
|
-
if ((block as any).type ===
|
|
1041
|
+
expect(typeof block).toBe('object');
|
|
1042
|
+
if ((block as any).type === 'redacted_thinking') {
|
|
1046
1043
|
streamHasReasoning = true;
|
|
1047
1044
|
expect(Object.keys(block).sort()).toEqual(
|
|
1048
|
-
[
|
|
1045
|
+
['type', 'data', 'index'].sort()
|
|
1049
1046
|
);
|
|
1050
1047
|
expect((block as any).data).toBeTruthy();
|
|
1051
|
-
expect(typeof (block as any).data).toBe(
|
|
1048
|
+
expect(typeof (block as any).data).toBe('string');
|
|
1052
1049
|
}
|
|
1053
1050
|
}
|
|
1054
1051
|
expect(streamHasReasoning).toBe(true);
|
|
@@ -1057,13 +1054,13 @@ test("Test redacted thinking blocks multiturn streaming", async () => {
|
|
|
1057
1054
|
|
|
1058
1055
|
const streamingMessages = [
|
|
1059
1056
|
new HumanMessage(
|
|
1060
|
-
|
|
1057
|
+
'ANTHROPIC_MAGIC_STRING_TRIGGER_REDACTED_THINKING_46C9A13E193C177646C7398A98432ECCCE4C1253D5E2D82641AC0E52CC2876CB'
|
|
1061
1058
|
),
|
|
1062
1059
|
];
|
|
1063
1060
|
|
|
1064
1061
|
streamingMessages.push(await doStreaming(streamingMessages));
|
|
1065
|
-
streamingMessages.push(new HumanMessage(
|
|
1062
|
+
streamingMessages.push(new HumanMessage('What is 42+7?'));
|
|
1066
1063
|
|
|
1067
1064
|
// test a second time to make sure that we've got input translation working correctly
|
|
1068
1065
|
await doStreaming(streamingMessages);
|
|
1069
|
-
});
|
|
1066
|
+
});
|