Skip to content

Conversation

christian-bromann
Copy link
Member

@christian-bromann christian-bromann commented Oct 1, 2025

This patch introduces threadLevelCallCount and runModelCallCount as _readonly` runtime properties. The values are managed as following:

  • runModelCallCount: class property of the AgentNode - value is not persisted
  • threadLevelCallCount: private state property of the AgentNode

The behavior can be showcased with the following basic test:

const model = new FakeToolCallingModel({});
const middleware = createMiddleware({
  name: "middleware",
  beforeModel: async (_, runtime) => {
    expect(runtime.threadLevelCallCount).toBe(0);
    expect(runtime.runModelCallCount).toBe(0);

    /**
     * try to override the private state
     */
    return {
      _privateState: {
        threadLevelCallCount: 123,
        runModelCallCount: 123,
      },
    };
  },
  modifyModelRequest: async (_, __, runtime) => {
    expect(runtime.threadLevelCallCount).toBe(0);
    expect(runtime.runModelCallCount).toBe(0);
  },
  afterModel: async (_, runtime) => {
    expect(runtime.threadLevelCallCount).toBe(1);
    expect(runtime.runModelCallCount).toBe(1);
        /**
         * try to override the private state
         */
    return {
      _privateState: {
        threadLevelCallCount: 123,
        runModelCallCount: 123,
      },
    };
  },
});

const agent = createAgent({
  model,
  middleware: [middleware] as const,
  checkpointer,
});

const result = await agent.invoke(
  {
    messages: [new HumanMessage("What is the weather in Tokyo?")],
  },
  config
);

// @ts-expect-error should not be defined in the state
expect(result.threadLevelCallCount).toBe(undefined);
// @ts-expect-error should not be defined in the state
expect(result.runModelCallCount).toBe(undefined);
// @ts-expect-error should not be defined in the state
expect(result._privateState).toBe(undefined);

Creating a new agent and passing the checkpointer and config along should result in the following:

const model = new FakeToolCallingModel({});
const middleware = createMiddleware({
  name: "middleware",
  beforeModel: async (_, runtime) => {
    expect(runtime.threadLevelCallCount).toBe(1);
    expect(runtime.runModelCallCount).toBe(0);
  },
  modifyModelRequest: async (_, __, runtime) => {
    expect(runtime.threadLevelCallCount).toBe(1);
    expect(runtime.runModelCallCount).toBe(0);
  },
  afterModel: async (_, runtime) => {
    expect(runtime.threadLevelCallCount).toBe(2);
    expect(runtime.runModelCallCount).toBe(1);
  },
});

const agent = createAgent({
  model,
  middleware: [middleware] as const,
  checkpointer,
});

await agent.invoke(
  {
    messages: [new HumanMessage("What is the weather in Tokyo?")],
  },
  config
);

Copy link

changeset-bot bot commented Oct 1, 2025

⚠️ No Changeset found

Latest commit: 4f2faeb

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

Click here to learn what changesets are, and how to add one.

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

Copy link

vercel bot commented Oct 1, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

2 Skipped Deployments
Project Deployment Preview Comments Updated (UTC)
langchainjs-api-refs Ignored Ignored Oct 5, 2025 10:19pm
langchainjs-docs Ignored Ignored Preview Comment Oct 5, 2025 10:19pm

@christian-bromann christian-bromann merged commit e1b32fd into v1 Oct 5, 2025
35 checks passed
@christian-bromann christian-bromann deleted the cb/call-count branch October 5, 2025 22:28
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.

1 participant