@langchain/anthropic 0.1.6 → 0.1.7
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/experimental/tests/tool_calling.int.test.js +14 -0
- package/dist/experimental/tool_calling.cjs +34 -15
- package/dist/experimental/tool_calling.d.ts +12 -8
- package/dist/experimental/tool_calling.js +35 -16
- package/dist/experimental/utils/tool_calling.cjs +15 -3
- package/dist/experimental/utils/tool_calling.js +15 -3
- package/dist/tests/chat_models.int.test.js +2 -2
- package/package.json +1 -1
|
@@ -262,3 +262,17 @@ test("Test ChatAnthropic withStructuredOutput on a single array item", async ()
|
|
|
262
262
|
tone: "positive",
|
|
263
263
|
});
|
|
264
264
|
});
|
|
265
|
+
test("Test ChatAnthropicTools", async () => {
|
|
266
|
+
const chat = new ChatAnthropicTools({
|
|
267
|
+
modelName: "claude-3-sonnet-20240229",
|
|
268
|
+
maxRetries: 0,
|
|
269
|
+
});
|
|
270
|
+
const structured = chat.withStructuredOutput(z.object({
|
|
271
|
+
nested: z.array(z.number()),
|
|
272
|
+
}), { force: false });
|
|
273
|
+
const res = await structured.invoke("What are the first five natural numbers?");
|
|
274
|
+
console.log(res);
|
|
275
|
+
expect(res).toEqual({
|
|
276
|
+
nested: [1, 2, 3, 4, 5],
|
|
277
|
+
});
|
|
278
|
+
});
|
|
@@ -18,6 +18,9 @@ class ChatAnthropicTools extends chat_models_1.BaseChatModel {
|
|
|
18
18
|
return "ChatAnthropicTools";
|
|
19
19
|
}
|
|
20
20
|
constructor(fields) {
|
|
21
|
+
if (fields?.cache !== undefined) {
|
|
22
|
+
throw new Error("Caching is not supported for this model.");
|
|
23
|
+
}
|
|
21
24
|
super(fields ?? {});
|
|
22
25
|
Object.defineProperty(this, "llm", {
|
|
23
26
|
enumerable: true,
|
|
@@ -59,7 +62,7 @@ class ChatAnthropicTools extends chat_models_1.BaseChatModel {
|
|
|
59
62
|
async *_streamResponseChunks(messages, options, runManager) {
|
|
60
63
|
yield* this.llm._streamResponseChunks(messages, options, runManager);
|
|
61
64
|
}
|
|
62
|
-
async _prepareAndParseToolCall({ messages, options,
|
|
65
|
+
async _prepareAndParseToolCall({ messages, options, systemPromptTemplate = tool_calling_js_1.DEFAULT_TOOL_SYSTEM_PROMPT, stopSequences, }) {
|
|
63
66
|
let promptMessages = messages;
|
|
64
67
|
let forced = false;
|
|
65
68
|
let toolCall;
|
|
@@ -105,8 +108,10 @@ class ChatAnthropicTools extends chat_models_1.BaseChatModel {
|
|
|
105
108
|
else if (options.tool_choice !== undefined) {
|
|
106
109
|
throw new Error(`If "tool_choice" is provided, "tools" must also be.`);
|
|
107
110
|
}
|
|
108
|
-
const chatResult = await this.llm
|
|
109
|
-
|
|
111
|
+
const chatResult = await this.llm
|
|
112
|
+
.withConfig({ runName: "ChatAnthropicTools" })
|
|
113
|
+
.invoke(promptMessages, options);
|
|
114
|
+
const chatGenerationContent = chatResult.content;
|
|
110
115
|
if (typeof chatGenerationContent !== "string") {
|
|
111
116
|
throw new Error("AnthropicFunctions does not support non-string output.");
|
|
112
117
|
}
|
|
@@ -171,15 +176,25 @@ class ChatAnthropicTools extends chat_models_1.BaseChatModel {
|
|
|
171
176
|
generations: [{ message: responseMessageWithFunctions, text: "" }],
|
|
172
177
|
};
|
|
173
178
|
}
|
|
174
|
-
return chatResult;
|
|
179
|
+
return { generations: [{ message: chatResult, text: "" }] };
|
|
175
180
|
}
|
|
176
|
-
async
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
181
|
+
async generate(messages, parsedOptions, callbacks) {
|
|
182
|
+
const baseMessages = messages.map((messageList) => messageList.map(messages_1.coerceMessageLikeToMessage));
|
|
183
|
+
// generate results
|
|
184
|
+
const chatResults = await Promise.all(baseMessages.map((messageList) => this._prepareAndParseToolCall({
|
|
185
|
+
messages: messageList,
|
|
186
|
+
options: { callbacks, ...parsedOptions },
|
|
180
187
|
systemPromptTemplate: this.systemPromptTemplate,
|
|
181
188
|
stopSequences: this.stopSequences ?? [],
|
|
182
|
-
});
|
|
189
|
+
})));
|
|
190
|
+
// create combined output
|
|
191
|
+
const output = {
|
|
192
|
+
generations: chatResults.map((chatResult) => chatResult.generations),
|
|
193
|
+
};
|
|
194
|
+
return output;
|
|
195
|
+
}
|
|
196
|
+
async _generate(_messages, _options, _runManager) {
|
|
197
|
+
throw new Error("Unused.");
|
|
183
198
|
}
|
|
184
199
|
_llmType() {
|
|
185
200
|
return "anthropic_tool_calling";
|
|
@@ -190,6 +205,7 @@ class ChatAnthropicTools extends chat_models_1.BaseChatModel {
|
|
|
190
205
|
let name;
|
|
191
206
|
let method;
|
|
192
207
|
let includeRaw;
|
|
208
|
+
let force;
|
|
193
209
|
if (isStructuredOutputMethodParams(outputSchema)) {
|
|
194
210
|
schema = outputSchema.schema;
|
|
195
211
|
name = outputSchema.name;
|
|
@@ -201,6 +217,7 @@ class ChatAnthropicTools extends chat_models_1.BaseChatModel {
|
|
|
201
217
|
name = config?.name;
|
|
202
218
|
method = config?.method;
|
|
203
219
|
includeRaw = config?.includeRaw;
|
|
220
|
+
force = config?.force ?? true;
|
|
204
221
|
}
|
|
205
222
|
if (method === "jsonMode") {
|
|
206
223
|
throw new Error(`Anthropic only supports "functionCalling" as a method.`);
|
|
@@ -244,12 +261,14 @@ class ChatAnthropicTools extends chat_models_1.BaseChatModel {
|
|
|
244
261
|
}
|
|
245
262
|
const llm = this.bind({
|
|
246
263
|
tools,
|
|
247
|
-
tool_choice:
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
264
|
+
tool_choice: force
|
|
265
|
+
? {
|
|
266
|
+
type: "function",
|
|
267
|
+
function: {
|
|
268
|
+
name: functionName,
|
|
269
|
+
},
|
|
270
|
+
}
|
|
271
|
+
: "auto",
|
|
253
272
|
});
|
|
254
273
|
if (!includeRaw) {
|
|
255
274
|
return llm.pipe(outputParser).withConfig({
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { BaseMessage } from "@langchain/core/messages";
|
|
2
|
-
import { ChatGenerationChunk, ChatResult } from "@langchain/core/outputs";
|
|
1
|
+
import { BaseMessage, BaseMessageLike } from "@langchain/core/messages";
|
|
2
|
+
import type { ChatGenerationChunk, ChatResult, LLMResult } from "@langchain/core/outputs";
|
|
3
3
|
import { BaseChatModel, BaseChatModelParams } from "@langchain/core/language_models/chat_models";
|
|
4
|
-
import { CallbackManagerForLLMRun } from "@langchain/core/callbacks/manager";
|
|
4
|
+
import { CallbackManagerForLLMRun, Callbacks } from "@langchain/core/callbacks/manager";
|
|
5
5
|
import { BasePromptTemplate } from "@langchain/core/prompts";
|
|
6
6
|
import { BaseLanguageModelCallOptions, BaseLanguageModelInput, StructuredOutputMethodParams, StructuredOutputMethodOptions, ToolDefinition } from "@langchain/core/language_models/base";
|
|
7
7
|
import { Runnable } from "@langchain/core/runnables";
|
|
@@ -35,17 +35,21 @@ export declare class ChatAnthropicTools extends BaseChatModel<ChatAnthropicTools
|
|
|
35
35
|
/** @ignore */
|
|
36
36
|
_identifyingParams(): Record<string, any>;
|
|
37
37
|
_streamResponseChunks(messages: BaseMessage[], options: this["ParsedCallOptions"], runManager?: CallbackManagerForLLMRun): AsyncGenerator<ChatGenerationChunk>;
|
|
38
|
-
_prepareAndParseToolCall({ messages, options,
|
|
38
|
+
_prepareAndParseToolCall({ messages, options, systemPromptTemplate, stopSequences, }: {
|
|
39
39
|
messages: BaseMessage[];
|
|
40
40
|
options: ChatAnthropicToolsCallOptions;
|
|
41
|
-
runManager?: CallbackManagerForLLMRun;
|
|
42
41
|
systemPromptTemplate?: BasePromptTemplate;
|
|
43
42
|
stopSequences: string[];
|
|
44
43
|
}): Promise<ChatResult>;
|
|
45
|
-
|
|
44
|
+
generate(messages: BaseMessageLike[][], parsedOptions?: ChatAnthropicToolsCallOptions, callbacks?: Callbacks): Promise<LLMResult>;
|
|
45
|
+
_generate(_messages: BaseMessage[], _options: this["ParsedCallOptions"], _runManager?: CallbackManagerForLLMRun | undefined): Promise<ChatResult>;
|
|
46
46
|
_llmType(): string;
|
|
47
|
-
withStructuredOutput<RunOutput extends Record<string, any> = Record<string, any>>(outputSchema: StructuredOutputMethodParams<RunOutput, false> | z.ZodType<RunOutput> | Record<string, any>, config?: StructuredOutputMethodOptions<false>
|
|
48
|
-
|
|
47
|
+
withStructuredOutput<RunOutput extends Record<string, any> = Record<string, any>>(outputSchema: StructuredOutputMethodParams<RunOutput, false> | z.ZodType<RunOutput> | Record<string, any>, config?: StructuredOutputMethodOptions<false> & {
|
|
48
|
+
force?: boolean;
|
|
49
|
+
}): Runnable<BaseLanguageModelInput, RunOutput>;
|
|
50
|
+
withStructuredOutput<RunOutput extends Record<string, any> = Record<string, any>>(outputSchema: StructuredOutputMethodParams<RunOutput, true> | z.ZodType<RunOutput> | Record<string, any>, config?: StructuredOutputMethodOptions<true> & {
|
|
51
|
+
force?: boolean;
|
|
52
|
+
}): Runnable<BaseLanguageModelInput, {
|
|
49
53
|
raw: BaseMessage;
|
|
50
54
|
parsed: RunOutput;
|
|
51
55
|
}>;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { XMLParser } from "fast-xml-parser";
|
|
2
|
-
import { AIMessage, SystemMessage, } from "@langchain/core/messages";
|
|
2
|
+
import { AIMessage, SystemMessage, coerceMessageLikeToMessage, } from "@langchain/core/messages";
|
|
3
3
|
import { BaseChatModel, } from "@langchain/core/language_models/chat_models";
|
|
4
4
|
import { RunnablePassthrough, RunnableSequence, } from "@langchain/core/runnables";
|
|
5
5
|
import { JsonOutputKeyToolsParser } from "@langchain/core/output_parsers/openai_tools";
|
|
@@ -15,6 +15,9 @@ export class ChatAnthropicTools extends BaseChatModel {
|
|
|
15
15
|
return "ChatAnthropicTools";
|
|
16
16
|
}
|
|
17
17
|
constructor(fields) {
|
|
18
|
+
if (fields?.cache !== undefined) {
|
|
19
|
+
throw new Error("Caching is not supported for this model.");
|
|
20
|
+
}
|
|
18
21
|
super(fields ?? {});
|
|
19
22
|
Object.defineProperty(this, "llm", {
|
|
20
23
|
enumerable: true,
|
|
@@ -56,7 +59,7 @@ export class ChatAnthropicTools extends BaseChatModel {
|
|
|
56
59
|
async *_streamResponseChunks(messages, options, runManager) {
|
|
57
60
|
yield* this.llm._streamResponseChunks(messages, options, runManager);
|
|
58
61
|
}
|
|
59
|
-
async _prepareAndParseToolCall({ messages, options,
|
|
62
|
+
async _prepareAndParseToolCall({ messages, options, systemPromptTemplate = DEFAULT_TOOL_SYSTEM_PROMPT, stopSequences, }) {
|
|
60
63
|
let promptMessages = messages;
|
|
61
64
|
let forced = false;
|
|
62
65
|
let toolCall;
|
|
@@ -102,8 +105,10 @@ export class ChatAnthropicTools extends BaseChatModel {
|
|
|
102
105
|
else if (options.tool_choice !== undefined) {
|
|
103
106
|
throw new Error(`If "tool_choice" is provided, "tools" must also be.`);
|
|
104
107
|
}
|
|
105
|
-
const chatResult = await this.llm
|
|
106
|
-
|
|
108
|
+
const chatResult = await this.llm
|
|
109
|
+
.withConfig({ runName: "ChatAnthropicTools" })
|
|
110
|
+
.invoke(promptMessages, options);
|
|
111
|
+
const chatGenerationContent = chatResult.content;
|
|
107
112
|
if (typeof chatGenerationContent !== "string") {
|
|
108
113
|
throw new Error("AnthropicFunctions does not support non-string output.");
|
|
109
114
|
}
|
|
@@ -168,15 +173,25 @@ export class ChatAnthropicTools extends BaseChatModel {
|
|
|
168
173
|
generations: [{ message: responseMessageWithFunctions, text: "" }],
|
|
169
174
|
};
|
|
170
175
|
}
|
|
171
|
-
return chatResult;
|
|
176
|
+
return { generations: [{ message: chatResult, text: "" }] };
|
|
172
177
|
}
|
|
173
|
-
async
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
178
|
+
async generate(messages, parsedOptions, callbacks) {
|
|
179
|
+
const baseMessages = messages.map((messageList) => messageList.map(coerceMessageLikeToMessage));
|
|
180
|
+
// generate results
|
|
181
|
+
const chatResults = await Promise.all(baseMessages.map((messageList) => this._prepareAndParseToolCall({
|
|
182
|
+
messages: messageList,
|
|
183
|
+
options: { callbacks, ...parsedOptions },
|
|
177
184
|
systemPromptTemplate: this.systemPromptTemplate,
|
|
178
185
|
stopSequences: this.stopSequences ?? [],
|
|
179
|
-
});
|
|
186
|
+
})));
|
|
187
|
+
// create combined output
|
|
188
|
+
const output = {
|
|
189
|
+
generations: chatResults.map((chatResult) => chatResult.generations),
|
|
190
|
+
};
|
|
191
|
+
return output;
|
|
192
|
+
}
|
|
193
|
+
async _generate(_messages, _options, _runManager) {
|
|
194
|
+
throw new Error("Unused.");
|
|
180
195
|
}
|
|
181
196
|
_llmType() {
|
|
182
197
|
return "anthropic_tool_calling";
|
|
@@ -187,6 +202,7 @@ export class ChatAnthropicTools extends BaseChatModel {
|
|
|
187
202
|
let name;
|
|
188
203
|
let method;
|
|
189
204
|
let includeRaw;
|
|
205
|
+
let force;
|
|
190
206
|
if (isStructuredOutputMethodParams(outputSchema)) {
|
|
191
207
|
schema = outputSchema.schema;
|
|
192
208
|
name = outputSchema.name;
|
|
@@ -198,6 +214,7 @@ export class ChatAnthropicTools extends BaseChatModel {
|
|
|
198
214
|
name = config?.name;
|
|
199
215
|
method = config?.method;
|
|
200
216
|
includeRaw = config?.includeRaw;
|
|
217
|
+
force = config?.force ?? true;
|
|
201
218
|
}
|
|
202
219
|
if (method === "jsonMode") {
|
|
203
220
|
throw new Error(`Anthropic only supports "functionCalling" as a method.`);
|
|
@@ -241,12 +258,14 @@ export class ChatAnthropicTools extends BaseChatModel {
|
|
|
241
258
|
}
|
|
242
259
|
const llm = this.bind({
|
|
243
260
|
tools,
|
|
244
|
-
tool_choice:
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
261
|
+
tool_choice: force
|
|
262
|
+
? {
|
|
263
|
+
type: "function",
|
|
264
|
+
function: {
|
|
265
|
+
name: functionName,
|
|
266
|
+
},
|
|
267
|
+
}
|
|
268
|
+
: "auto",
|
|
250
269
|
});
|
|
251
270
|
if (!includeRaw) {
|
|
252
271
|
return llm.pipe(outputParser).withConfig({
|
|
@@ -62,9 +62,21 @@ xmlParameters
|
|
|
62
62
|
const schemaType = schema.properties[key].type;
|
|
63
63
|
// Crawl for lists indistinguishable from single items
|
|
64
64
|
if (schema.properties && schema.properties[key] && schemaType === "array") {
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
65
|
+
const value = xmlParameters[key];
|
|
66
|
+
if (Array.isArray(value)) {
|
|
67
|
+
fixedParameters[key] = value;
|
|
68
|
+
}
|
|
69
|
+
else if (typeof value === "string") {
|
|
70
|
+
if (value.startsWith("[") && value.endsWith("]")) {
|
|
71
|
+
fixedParameters[key] = JSON.parse(value);
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
fixedParameters[key] = value.split(",");
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
fixedParameters[key] = [value];
|
|
79
|
+
}
|
|
68
80
|
// Crawl for objects like {"item": "my string"} that should really just be "my string"
|
|
69
81
|
if (schemaType !== "object" &&
|
|
70
82
|
typeof xmlParameters[key] === "object" &&
|
|
@@ -58,9 +58,21 @@ xmlParameters
|
|
|
58
58
|
const schemaType = schema.properties[key].type;
|
|
59
59
|
// Crawl for lists indistinguishable from single items
|
|
60
60
|
if (schema.properties && schema.properties[key] && schemaType === "array") {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
61
|
+
const value = xmlParameters[key];
|
|
62
|
+
if (Array.isArray(value)) {
|
|
63
|
+
fixedParameters[key] = value;
|
|
64
|
+
}
|
|
65
|
+
else if (typeof value === "string") {
|
|
66
|
+
if (value.startsWith("[") && value.endsWith("]")) {
|
|
67
|
+
fixedParameters[key] = JSON.parse(value);
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
fixedParameters[key] = value.split(",");
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
fixedParameters[key] = [value];
|
|
75
|
+
}
|
|
64
76
|
// Crawl for objects like {"item": "my string"} that should really just be "my string"
|
|
65
77
|
if (schemaType !== "object" &&
|
|
66
78
|
typeof xmlParameters[key] === "object" &&
|
|
@@ -95,7 +95,7 @@ test("Test ChatAnthropic in streaming mode", async () => {
|
|
|
95
95
|
}),
|
|
96
96
|
});
|
|
97
97
|
const message = new HumanMessage("Hello!");
|
|
98
|
-
const res = await model.
|
|
98
|
+
const res = await model.invoke([message]);
|
|
99
99
|
console.log({ res });
|
|
100
100
|
expect(nrNewTokens > 0).toBe(true);
|
|
101
101
|
expect(res.content).toBe(streamedCompletion);
|
|
@@ -117,7 +117,7 @@ test("Test ChatAnthropic in streaming mode with a signal", async () => {
|
|
|
117
117
|
const controller = new AbortController();
|
|
118
118
|
const message = new HumanMessage("Hello! Give me an extremely verbose response");
|
|
119
119
|
await expect(() => {
|
|
120
|
-
const res = model.
|
|
120
|
+
const res = model.invoke([message], {
|
|
121
121
|
signal: controller.signal,
|
|
122
122
|
});
|
|
123
123
|
setTimeout(() => {
|