@langchain/anthropic 0.1.9 → 0.1.11

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.
@@ -0,0 +1,80 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AnthropicToolsOutputParser = void 0;
4
+ const output_parsers_1 = require("@langchain/core/output_parsers");
5
+ class AnthropicToolsOutputParser extends output_parsers_1.BaseLLMOutputParser {
6
+ static lc_name() {
7
+ return "AnthropicToolsOutputParser";
8
+ }
9
+ constructor(params) {
10
+ super(params);
11
+ Object.defineProperty(this, "lc_namespace", {
12
+ enumerable: true,
13
+ configurable: true,
14
+ writable: true,
15
+ value: ["langchain", "anthropic", "output_parsers"]
16
+ });
17
+ Object.defineProperty(this, "returnId", {
18
+ enumerable: true,
19
+ configurable: true,
20
+ writable: true,
21
+ value: false
22
+ });
23
+ /** The type of tool calls to return. */
24
+ Object.defineProperty(this, "keyName", {
25
+ enumerable: true,
26
+ configurable: true,
27
+ writable: true,
28
+ value: void 0
29
+ });
30
+ /** Whether to return only the first tool call. */
31
+ Object.defineProperty(this, "returnSingle", {
32
+ enumerable: true,
33
+ configurable: true,
34
+ writable: true,
35
+ value: false
36
+ });
37
+ Object.defineProperty(this, "zodSchema", {
38
+ enumerable: true,
39
+ configurable: true,
40
+ writable: true,
41
+ value: void 0
42
+ });
43
+ this.keyName = params.keyName;
44
+ this.returnSingle = params.returnSingle ?? this.returnSingle;
45
+ this.zodSchema = params.zodSchema;
46
+ }
47
+ async _validateResult(result) {
48
+ if (this.zodSchema === undefined) {
49
+ return result;
50
+ }
51
+ const zodParsedResult = await this.zodSchema.safeParseAsync(result);
52
+ if (zodParsedResult.success) {
53
+ return zodParsedResult.data;
54
+ }
55
+ else {
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
+ }
58
+ }
59
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
60
+ async parseResult(generations) {
61
+ const tools = generations.flatMap((generation) => {
62
+ const { message } = generation;
63
+ if (typeof message === "string") {
64
+ return [];
65
+ }
66
+ if (!Array.isArray(message.content)) {
67
+ return [];
68
+ }
69
+ const tool = message.content.find((item) => item.type === "tool_use");
70
+ return tool;
71
+ });
72
+ if (tools[0] === undefined) {
73
+ throw new Error("No parseable tool calls provided to AnthropicToolsOutputParser.");
74
+ }
75
+ const [tool] = tools;
76
+ const validatedResult = await this._validateResult(tool.input);
77
+ return validatedResult;
78
+ }
79
+ }
80
+ exports.AnthropicToolsOutputParser = AnthropicToolsOutputParser;
@@ -0,0 +1,20 @@
1
+ import { z } from "zod";
2
+ import { BaseLLMOutputParser } from "@langchain/core/output_parsers";
3
+ import { JsonOutputKeyToolsParserParams } from "@langchain/core/output_parsers/openai_tools";
4
+ import { ChatGeneration } from "@langchain/core/outputs";
5
+ interface AnthropicToolsOutputParserParams<T extends Record<string, any>> extends JsonOutputKeyToolsParserParams<T> {
6
+ }
7
+ export declare class AnthropicToolsOutputParser<T extends Record<string, any> = Record<string, any>> extends BaseLLMOutputParser<T> {
8
+ static lc_name(): string;
9
+ lc_namespace: string[];
10
+ returnId: boolean;
11
+ /** The type of tool calls to return. */
12
+ keyName: string;
13
+ /** Whether to return only the first tool call. */
14
+ returnSingle: boolean;
15
+ zodSchema?: z.ZodType<T>;
16
+ constructor(params: AnthropicToolsOutputParserParams<T>);
17
+ protected _validateResult(result: unknown): Promise<T>;
18
+ parseResult(generations: ChatGeneration[]): Promise<T>;
19
+ }
20
+ export {};
@@ -0,0 +1,76 @@
1
+ import { BaseLLMOutputParser, OutputParserException, } from "@langchain/core/output_parsers";
2
+ export class AnthropicToolsOutputParser extends BaseLLMOutputParser {
3
+ static lc_name() {
4
+ return "AnthropicToolsOutputParser";
5
+ }
6
+ constructor(params) {
7
+ super(params);
8
+ Object.defineProperty(this, "lc_namespace", {
9
+ enumerable: true,
10
+ configurable: true,
11
+ writable: true,
12
+ value: ["langchain", "anthropic", "output_parsers"]
13
+ });
14
+ Object.defineProperty(this, "returnId", {
15
+ enumerable: true,
16
+ configurable: true,
17
+ writable: true,
18
+ value: false
19
+ });
20
+ /** The type of tool calls to return. */
21
+ Object.defineProperty(this, "keyName", {
22
+ enumerable: true,
23
+ configurable: true,
24
+ writable: true,
25
+ value: void 0
26
+ });
27
+ /** Whether to return only the first tool call. */
28
+ Object.defineProperty(this, "returnSingle", {
29
+ enumerable: true,
30
+ configurable: true,
31
+ writable: true,
32
+ value: false
33
+ });
34
+ Object.defineProperty(this, "zodSchema", {
35
+ enumerable: true,
36
+ configurable: true,
37
+ writable: true,
38
+ value: void 0
39
+ });
40
+ this.keyName = params.keyName;
41
+ this.returnSingle = params.returnSingle ?? this.returnSingle;
42
+ this.zodSchema = params.zodSchema;
43
+ }
44
+ async _validateResult(result) {
45
+ if (this.zodSchema === undefined) {
46
+ return result;
47
+ }
48
+ const zodParsedResult = await this.zodSchema.safeParseAsync(result);
49
+ if (zodParsedResult.success) {
50
+ return zodParsedResult.data;
51
+ }
52
+ else {
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
+ }
55
+ }
56
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
57
+ async parseResult(generations) {
58
+ const tools = generations.flatMap((generation) => {
59
+ const { message } = generation;
60
+ if (typeof message === "string") {
61
+ return [];
62
+ }
63
+ if (!Array.isArray(message.content)) {
64
+ return [];
65
+ }
66
+ const tool = message.content.find((item) => item.type === "tool_use");
67
+ return tool;
68
+ });
69
+ if (tools[0] === undefined) {
70
+ throw new Error("No parseable tool calls provided to AnthropicToolsOutputParser.");
71
+ }
72
+ const [tool] = tools;
73
+ const validatedResult = await this._validateResult(tool.input);
74
+ return validatedResult;
75
+ }
76
+ }
@@ -4,6 +4,9 @@ import { HumanMessage } from "@langchain/core/messages";
4
4
  import { ChatPromptValue } from "@langchain/core/prompt_values";
