@langchain/anthropic 0.1.13 → 0.1.15
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/chat_models.cjs +216 -130
- package/dist/chat_models.d.ts +8 -15
- package/dist/chat_models.js +216 -131
- package/dist/output_parsers.cjs +14 -7
- package/dist/output_parsers.d.ts +2 -0
- package/dist/output_parsers.js +12 -6
- package/dist/tests/agent.int.test.d.ts +1 -0
- package/dist/tests/agent.int.test.js +39 -0
- package/dist/tests/chat_models-tools.int.test.d.ts +1 -0
- package/dist/tests/chat_models-tools.int.test.js +218 -0
- package/dist/tests/chat_models.int.test.js +0 -178
- package/package.json +2 -2
package/dist/chat_models.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { Anthropic } from "@anthropic-ai/sdk";
|
|
2
|
-
import { AIMessage, AIMessageChunk, } from "@langchain/core/messages";
|
|
2
|
+
import { AIMessage, AIMessageChunk, HumanMessage, isAIMessage, } from "@langchain/core/messages";
|
|
3
3
|
import { ChatGenerationChunk, } from "@langchain/core/outputs";
|
|
4
4
|
import { getEnvironmentVariable } from "@langchain/core/utils/env";
|
|
5
5
|
import { BaseChatModel, } from "@langchain/core/language_models/chat_models";
|
|
6
6
|
import { zodToJsonSchema } from "zod-to-json-schema";
|
|
7
7
|
import { RunnablePassthrough, RunnableSequence, } from "@langchain/core/runnables";
|
|
8
8
|
import { isZodSchema } from "@langchain/core/utils/types";
|
|
9
|
-
import { AnthropicToolsOutputParser } from "./output_parsers.js";
|
|
9
|
+
import { AnthropicToolsOutputParser, extractToolCalls, } from "./output_parsers.js";
|
|
10
10
|
function _formatImage(imageUrl) {
|
|
11
11
|
const regex = /^data:(image\/.+);base64,(.+)$/;
|
|
12
12
|
const match = imageUrl.match(regex);
|
|
@@ -33,14 +33,15 @@ function anthropicResponseToChatMessages(messages, additionalKwargs) {
|
|
|
33
33
|
];
|
|
34
34
|
}
|
|
35
35
|
else {
|
|
36
|
-
|
|
37
|
-
const castMessage = messages;
|
|
36
|
+
const toolCalls = extractToolCalls(messages);
|
|
38
37
|
const generations = [
|
|
39
38
|
{
|
|
40
39
|
text: "",
|
|
41
40
|
message: new AIMessage({
|
|
42
|
-
|
|
41
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
42
|
+
content: messages,
|
|
43
43
|
additional_kwargs: additionalKwargs,
|
|
44
|
+
tool_calls: toolCalls,
|
|
44
45
|
}),
|
|
45
46
|
},
|
|
46
47
|
];
|
|
@@ -51,6 +52,179 @@ function anthropicResponseToChatMessages(messages, additionalKwargs) {
|
|
|
51
52
|
function isAnthropicTool(tool) {
|
|
52
53
|
return "input_schema" in tool;
|
|
53
54
|
}
|
|
55
|
+
function _mergeMessages(messages) {
|
|
56
|
+
// Merge runs of human/tool messages into single human messages with content blocks.
|
|
57
|
+
const merged = [];
|
|
58
|
+
for (const message of messages) {
|
|
59
|
+
if (message._getType() === "tool") {
|
|
60
|
+
if (typeof message.content === "string") {
|
|
61
|
+
merged.push(new HumanMessage({
|
|
62
|
+
content: [
|
|
63
|
+
{
|
|
64
|
+
type: "tool_result",
|
|
65
|
+
content: message.content,
|
|
66
|
+
tool_use_id: message.tool_call_id,
|
|
67
|
+
},
|
|
68
|
+
],
|
|
69
|
+
}));
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
merged.push(new HumanMessage({ content: message.content }));
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
const previousMessage = merged[merged.length - 1];
|
|
77
|
+
if (previousMessage?._getType() === "human" &&
|
|
78
|
+
message._getType() === "human") {
|
|
79
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
80
|
+
let combinedContent;
|
|
81
|
+
if (typeof previousMessage.content === "string") {
|
|
82
|
+
combinedContent = [{ type: "text", text: previousMessage.content }];
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
combinedContent = previousMessage.content;
|
|
86
|
+
}
|
|
87
|
+
if (typeof message.content === "string") {
|
|
88
|
+
combinedContent.push({ type: "text", text: message.content });
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
combinedContent = combinedContent.concat(message.content);
|
|
92
|
+
}
|
|
93
|
+
previousMessage.content = combinedContent;
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
merged.push(message);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return merged;
|
|
101
|
+
}
|
|
102
|
+
export function _convertLangChainToolCallToAnthropic(toolCall) {
|
|
103
|
+
if (toolCall.id === undefined) {
|
|
104
|
+
throw new Error(`Anthropic requires all tool calls to have an "id".`);
|
|
105
|
+
}
|
|
106
|
+
return {
|
|
107
|
+
type: "tool_use",
|
|
108
|
+
id: toolCall.id,
|
|
109
|
+
name: toolCall.name,
|
|
110
|
+
input: toolCall.args,
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
function _formatContent(content) {
|
|
114
|
+
if (typeof content === "string") {
|
|
115
|
+
return content;
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
const contentBlocks = content.map((contentPart) => {
|
|
119
|
+
if (contentPart.type === "image_url") {
|
|
120
|
+
let source;
|
|
121
|
+
if (typeof contentPart.image_url === "string") {
|
|
122
|
+
source = _formatImage(contentPart.image_url);
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
source = _formatImage(contentPart.image_url.url);
|
|
126
|
+
}
|
|
127
|
+
return {
|
|
128
|
+
type: "image",
|
|
129
|
+
source,
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
else if (contentPart.type === "text") {
|
|
133
|
+
// Assuming contentPart is of type MessageContentText here
|
|
134
|
+
return {
|
|
135
|
+
type: "text",
|
|
136
|
+
text: contentPart.text,
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
else if (contentPart.type === "tool_use" ||
|
|
140
|
+
contentPart.type === "tool_result") {
|
|
141
|
+
// TODO: Fix when SDK types are fixed
|
|
142
|
+
return {
|
|
143
|
+
...contentPart,
|
|
144
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
else {
|
|
148
|
+
throw new Error("Unsupported message content format");
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
return contentBlocks;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Formats messages as a prompt for the model.
|
|
156
|
+
* @param messages The base messages to format as a prompt.
|
|
157
|
+
* @returns The formatted prompt.
|
|
158
|
+
*/
|
|
159
|
+
function _formatMessagesForAnthropic(messages) {
|
|
160
|
+
const mergedMessages = _mergeMessages(messages);
|
|
161
|
+
let system;
|
|
162
|
+
if (mergedMessages.length > 0 && mergedMessages[0]._getType() === "system") {
|
|
163
|
+
if (typeof messages[0].content !== "string") {
|
|
164
|
+
throw new Error("System message content must be a string.");
|
|
165
|
+
}
|
|
166
|
+
system = messages[0].content;
|
|
167
|
+
}
|
|
168
|
+
const conversationMessages = system !== undefined ? mergedMessages.slice(1) : mergedMessages;
|
|
169
|
+
const formattedMessages = conversationMessages.map((message) => {
|
|
170
|
+
let role;
|
|
171
|
+
if (message._getType() === "human") {
|
|
172
|
+
role = "user";
|
|
173
|
+
}
|
|
174
|
+
else if (message._getType() === "ai") {
|
|
175
|
+
role = "assistant";
|
|
176
|
+
}
|
|
177
|
+
else if (message._getType() === "tool") {
|
|
178
|
+
role = "user";
|
|
179
|
+
}
|
|
180
|
+
else if (message._getType() === "system") {
|
|
181
|
+
throw new Error("System messages are only permitted as the first passed message.");
|
|
182
|
+
}
|
|
183
|
+
else {
|
|
184
|
+
throw new Error(`Message type "${message._getType()}" is not supported.`);
|
|
185
|
+
}
|
|
186
|
+
if (isAIMessage(message) && !!message.tool_calls?.length) {
|
|
187
|
+
if (typeof message.content === "string") {
|
|
188
|
+
if (message.content === "") {
|
|
189
|
+
return {
|
|
190
|
+
role,
|
|
191
|
+
content: message.tool_calls.map(_convertLangChainToolCallToAnthropic),
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
else {
|
|
195
|
+
return {
|
|
196
|
+
role,
|
|
197
|
+
content: [
|
|
198
|
+
{ type: "text", text: message.content },
|
|
199
|
+
...message.tool_calls.map(_convertLangChainToolCallToAnthropic),
|
|
200
|
+
],
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
else {
|
|
205
|
+
const { content } = message;
|
|
206
|
+
const hasMismatchedToolCalls = !message.tool_calls.every((toolCall) => content.find((contentPart) => contentPart.type === "tool_use" && contentPart.id === toolCall.id));
|
|
207
|
+
if (hasMismatchedToolCalls) {
|
|
208
|
+
console.warn(`The "tool_calls" field on a message is only respected if content is a string.`);
|
|
209
|
+
}
|
|
210
|
+
return {
|
|
211
|
+
role,
|
|
212
|
+
content: _formatContent(message.content),
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
else {
|
|
217
|
+
return {
|
|
218
|
+
role,
|
|
219
|
+
content: _formatContent(message.content),
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
});
|
|
223
|
+
return {
|
|
224
|
+
messages: formattedMessages,
|
|
225
|
+
system,
|
|
226
|
+
};
|
|
227
|
+
}
|
|
54
228
|
/**
|
|
55
229
|
* Wrapper around Anthropic large language models.
|
|
56
230
|
*
|
|
@@ -239,6 +413,12 @@ export class ChatAnthropicMessages extends BaseChatModel {
|
|
|
239
413
|
input_schema: zodToJsonSchema(tool.schema),
|
|
240
414
|
}));
|
|
241
415
|
}
|
|
416
|
+
bindTools(tools, kwargs) {
|
|
417
|
+
return this.bind({
|
|
418
|
+
tools: this.formatStructuredToolToAnthropic(tools),
|
|
419
|
+
...kwargs,
|
|
420
|
+
});
|
|
421
|
+
}
|
|
242
422
|
/**
|
|
243
423
|
* Get the parameters used to invoke the model
|
|
244
424
|
*/
|
|
@@ -251,30 +431,10 @@ export class ChatAnthropicMessages extends BaseChatModel {
|
|
|
251
431
|
stop_sequences: options?.stop ?? this.stopSequences,
|
|
252
432
|
stream: this.streaming,
|
|
253
433
|
max_tokens: this.maxTokens,
|
|
434
|
+
tools: this.formatStructuredToolToAnthropic(options?.tools),
|
|
254
435
|
...this.invocationKwargs,
|
|
255
436
|
};
|
|
256
437
|
}
|
|
257
|
-
invocationOptions(request, options) {
|
|
258
|
-
const toolUseBetaHeader = {
|
|
259
|
-
"anthropic-beta": "tools-2024-04-04",
|
|
260
|
-
};
|
|
261
|
-
const tools = this.formatStructuredToolToAnthropic(options?.tools);
|
|
262
|
-
// If tools are present, populate the body with the message request params.
|
|
263
|
-
// This is because Anthropic overwrites the message request params if a body
|
|
264
|
-
// is passed.
|
|
265
|
-
const body = tools
|
|
266
|
-
? {
|
|
267
|
-
...request,
|
|
268
|
-
tools,
|
|
269
|
-
}
|
|
270
|
-
: undefined;
|
|
271
|
-
const headers = tools ? toolUseBetaHeader : undefined;
|
|
272
|
-
return {
|
|
273
|
-
signal: options.signal,
|
|
274
|
-
...(body ? { body } : {}),
|
|
275
|
-
...(headers ? { headers } : {}),
|
|
276
|
-
};
|
|
277
|
-
}
|
|
278
438
|
/** @ignore */
|
|
279
439
|
_identifyingParams() {
|
|
280
440
|
return {
|
|
@@ -293,22 +453,23 @@ export class ChatAnthropicMessages extends BaseChatModel {
|
|
|
293
453
|
}
|
|
294
454
|
async *_streamResponseChunks(messages, options, runManager) {
|
|
295
455
|
const params = this.invocationParams(options);
|
|
296
|
-
const
|
|
297
|
-
...params,
|
|
298
|
-
stream: false,
|
|
299
|
-
...this.formatMessagesForAnthropic(messages),
|
|
300
|
-
}, options);
|
|
456
|
+
const formattedMessages = _formatMessagesForAnthropic(messages);
|
|
301
457
|
if (options.tools !== undefined && options.tools.length > 0) {
|
|
302
|
-
const
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
458
|
+
const generations = await this._generateNonStreaming(messages, params, {
|
|
459
|
+
signal: options.signal,
|
|
460
|
+
});
|
|
461
|
+
const result = generations[0].message;
|
|
462
|
+
const toolCallChunks = result.tool_calls?.map((toolCall, index) => ({
|
|
463
|
+
name: toolCall.name,
|
|
464
|
+
args: JSON.stringify(toolCall.args),
|
|
465
|
+
id: toolCall.id,
|
|
466
|
+
index,
|
|
467
|
+
}));
|
|
308
468
|
yield new ChatGenerationChunk({
|
|
309
469
|
message: new AIMessageChunk({
|
|
310
|
-
content:
|
|
311
|
-
additional_kwargs:
|
|
470
|
+
content: result.content,
|
|
471
|
+
additional_kwargs: result.additional_kwargs,
|
|
472
|
+
tool_call_chunks: toolCallChunks,
|
|
312
473
|
}),
|
|
313
474
|
text: generations[0].text,
|
|
314
475
|
});
|
|
@@ -316,9 +477,9 @@ export class ChatAnthropicMessages extends BaseChatModel {
|
|
|
316
477
|
else {
|
|
317
478
|
const stream = await this.createStreamWithRetry({
|
|
318
479
|
...params,
|
|
319
|
-
...
|
|
480
|
+
...formattedMessages,
|
|
320
481
|
stream: true,
|
|
321
|
-
}
|
|
482
|
+
});
|
|
322
483
|
let usageData = { input_tokens: 0, output_tokens: 0 };
|
|
323
484
|
for await (const data of stream) {
|
|
324
485
|
if (options.signal?.aborted) {
|
|
@@ -379,95 +540,22 @@ export class ChatAnthropicMessages extends BaseChatModel {
|
|
|
379
540
|
});
|
|
380
541
|
}
|
|
381
542
|
}
|
|
382
|
-
/**
|
|
383
|
-
* Formats messages as a prompt for the model.
|
|
384
|
-
* @param messages The base messages to format as a prompt.
|
|
385
|
-
* @returns The formatted prompt.
|
|
386
|
-
*/
|
|
387
|
-
formatMessagesForAnthropic(messages) {
|
|
388
|
-
let system;
|
|
389
|
-
if (messages.length > 0 && messages[0]._getType() === "system") {
|
|
390
|
-
if (typeof messages[0].content !== "string") {
|
|
391
|
-
throw new Error("System message content must be a string.");
|
|
392
|
-
}
|
|
393
|
-
system = messages[0].content;
|
|
394
|
-
}
|
|
395
|
-
const conversationMessages = system !== undefined ? messages.slice(1) : messages;
|
|
396
|
-
const formattedMessages = conversationMessages.map((message) => {
|
|
397
|
-
let role;
|
|
398
|
-
if (message._getType() === "human") {
|
|
399
|
-
role = "user";
|
|
400
|
-
}
|
|
401
|
-
else if (message._getType() === "ai") {
|
|
402
|
-
role = "assistant";
|
|
403
|
-
}
|
|
404
|
-
else if (message._getType() === "tool") {
|
|
405
|
-
role = "user";
|
|
406
|
-
}
|
|
407
|
-
else if (message._getType() === "system") {
|
|
408
|
-
throw new Error("System messages are only permitted as the first passed message.");
|
|
409
|
-
}
|
|
410
|
-
else {
|
|
411
|
-
throw new Error(`Message type "${message._getType()}" is not supported.`);
|
|
412
|
-
}
|
|
413
|
-
if (typeof message.content === "string") {
|
|
414
|
-
return {
|
|
415
|
-
role,
|
|
416
|
-
content: message.content,
|
|
417
|
-
};
|
|
418
|
-
}
|
|
419
|
-
else {
|
|
420
|
-
const contentBlocks = message.content.map((contentPart) => {
|
|
421
|
-
if (contentPart.type === "image_url") {
|
|
422
|
-
let source;
|
|
423
|
-
if (typeof contentPart.image_url === "string") {
|
|
424
|
-
source = _formatImage(contentPart.image_url);
|
|
425
|
-
}
|
|
426
|
-
else {
|
|
427
|
-
source = _formatImage(contentPart.image_url.url);
|
|
428
|
-
}
|
|
429
|
-
return {
|
|
430
|
-
type: "image",
|
|
431
|
-
source,
|
|
432
|
-
};
|
|
433
|
-
}
|
|
434
|
-
else if (contentPart.type === "text") {
|
|
435
|
-
// Assuming contentPart is of type MessageContentText here
|
|
436
|
-
return {
|
|
437
|
-
type: "text",
|
|
438
|
-
text: contentPart.text,
|
|
439
|
-
};
|
|
440
|
-
}
|
|
441
|
-
else if (contentPart.type === "tool_use" ||
|
|
442
|
-
contentPart.type === "tool_result") {
|
|
443
|
-
// TODO: Fix when SDK types are fixed
|
|
444
|
-
return {
|
|
445
|
-
...contentPart,
|
|
446
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
447
|
-
};
|
|
448
|
-
}
|
|
449
|
-
else {
|
|
450
|
-
throw new Error("Unsupported message content format");
|
|
451
|
-
}
|
|
452
|
-
});
|
|
453
|
-
return {
|
|
454
|
-
role,
|
|
455
|
-
content: contentBlocks,
|
|
456
|
-
};
|
|
457
|
-
}
|
|
458
|
-
});
|
|
459
|
-
return {
|
|
460
|
-
messages: formattedMessages,
|
|
461
|
-
system,
|
|
462
|
-
};
|
|
463
|
-
}
|
|
464
543
|
/** @ignore */
|
|
465
544
|
async _generateNonStreaming(messages, params, requestOptions) {
|
|
545
|
+
const options = params.tools !== undefined
|
|
546
|
+
? {
|
|
547
|
+
...requestOptions,
|
|
548
|
+
headers: {
|
|
549
|
+
...requestOptions.headers,
|
|
550
|
+
"anthropic-beta": "tools-2024-04-04",
|
|
551
|
+
},
|
|
552
|
+
}
|
|
553
|
+
: requestOptions;
|
|
466
554
|
const response = await this.completionWithRetry({
|
|
467
555
|
...params,
|
|
468
556
|
stream: false,
|
|
469
|
-
...
|
|
470
|
-
},
|
|
557
|
+
..._formatMessagesForAnthropic(messages),
|
|
558
|
+
}, options);
|
|
471
559
|
const { content, ...additionalKwargs } = response;
|
|
472
560
|
const generations = anthropicResponseToChatMessages(content, additionalKwargs);
|
|
473
561
|
return generations;
|
|
@@ -502,12 +590,9 @@ export class ChatAnthropicMessages extends BaseChatModel {
|
|
|
502
590
|
};
|
|
503
591
|
}
|
|
504
592
|
else {
|
|
505
|
-
const
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
...this.formatMessagesForAnthropic(messages),
|
|
509
|
-
}, options);
|
|
510
|
-
const generations = await this._generateNonStreaming(messages, params, requestOptions);
|
|
593
|
+
const generations = await this._generateNonStreaming(messages, params, {
|
|
594
|
+
signal: options.signal,
|
|
595
|
+
});
|
|
511
596
|
return {
|
|
512
597
|
generations,
|
|
513
598
|
};
|
package/dist/output_parsers.cjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.AnthropicToolsOutputParser = void 0;
|
|
3
|
+
exports.extractToolCalls = exports.AnthropicToolsOutputParser = void 0;
|
|
4
4
|
const output_parsers_1 = require("@langchain/core/output_parsers");
|
|
5
5
|
class AnthropicToolsOutputParser extends output_parsers_1.BaseLLMOutputParser {
|
|
6
6
|
static lc_name() {
|
|
@@ -56,25 +56,32 @@ class AnthropicToolsOutputParser extends output_parsers_1.BaseLLMOutputParser {
|
|
|
56
56
|
throw new output_parsers_1.OutputParserException(`Failed to parse. Text: "${JSON.stringify(result, null, 2)}". Error: ${JSON.stringify(zodParsedResult.error.errors)}`, JSON.stringify(result, null, 2));
|
|
57
57
|
}
|
|
58
58
|
}
|
|
59
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
60
59
|
async parseResult(generations) {
|
|
61
60
|
const tools = generations.flatMap((generation) => {
|
|
62
61
|
const { message } = generation;
|
|
63
|
-
if (typeof message === "string") {
|
|
64
|
-
return [];
|
|
65
|
-
}
|
|
66
62
|
if (!Array.isArray(message.content)) {
|
|
67
63
|
return [];
|
|
68
64
|
}
|
|
69
|
-
const tool = message.content
|
|
65
|
+
const tool = extractToolCalls(message.content)[0];
|
|
70
66
|
return tool;
|
|
71
67
|
});
|
|
72
68
|
if (tools[0] === undefined) {
|
|
73
69
|
throw new Error("No parseable tool calls provided to AnthropicToolsOutputParser.");
|
|
74
70
|
}
|
|
75
71
|
const [tool] = tools;
|
|
76
|
-
const validatedResult = await this._validateResult(tool.
|
|
72
|
+
const validatedResult = await this._validateResult(tool.args);
|
|
77
73
|
return validatedResult;
|
|
78
74
|
}
|
|
79
75
|
}
|
|
80
76
|
exports.AnthropicToolsOutputParser = AnthropicToolsOutputParser;
|
|
77
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
78
|
+
function extractToolCalls(content) {
|
|
79
|
+
const toolCalls = [];
|
|
80
|
+
for (const block of content) {
|
|
81
|
+
if (block.type === "tool_use") {
|
|
82
|
+
toolCalls.push({ name: block.name, args: block.input, id: block.id });
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return toolCalls;
|
|
86
|
+
}
|
|
87
|
+
exports.extractToolCalls = extractToolCalls;
|
package/dist/output_parsers.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { z } from "zod";
|
|
|
2
2
|
import { BaseLLMOutputParser } from "@langchain/core/output_parsers";
|
|
3
3
|
import { JsonOutputKeyToolsParserParams } from "@langchain/core/output_parsers/openai_tools";
|
|
4
4
|
import { ChatGeneration } from "@langchain/core/outputs";
|
|
5
|
+
import { ToolCall } from "@langchain/core/messages/tool";
|
|
5
6
|
interface AnthropicToolsOutputParserParams<T extends Record<string, any>> extends JsonOutputKeyToolsParserParams<T> {
|
|
6
7
|
}
|
|
7
8
|
export declare class AnthropicToolsOutputParser<T extends Record<string, any> = Record<string, any>> extends BaseLLMOutputParser<T> {
|
|
@@ -17,4 +18,5 @@ export declare class AnthropicToolsOutputParser<T extends Record<string, any> =
|
|
|
17
18
|
protected _validateResult(result: unknown): Promise<T>;
|
|
18
19
|
parseResult(generations: ChatGeneration[]): Promise<T>;
|
|
19
20
|
}
|
|
21
|
+
export declare function extractToolCalls(content: Record<string, any>[]): ToolCall[];
|
|
20
22
|
export {};
|
package/dist/output_parsers.js
CHANGED
|
@@ -53,24 +53,30 @@ export class AnthropicToolsOutputParser extends BaseLLMOutputParser {
|
|
|
53
53
|
throw new OutputParserException(`Failed to parse. Text: "${JSON.stringify(result, null, 2)}". Error: ${JSON.stringify(zodParsedResult.error.errors)}`, JSON.stringify(result, null, 2));
|
|
54
54
|
}
|
|
55
55
|
}
|
|
56
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
57
56
|
async parseResult(generations) {
|
|
58
57
|
const tools = generations.flatMap((generation) => {
|
|
59
58
|
const { message } = generation;
|
|
60
|
-
if (typeof message === "string") {
|
|
61
|
-
return [];
|
|
62
|
-
}
|
|
63
59
|
if (!Array.isArray(message.content)) {
|
|
64
60
|
return [];
|
|
65
61
|
}
|
|
66
|
-
const tool = message.content
|
|
62
|
+
const tool = extractToolCalls(message.content)[0];
|
|
67
63
|
return tool;
|
|
68
64
|
});
|
|
69
65
|
if (tools[0] === undefined) {
|
|
70
66
|
throw new Error("No parseable tool calls provided to AnthropicToolsOutputParser.");
|
|
71
67
|
}
|
|
72
68
|
const [tool] = tools;
|
|
73
|
-
const validatedResult = await this._validateResult(tool.
|
|
69
|
+
const validatedResult = await this._validateResult(tool.args);
|
|
74
70
|
return validatedResult;
|
|
75
71
|
}
|
|
76
72
|
}
|
|
73
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
74
|
+
export function extractToolCalls(content) {
|
|
75
|
+
const toolCalls = [];
|
|
76
|
+
for (const block of content) {
|
|
77
|
+
if (block.type === "tool_use") {
|
|
78
|
+
toolCalls.push({ name: block.name, args: block.input, id: block.id });
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return toolCalls;
|
|
82
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
// import { test, expect } from "@jest/globals";
|
|
2
|
+
// import { ChatPromptTemplate } from "@langchain/core/prompts";
|
|
3
|
+
// import { TavilySearchResults } from "@langchain/community/tools/tavily_search";
|
|
4
|
+
// import { AgentExecutor, createToolCallingAgent } from "langchain/agents";
|
|
5
|
+
// import { ChatAnthropic } from "../index.js";
|
|
6
|
+
// const tools = [new TavilySearchResults({ maxResults: 1 })];
|
|
7
|
+
// TODO: This test breaks CI build due to dependencies. Figure out a way around it.
|
|
8
|
+
test("createToolCallingAgent works", async () => {
|
|
9
|
+
// const prompt = ChatPromptTemplate.fromMessages([
|
|
10
|
+
// ["system", "You are a helpful assistant"],
|
|
11
|
+
// ["placeholder", "{chat_history}"],
|
|
12
|
+
// ["human", "{input}"],
|
|
13
|
+
// ["placeholder", "{agent_scratchpad}"],
|
|
14
|
+
// ]);
|
|
15
|
+
// const llm = new ChatAnthropic({
|
|
16
|
+
// modelName: "claude-3-sonnet-20240229",
|
|
17
|
+
// temperature: 0,
|
|
18
|
+
// });
|
|
19
|
+
// const agent = await createToolCallingAgent({
|
|
20
|
+
// llm,
|
|
21
|
+
// tools,
|
|
22
|
+
// prompt,
|
|
23
|
+
// });
|
|
24
|
+
// const agentExecutor = new AgentExecutor({
|
|
25
|
+
// agent,
|
|
26
|
+
// tools,
|
|
27
|
+
// });
|
|
28
|
+
// const input = "what is the current weather in SF?";
|
|
29
|
+
// const result = await agentExecutor.invoke({
|
|
30
|
+
// input,
|
|
31
|
+
// });
|
|
32
|
+
// console.log(result);
|
|
33
|
+
// expect(result.input).toBe(input);
|
|
34
|
+
// expect(typeof result.output).toBe("string");
|
|
35
|
+
// // Length greater than 10 because any less than that would warrant
|
|
36
|
+
// // an investigation into why such a short generation was returned.
|
|
37
|
+
// expect(result.output.length).toBeGreaterThan(10);
|
|
38
|
+
});
|
|
39
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|