Skip to content

Commit 960af6c

Browse files
authored
feat(core): add tool metadata (#8644)
1 parent f0d505e commit 960af6c

File tree

4 files changed

+49
-5
lines changed

4 files changed

+49
-5
lines changed

langchain-core/src/messages/tool.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ export interface ToolMessageFieldsWithToolCallId extends ToolMessageFields {
3131
* @version 0.2.19
3232
*/
3333
status?: "success" | "error";
34+
metadata?: Record<string, unknown>;
3435
}
3536

3637
/**
@@ -78,6 +79,8 @@ export class ToolMessage extends BaseMessage implements DirectToolOutput {
7879

7980
tool_call_id: string;
8081

82+
metadata?: Record<string, unknown>;
83+
8184
/**
8285
* Artifact of the Tool execution which is not meant to be sent to the model.
8386
*
@@ -109,6 +112,7 @@ export class ToolMessage extends BaseMessage implements DirectToolOutput {
109112
this.tool_call_id = fields.tool_call_id;
110113
this.artifact = fields.artifact;
111114
this.status = fields.status;
115+
this.metadata = fields.metadata;
112116
}
113117

114118
_getType(): MessageType {

langchain-core/src/tools/index.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ import {
3636
type ZodStringV4,
3737
type ZodObjectV3,
3838
type ZodObjectV4,
39+
getSchemaDescription,
3940
} from "../utils/types/zod.js";
4041
import type {
4142
StructuredToolCallInput,
@@ -137,6 +138,7 @@ export abstract class StructuredTool<
137138
fields?.verboseParsingErrors ?? this.verboseParsingErrors;
138139
this.responseFormat = fields?.responseFormat ?? this.responseFormat;
139140
this.defaultConfig = fields?.defaultConfig ?? this.defaultConfig;
141+
this.metadata = fields?.metadata ?? this.metadata;
140142
}
141143

142144
protected abstract _call(
@@ -247,7 +249,7 @@ export abstract class StructuredTool<
247249
parsed = inputForValidation as SchemaOutputT;
248250
}
249251

250-
const config = parseCallbackConfigArg(configArg);
252+
const config = parseCallbackConfigArg(configArg) as ToolRunnableConfig;
251253
const callbackManager_ = CallbackManager.configure(
252254
config.callbacks,
253255
this.callbacks,
@@ -307,6 +309,7 @@ export abstract class StructuredTool<
307309
artifact,
308310
toolCallId,
309311
name: this.name,
312+
metadata: this.metadata,
310313
});
311314
await runManager?.handleToolEnd(formattedOutput);
312315
return formattedOutput as ToolReturnType<TArg, TConfig, ToolOutputT>;
@@ -638,7 +641,7 @@ export function tool<
638641
...fields,
639642
description:
640643
fields.description ??
641-
(fields.schema as { description?: string } | undefined)?.description ??
644+
(fields.schema && getSchemaDescription(fields.schema)) ??
642645
`${fields.name} tool`,
643646
func: async (input, runManager, config) => {
644647
return new Promise<ToolOutputT>((resolve, reject) => {
@@ -708,8 +711,9 @@ function _formatToolOutput<TOutput extends ToolOutputType>(params: {
708711
name: string;
709712
artifact?: unknown;
710713
toolCallId?: string;
714+
metadata?: Record<string, unknown>;
711715
}): ToolMessage | TOutput {
712-
const { content, artifact, toolCallId } = params;
716+
const { content, artifact, toolCallId, metadata } = params;
713717
if (toolCallId && !isDirectToolOutput(content)) {
714718
if (
715719
typeof content === "string" ||
@@ -721,13 +725,15 @@ function _formatToolOutput<TOutput extends ToolOutputType>(params: {
721725
artifact,
722726
tool_call_id: toolCallId,
723727
name: params.name,
728+
metadata,
724729
});
725730
} else {
726731
return new ToolMessage({
727732
content: _stringify(content),
728733
artifact,
729734
tool_call_id: toolCallId,
730735
name: params.name,
736+
metadata,
731737
});
732738
}
733739
} else {

langchain-core/src/tools/tests/tools.test.ts

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,13 @@ import { z as z4 } from "zod/v4";
44

55
import {
66
DynamicStructuredTool,
7+
DynamicTool,
78
StructuredToolParams,
89
ToolInputParsingException,
910
isStructuredToolParams,
1011
tool,
1112
} from "../index.js";
12-
import { ToolMessage } from "../../messages/tool.js";
13+
import { ToolCall, ToolMessage } from "../../messages/tool.js";
1314
import { RunnableConfig } from "../../runnables/types.js";
1415

1516
test("Tool should error if responseFormat is content_and_artifact but the function doesn't return a tuple", async () => {
@@ -447,3 +448,30 @@ describe("isStructuredToolParams", () => {
447448
expect(isStructuredToolParams(nonStructuredToolParams)).toBe(false);
448449
});
449450
});
451+
452+
describe("DynamicTool", () => {
453+
test("will thread metadata through to a resulting ToolMessage", async () => {
454+
const tool = new DynamicTool({
455+
name: "test",
456+
description: "test",
457+
metadata: {
458+
foo: "bar",
459+
},
460+
func: async () => "test",
461+
});
462+
463+
const input: ToolCall = {
464+
id: "test_id",
465+
name: "test",
466+
args: { input: "test" },
467+
type: "tool_call",
468+
};
469+
470+
const result = await tool.invoke(input);
471+
472+
expect(result).toBeInstanceOf(ToolMessage);
473+
expect(result.metadata).toEqual({
474+
foo: "bar",
475+
});
476+
});
477+
});

langchain-core/src/tools/types.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,12 +81,18 @@ export interface ToolParams extends BaseLangChainParams {
8181
* @default false
8282
*/
8383
verboseParsingErrors?: boolean;
84+
/**
85+
* Metadata for the tool.
86+
*/
87+
metadata?: Record<string, unknown>;
8488
}
8589

8690
export type ToolRunnableConfig<
8791
// eslint-disable-next-line @typescript-eslint/no-explicit-any
8892
ConfigurableFieldType extends Record<string, any> = Record<string, any>
89-
> = RunnableConfig<ConfigurableFieldType> & { toolCall?: ToolCall };
93+
> = RunnableConfig<ConfigurableFieldType> & {
94+
toolCall?: ToolCall;
95+
};
9096

9197
/**
9298
* Schema for defining tools.

0 commit comments

Comments
 (0)