Skip to content

Conversation

@christian-bromann
Copy link
Member

After trying to support state and context aware tools through contextSchema and stateSchema as tool arguments, here is a 2nd approach:

This patch attempts to add more overwrites to the tool function to allow users to define the second tool function argument to be a ToolRuntime. Note that specific ToolRuntime parameters are only applied (in ToolNode) when using the tool via createAgent.

@changeset-bot
Copy link

changeset-bot bot commented Nov 4, 2025

🦋 Changeset detected

Latest commit: 865079a

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 3 packages
Name Type
@langchain/core Patch
langchain Patch
@langchain/standard-tests Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

Copy link
Member

@hntrl hntrl left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like this a lot better! Still not sold on the idea of needing more tool overloads, see my comment.

I've got a radical idea that appeases the encapsulation goblin inside of me + lets us avoid one of the deadly sins of TS types (for your consideration):

const definition = createAgentDefinition({
  stateSchema: z.object(),
  contextSchema: z.object(),
  store
});

// runtime can be auto-typed to include schemas + store
const myTool1 = definition.tool((state, runtime) => {}, {});

const agent = createAgent({
  model: "foobar",
  tools: [myTool1],
  definition
});

export type ToolRuntime<
TState = unknown,
TContext = unknown
> = RunnableConfig & {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

think we want this to extend ToolRunnableConfig? (since additional properties like toolCall get added within the tool invocation)

Copy link
Member Author

@christian-bromann christian-bromann Nov 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I couldn't quite use ToolRunnableConfig as it makes context an optional property which it isn't it every case, so I extended ToolRuntime with toolCall to match the same type.

| DynamicTool<ToolOutputT>;

// Overloads with ToolRuntime as CallOptions
export function tool<
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what's the purpose of these overloads if TState + TContext is only used in the runtime arg? Would say something different if we were recommending the usage of this should be by passing in TState + TContext as generic args, but I don't think that's what we want either. E.g. this should be possible w/o overloads?

const myTool = tool((state, runtime: ToolRuntime<...>) => {}, {})

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This are unfortunately required because the runtime type is otherwise defined as:

  options:
    | CallOptions
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    | Record<string, any>
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    | (Record<string, any> & CallOptions)
) => RunOutput | Promise<RunOutput>;

which fails because of:

Type 'Record<string, any>' is missing the following properties from type '{ state: { userId: string; }; toolCallId: string; config: ToolRunnableConfig<Record<string, any>, any>; context: { db: { foo: string; }; }; store: BaseStore<string, unknown> | null; writer: ((chunk: unknown) => void) | null; }': state, toolCallId, config, context, and 2 more.ts(2769)

Copy link
Contributor

@sydney-runkle sydney-runkle left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems much more in line w/ what we have in python, and looks like a less significant change. nice!

Add changeset for ToolRuntime support in langchain.
@christian-bromann christian-bromann merged commit 0a8a23b into main Nov 6, 2025
24 checks passed
@christian-bromann christian-bromann deleted the cb/toolruntime-take-2 branch November 6, 2025 02:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants