@langchain/anthropic 0.1.10 → 0.1.12-rc.0

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.
@@ -400,7 +400,7 @@ class ChatAnthropicMessages extends chat_models_1.BaseChatModel {
400
400
  content: message.content,
401
401
  };
402
402
  }
403
- else if ("type" in message.content) {
403
+ else {
404
404
  const contentBlocks = message.content.map((contentPart) => {
405
405
  if (contentPart.type === "image_url") {
406
406
  let source;
@@ -422,6 +422,14 @@ class ChatAnthropicMessages extends chat_models_1.BaseChatModel {
422
422
  text: contentPart.text,
423
423
  };
424
424
  }
425
+ else if (contentPart.type === "tool_use" ||
426
+ contentPart.type === "tool_result") {
427
+ // TODO: Fix when SDK types are fixed
428
+ return {
429
+ ...contentPart,
430
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
431
+ };
432
+ }
425
433
  else {
426
434
  throw new Error("Unsupported message content format");
427
435
  }
@@ -431,9 +439,6 @@ class ChatAnthropicMessages extends chat_models_1.BaseChatModel {
431
439
  content: contentBlocks,
432
440
  };
433
441
  }
434
- else {
435
- throw new Error("Unsupported message content format");
436
- }
437
442
  });
438
443
  return {
439
444
  messages: formattedMessages,
@@ -144,7 +144,7 @@ export declare class ChatAnthropicMessages<CallOptions extends ChatAnthropicCall
144
144
  metadata?: Anthropic.Messages.MessageCreateParams.Metadata | undefined;
145
145
  stream?: boolean | undefined;
146
146
  max_tokens: number;
147
- model: (string & {}) | "claude-3-opus-20240229" | "claude-3-sonnet-20240229" | "claude-2.1'" | "claude-2.0" | "claude-instant-1.2";
147
+ model: "claude-2.1" | (string & {}) | "claude-3-opus-20240229" | "claude-3-sonnet-20240229" | "claude-3-haiku-20240307" | "claude-2.0" | "claude-instant-1.2";
148
148
  stop_sequences?: string[] | undefined;
149
149
  temperature?: number | undefined;
150
150
  top_k?: number | undefined;
@@ -159,7 +159,7 @@ export declare class ChatAnthropicMessages<CallOptions extends ChatAnthropicCall
159
159
  metadata?: Anthropic.Messages.MessageCreateParams.Metadata | undefined;
160
160
  stream?: boolean | undefined;
161
161
  max_tokens: number;
162
- model: (string & {}) | "claude-3-opus-20240229" | "claude-3-sonnet-20240229" | "claude-2.1'" | "claude-2.0" | "claude-instant-1.2";
162
+ model: "claude-2.1" | (string & {}) | "claude-3-opus-20240229" | "claude-3-sonnet-20240229" | "claude-3-haiku-20240307" | "claude-2.0" | "claude-instant-1.2";
163
163
  stop_sequences?: string[] | undefined;
164
164
  temperature?: number | undefined;
165
165
  top_k?: number | undefined;
@@ -397,7 +397,7 @@ export class ChatAnthropicMessages extends BaseChatModel {
397
397
  content: message.content,
398
398
  };
399
399
  }
400
- else if ("type" in message.content) {
400
+ else {
401
401
  const contentBlocks = message.content.map((contentPart) => {
402
402
  if (contentPart.type === "image_url") {
403
403
  let source;
@@ -419,6 +419,14 @@ export class ChatAnthropicMessages extends BaseChatModel {
419
419
  text: contentPart.text,
420
420
  };
421
421
  }
422
+ else if (contentPart.type === "tool_use" ||
423
+ contentPart.type === "tool_result") {
424
+ // TODO: Fix when SDK types are fixed
425
+ return {
426
+ ...contentPart,
427
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
428
+ };
429
+ }
422
430
  else {
423
431
  throw new Error("Unsupported message content format");
424
432
  }
@@ -428,9 +436,6 @@ export class ChatAnthropicMessages extends BaseChatModel {
428
436
  content: contentBlocks,
429
437
  };
430
438
  }
431
- else {
432
- throw new Error("Unsupported message content format");
433
- }
434
439
  });
435
440
  return {
436
441
  messages: formattedMessages,
@@ -6,7 +6,7 @@ import { zodToJsonSchema } from "zod-to-json-schema";
6
6
  import { HumanMessage } from "@langchain/core/messages";
7
7
  import { ChatPromptTemplate } from "@langchain/core/prompts";
8
8
  import { ChatAnthropicTools } from "../tool_calling.js";
9
- test("Test ChatAnthropicTools", async () => {
9
+ test.skip("Test ChatAnthropicTools", async () => {
10
10
  const chat = new ChatAnthropicTools({
11
11
  modelName: "claude-3-sonnet-20240229",
12
12
  maxRetries: 0,
@@ -15,7 +15,7 @@ test("Test ChatAnthropicTools", async () => {
15
15
  const res = await chat.invoke([message]);
16
16
  console.log(JSON.stringify(res));
17
17
  });
18
- test("Test ChatAnthropicTools streaming", async () => {
18
+ test.skip("Test ChatAnthropicTools streaming", async () => {
19
19
  const chat = new ChatAnthropicTools({
20
20
  modelName: "claude-3-sonnet-20240229",
21
21
  maxRetries: 0,
@@ -29,7 +29,7 @@ test("Test ChatAnthropicTools streaming", async () => {
29
29
  }
30
30
  expect(chunks.length).toBeGreaterThan(1);
31
31
  });
32
- test("Test ChatAnthropicTools with tools", async () => {
32
+ test.skip("Test ChatAnthropicTools with tools", async () => {
33
33
  const chat = new ChatAnthropicTools({
34
34
  modelName: "claude-3-sonnet-20240229",
35
35
  temperature: 0.1,
@@ -65,7 +65,7 @@ test("Test ChatAnthropicTools with tools", async () => {
65
65
  expect(res.additional_kwargs.tool_calls).toBeDefined();
66
66
  expect(res.additional_kwargs.tool_calls?.[0].function.name).toEqual("get_current_weather");
67
67
  });
68
- test("Test ChatAnthropicTools with a forced function call", async () => {
68
+ test.skip("Test ChatAnthropicTools with a forced function call", async () => {
69
69
  const chat = new ChatAnthropicTools({
70
70
  modelName: "claude-3-sonnet-20240229",
71
71
  temperature: 0.1,
@@ -106,7 +106,7 @@ test("Test ChatAnthropicTools with a forced function call", async () => {
106
106
  expect(res.additional_kwargs.tool_calls).toBeDefined();
107
107
  expect(res.additional_kwargs.tool_calls?.[0]?.function.name).toEqual("extract_data");
108
108
  });
109
- test("ChatAnthropicTools with Zod schema", async () => {
109
+ test.skip("ChatAnthropicTools with Zod schema", async () => {
110
110
  const schema = z.object({
111
111
  people: z.array(z.object({
112
112
  name: z.string().describe("The name of a person"),
@@ -148,7 +148,7 @@ test("ChatAnthropicTools with Zod schema", async () => {
148
148
  ]),
149
149
  });
150
150
  });
151
- test("ChatAnthropicTools with parallel tool calling", async () => {
151
+ test.skip("ChatAnthropicTools with parallel tool calling", async () => {
152
152
  const schema = z.object({
153
153
  name: z.string().describe("The name of a person"),
154
154
  height: z.number().describe("The person's height"),
@@ -186,7 +186,7 @@ test("ChatAnthropicTools with parallel tool calling", async () => {
186
186
  { name: "Claudia", height: 6, hairColor: "brunette" },
187
187
  ]));
188
188
  });
189
- test("Test ChatAnthropic withStructuredOutput", async () => {
189
+ test.skip("Test ChatAnthropic withStructuredOutput", async () => {
190
190
  const runnable = new ChatAnthropicTools({
191
191
  modelName: "claude-3-sonnet-20240229",
192
192
  maxRetries: 0,
@@ -202,7 +202,7 @@ test("Test ChatAnthropic withStructuredOutput", async () => {
202
202
  console.log(JSON.stringify(res, null, 2));
203
203
  expect(res).toEqual({ name: "Alex", height: 5, hairColor: "blonde" });
204
204
  });
205
- test("Test ChatAnthropic withStructuredOutput on a single array item", async () => {
205
+ test.skip("Test ChatAnthropic withStructuredOutput on a single array item", async () => {
206
206
  const runnable = new ChatAnthropicTools({
207
207
  modelName: "claude-3-sonnet-20240229",
208
208
  maxRetries: 0,
@@ -220,7 +220,7 @@ test("Test ChatAnthropic withStructuredOutput on a single array item", async ()
220
220
  people: [{ hairColor: "blonde", height: 5, name: "Alex" }],
221
221
  });
222
222
  });
223
- test("Test ChatAnthropic withStructuredOutput on a single array item", async () => {
223
+ test.skip("Test ChatAnthropic withStructuredOutput on a single array item", async () => {
224
224
  const runnable = new ChatAnthropicTools({
225
225
  modelName: "claude-3-sonnet-20240229",
226
226
  maxRetries: 0,
@@ -262,7 +262,7 @@ test("Test ChatAnthropic withStructuredOutput on a single array item", async ()
262
262
  tone: "positive",
263
263
  });
264
264
  });
265
- test("Test ChatAnthropicTools", async () => {
265
+ test.skip("Test ChatAnthropicTools", async () => {
266
266
  const chat = new ChatAnthropicTools({
267
267
  modelName: "claude-3-sonnet-20240229",
268
268
  maxRetries: 0,
@@ -34,8 +34,27 @@ class AnthropicToolsOutputParser extends output_parsers_1.BaseLLMOutputParser {
34
34
  writable: true,
35
35
  value: false
36
36
  });
37
+ Object.defineProperty(this, "zodSchema", {
38
+ enumerable: true,
39
+ configurable: true,
40
+ writable: true,
41
+ value: void 0
42
+ });
37
43
  this.keyName = params.keyName;
38
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
+ }
39
58
  }
40
59
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
41
60
  async parseResult(generations) {
@@ -50,11 +69,12 @@ class AnthropicToolsOutputParser extends output_parsers_1.BaseLLMOutputParser {
50
69
  const tool = message.content.find((item) => item.type === "tool_use");
51
70
  return tool;
52
71
  });
53
- if (tools.length === 0 || !tools[0]) {
54
- throw new Error("No tools provided to AnthropicToolsOutputParser.");
72
+ if (tools[0] === undefined) {
73
+ throw new Error("No parseable tool calls provided to AnthropicToolsOutputParser.");
55
74
  }
56
75
  const [tool] = tools;
57
- return tool.input;
76
+ const validatedResult = await this._validateResult(tool.input);
77
+ return validatedResult;
58
78
  }
59
79
  }
60
80
  exports.AnthropicToolsOutputParser = AnthropicToolsOutputParser;
@@ -1,7 +1,8 @@
1
+ import { z } from "zod";
1
2
  import { BaseLLMOutputParser } from "@langchain/core/output_parsers";
2
3
  import { JsonOutputKeyToolsParserParams } from "@langchain/core/output_parsers/openai_tools";
3
4
  import { ChatGeneration } from "@langchain/core/outputs";
4
- interface AnthropicToolsOutputParserParams extends JsonOutputKeyToolsParserParams {
5
+ interface AnthropicToolsOutputParserParams<T extends Record<string, any>> extends JsonOutputKeyToolsParserParams<T> {
5
6
  }
6
7
  export declare class AnthropicToolsOutputParser<T extends Record<string, any> = Record<string, any>> extends BaseLLMOutputParser<T> {
7
8
  static lc_name(): string;
@@ -11,7 +12,9 @@ export declare class AnthropicToolsOutputParser<T extends Record<string, any> =
11
12
  keyName: string;
12
13
  /** Whether to return only the first tool call. */
13
14
  returnSingle: boolean;
14
- constructor(params: AnthropicToolsOutputParserParams);
15
+ zodSchema?: z.ZodType<T>;
16
+ constructor(params: AnthropicToolsOutputParserParams<T>);
17
+ protected _validateResult(result: unknown): Promise<T>;
15
18
  parseResult(generations: ChatGeneration[]): Promise<T>;
16
19
  }
17
20
  export {};
@@ -1,4 +1,4 @@
1
- import { BaseLLMOutputParser } from "@langchain/core/output_parsers";
1
+ import { BaseLLMOutputParser, OutputParserException, } from "@langchain/core/output_parsers";
2
2
  export class AnthropicToolsOutputParser extends BaseLLMOutputParser {
3
3
  static lc_name() {
4
4
  return "AnthropicToolsOutputParser";
@@ -31,8 +31,27 @@ export class AnthropicToolsOutputParser extends BaseLLMOutputParser {
31
31
  writable: true,
32
32
  value: false
33
33
  });
34
+ Object.defineProperty(this, "zodSchema", {
35
+ enumerable: true,
36
+ configurable: true,
37
+ writable: true,
38
+ value: void 0
39
+ });
34
40
  this.keyName = params.keyName;
35
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
+ }
36
55
  }
37
56
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
38
57
  async parseResult(generations) {
@@ -47,10 +66,11 @@ export class AnthropicToolsOutputParser extends BaseLLMOutputParser {
47
66
  const tool = message.content.find((item) => item.type === "tool_use");
48
67
  return tool;
49
68
  });
50
- if (tools.length === 0 || !tools[0]) {
51
- throw new Error("No tools provided to AnthropicToolsOutputParser.");
69
+ if (tools[0] === undefined) {
70
+ throw new Error("No parseable tool calls provided to AnthropicToolsOutputParser.");
52
71
  }
53
72
  const [tool] = tools;
54
- return tool.input;
73
+ const validatedResult = await this._validateResult(tool.input);
74
+ return validatedResult;
55
75
  }
56
76
  }
@@ -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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@langchain/anthropic",
3
- "version": "0.1.10",
3
+ "version": "0.1.12-rc.0",
4
4
  "description": "Anthropic integrations for LangChain.js",
5
5
  "type": "module",
6
6
  "engines": {
@@ -39,7 +39,7 @@
39
39
  "author": "LangChain",
40
40
  "license": "MIT",
41
41
  "dependencies": {
42
- "@anthropic-ai/sdk": "^0.17.2",
42
+ "@anthropic-ai/sdk": "^0.20.1",
43
43
  "@langchain/core": "~0.1.54",
44
44
  "fast-xml-parser": "^4.3.5",
45
45
  "zod": "^3.22.4",