5
5
  import { PromptTemplate, ChatPromptTemplate, AIMessagePromptTemplate, HumanMessagePromptTemplate, SystemMessagePromptTemplate, } from "@langchain/core/prompts";
6
6
  import { CallbackManager } from "@langchain/core/callbacks/manager";
7
+ import { StructuredTool } from "@langchain/core/tools";
8
+ import { z } from "zod";
9
+ import { zodToJsonSchema } from "zod-to-json-schema";
7
10
  import { ChatAnthropic } from "../chat_models.js";
8
11
  test.skip("Test ChatAnthropic", async () => {
9
12
  const chat = new ChatAnthropic({
@@ -272,3 +275,178 @@ test("Test ChatAnthropic multimodal", async () => {
272
275
  ]);
273
276
  console.log(res);
274
277
  });
278
+ describe("Tool calling", () => {
279
+ const zodSchema = z
280
+ .object({
281
+ location: z.string().describe("The name of city to get the weather for."),
282
+ })
283
+ .describe("Get the weather of a specific location and return the temperature in Celsius.");
284
+ class WeatherTool extends StructuredTool {
285
+ constructor() {
286
+ super(...arguments);
287
+ Object.defineProperty(this, "schema", {
288
+ enumerable: true,
289
+ configurable: true,
290
+ writable: true,
291
+ value: z.object({
292
+ location: z.string().describe("The name of city to get the weather for."),
293
+ })
294
+ });
295
+ Object.defineProperty(this, "description", {
296
+ enumerable: true,
297
+ configurable: true,
298
+ writable: true,
299
+ value: "Get the weather of a specific location and return the temperature in Celsius."
300
+ });
301
+ Object.defineProperty(this, "name", {
302
+ enumerable: true,
303
+ configurable: true,
304
+ writable: true,
305
+ value: "get_weather"
306
+ });
307
+ }
308
+ async _call(input) {
309
+ console.log(`WeatherTool called with input: ${input}`);
310
+ return `The weather in ${input.location} is 25°C`;
311
+ }
312
+ }
313
+ const model = new ChatAnthropic({
314
+ modelName: "claude-3-sonnet-20240229",
315
+ temperature: 0,
316
+ });
317
+ const anthropicTool = {
318
+ name: "get_weather",
319
+ description: "Get the weather of a specific location and return the temperature in Celsius.",
320
+ input_schema: {
321
+ type: "object",
322
+ properties: {
323
+ location: {
324
+ type: "string",
325
+ description: "The name of city to get the weather for.",
326
+ },
327
+ },
328
+ required: ["location"],
329
+ },
330
+ };
331
+ test("Can bind & invoke StructuredTools", async () => {
332
+ const tools = [new WeatherTool()];
333
+ const modelWithTools = model.bind({
334
+ tools,
335
+ });
336
+ const result = await modelWithTools.invoke("What is the weather in London today?");
337
+ console.log({
338
+ tool_calls: JSON.stringify(result.content, null, 2),
339
+ }, "Can bind & invoke StructuredTools");
340
+ expect(Array.isArray(result.content)).toBeTruthy();
341
+ if (!Array.isArray(result.content)) {
342
+ throw new Error("Content is not an array");
343
+ }
344
+ let toolCall;
345
+ result.content.forEach((item) => {
346
+ if (item.type === "tool_use") {
347
+ toolCall = item;
348
+ }
349
+ });
350
+ if (!toolCall) {
351
+ throw new Error("No tool call found");
352
+ }
353
+ expect(toolCall).toBeTruthy();
354
+ const { name, input } = toolCall;
355
+ expect(name).toBe("get_weather");
356
+ expect(input).toBeTruthy();
357
+ expect(input.location).toBeTruthy();
358
+ });
359
+ test("Can bind & invoke AnthropicTools", async () => {
360
+ const modelWithTools = model.bind({
361
+ tools: [anthropicTool],
362
+ });
363
+ const result = await modelWithTools.invoke("What is the weather in London today?");
364
+ console.log({
365
+ tool_calls: JSON.stringify(result.content, null, 2),
366
+ }, "Can bind & invoke StructuredTools");
367
+ expect(Array.isArray(result.content)).toBeTruthy();
368
+ if (!Array.isArray(result.content)) {
369
+ throw new Error("Content is not an array");
370
+ }
371
+ let toolCall;
372
+ result.content.forEach((item) => {
373
+ if (item.type === "tool_use") {
374
+ toolCall = item;
375
+ }
376
+ });
377
+ if (!toolCall) {
378
+ throw new Error("No tool call found");
379
+ }
380
+ expect(toolCall).toBeTruthy();
381
+ const { name, input } = toolCall;
382
+ expect(name).toBe("get_weather");
383
+ expect(input).toBeTruthy();
384
+ expect(input.location).toBeTruthy();
385
+ });
386
+ test("Can bind & stream AnthropicTools", async () => {
387
+ const modelWithTools = model.bind({
388
+ tools: [anthropicTool],
389
+ });
390
+ const result = await modelWithTools.stream("What is the weather in London today?");
391
+ let finalMessage;
392
+ for await (const item of result) {
393
+ console.log("item", JSON.stringify(item, null, 2));
394
+ finalMessage = item;
395
+ }
396
+ if (!finalMessage) {
397
+ throw new Error("No final message returned");
398
+ }
399
+ console.log({
400
+ tool_calls: JSON.stringify(finalMessage.content, null, 2),
401
+ }, "Can bind & invoke StructuredTools");
402
+ expect(Array.isArray(finalMessage.content)).toBeTruthy();
403
+ if (!Array.isArray(finalMessage.content)) {
404
+ throw new Error("Content is not an array");
405
+ }
406
+ let toolCall;
407
+ finalMessage.content.forEach((item) => {
408
+ if (item.type === "tool_use") {
409
+ toolCall = item;
410
+ }
411
+ });
412
+ if (!toolCall) {
413
+ throw new Error("No tool call found");
414
+ }
415
+ expect(toolCall).toBeTruthy();
416
+ const { name, input } = toolCall;
417
+ expect(name).toBe("get_weather");
418
+ expect(input).toBeTruthy();
419
+ expect(input.location).toBeTruthy();
420
+ });
421
+ test("withStructuredOutput with zod schema", async () => {
422
+ const modelWithTools = model.withStructuredOutput(zodSchema, {
423
+ name: "get_weather",
424
+ });
425
+ const result = await modelWithTools.invoke("What is the weather in London today?");
426
+ console.log({
427
+ result,
428
+ }, "withStructuredOutput with zod schema");
429
+ expect(typeof result.location).toBe("string");
430
+ });
431
+ test("withStructuredOutput with AnthropicTool", async () => {
432
+ const modelWithTools = model.withStructuredOutput(anthropicTool, {
433
+ name: anthropicTool.name,
434
+ });
435
+ const result = await modelWithTools.invoke("What is the weather in London today?");
436
+ console.log({
437
+ result,
438
+ }, "withStructuredOutput with AnthropicTool");
439
+ expect(typeof result.location).toBe("string");
440
+ });
441
+ test("withStructuredOutput JSON Schema only", async () => {
442
+ const jsonSchema = zodToJsonSchema(zodSchema);
443
+ const modelWithTools = model.withStructuredOutput(jsonSchema, {
444
+ name: "get_weather",
445
+ });
446
+ const result = await modelWithTools.invoke("What is the weather in London today?");
447
+ console.log({
448
+ result,
449
+ }, "withStructuredOutput JSON Schema only");
450
+ expect(typeof result.location).toBe("string");
451
+ });
452
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,88 @@
1
+ import { jest, test } from "@jest/globals";
2
+ import { AIMessage } from "@langchain/core/messages";
3
+ import { z } from "zod";
4
+ import { OutputParserException } from "@langchain/core/output_parsers";
5
+ import { ChatAnthropic } from "../chat_models.js";
6
+ test("withStructuredOutput with output validation", async () => {
7
+ const model = new ChatAnthropic({
8
+ modelName: "claude-3-haiku-20240307",
9
+ temperature: 0,
10
+ anthropicApiKey: "testing",
11
+ });
12
+ jest
13
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
14
+ .spyOn(model, "invoke")
15
+ .mockResolvedValue(new AIMessage({
16
+ content: [
17
+ {
18
+ type: "tool_use",
19
+ id: "notreal",
20
+ name: "Extractor",
21
+ input: "Incorrect string tool call input",
22
+ },
23
+ ],
24
+ }));
25
+ const schema = z.object({
26
+ alerts: z
27
+ .array(z.object({
28
+ description: z.string().describe("A description of the alert."),
29
+ severity: z
30
+ .enum(["HIGH", "MEDIUM", "LOW"])
31
+ .describe("How severe the alert is."),
32
+ }))
33
+ .describe("Important security or infrastructure alerts present in the given text."),
34
+ });
35
+ const modelWithStructuredOutput = model.withStructuredOutput(schema, {
36
+ name: "Extractor",
37
+ });
38
+ await expect(async () => {
39
+ await modelWithStructuredOutput.invoke(`
40
+ Enumeration of Kernel Modules via Proc
41
+ Prompt for Credentials with OSASCRIPT
42
+ User Login
43
+ Modification of Standard Authentication Module
44
+ Suspicious Automator Workflows Execution
45
+ `);
46
+ }).rejects.toThrowError(OutputParserException);
47
+ });
48
+ test("withStructuredOutput with proper output", async () => {
49
+ const model = new ChatAnthropic({
50
+ modelName: "claude-3-haiku-20240307",
51
+ temperature: 0,
52
+ anthropicApiKey: "testing",
53
+ });
54
+ jest
55
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
56
+ .spyOn(model, "invoke")
57
+ .mockResolvedValue(new AIMessage({
58
+ content: [
59
+ {
60
+ type: "tool_use",
61
+ id: "notreal",
62
+ name: "Extractor",
63
+ input: { alerts: [{ description: "test", severity: "LOW" }] },
64
+ },
65
+ ],
66
+ }));
67
+ const schema = z.object({
68
+ alerts: z
69
+ .array(z.object({
70
+ description: z.string().describe("A description of the alert."),
71
+ severity: z
72
+ .enum(["HIGH", "MEDIUM", "LOW"])
73
+ .describe("How severe the alert is."),
74
+ }))
75
+ .describe("Important security or infrastructure alerts present in the given text."),
76
+ });
77
+ const modelWithStructuredOutput = model.withStructuredOutput(schema, {
78
+ name: "Extractor",
79
+ });
80
+ const result = await modelWithStructuredOutput.invoke(`
81
+ Enumeration of Kernel Modules via Proc
82
+ Prompt for Credentials with OSASCRIPT
83
+ User Login
84
+ Modification of Standard Authentication Module
85
+ Suspicious Automator Workflows Execution
86
+ `);
87
+ console.log(result);
88
+ });
package/dist/types.cjs ADDED
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,6 @@
1
+ export type AnthropicToolResponse = {
2
+ type: "tool_use";
3
+ id: string;
4
+ name: string;
5
+ input: Record<string, any>;
6
+ };
package/dist/types.js ADDED
@@ -0,0 +1 @@
1
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@langchain/anthropic",
3
- "version": "0.1.9",
3
+ "version": "0.1.11",
4
4
  "description": "Anthropic integrations for LangChain.js",
5
5
  "type": "module",
6
6
  "engines": {
@@ -39,8 +39,8 @@
39
39
  "author": "LangChain",
40
40
  "license": "MIT",
41
41
  "dependencies": {
42
- "@anthropic-ai/sdk": "^0.17.2",
43
- "@langchain/core": "~0.1.44",
42
+ "@anthropic-ai/sdk": "^0.20.1",
43
+ "@langchain/core": "~0.1.54",
44
44
  "fast-xml-parser": "^4.3.5",
45
45
  "zod": "^3.22.4",
46
46
  "zod-to-json-schema": "^3.22.4"