Skip to content

Conversation

@karpetrosyan
Copy link
Contributor

@karpetrosyan karpetrosyan commented Jan 13, 2026

Fix tool runner pushMessages and tool response caching

Summary

This PR fixes a bug in the BetaToolRunner where tool response caching was not properly invalidated when pushMessages() was called, causing stale cached responses to be reused. The fix ensures that when the conversation is mutated via pushMessages(), the tool response cache is correctly invalidated.

Changes

Core Fix (src/lib/tools/BetaToolRunner.ts)

  • Added #lastProcessedMessage field to track the last message processed for tool response generation
  • Modified generateToolResponse() to check mutation state (this.#mutated) before using cached message
  • Updated #generateToolResponse() to use reference equality check to invalidate cache when message reference changes
  • This ensures that when pushMessages() adds new messages, subsequent calls to generateToolResponse() correctly process the new conversation state

Behavior

Before this fix, calling pushMessages() after a tool use would still result in stale cached tool responses being used. Now, the cache is properly invalidated based on message reference changes, ensuring the runner correctly handles dynamically modified conversation history.

@karpetrosyan karpetrosyan requested a review from a team as a code owner January 13, 2026 13:45
@karpetrosyan karpetrosyan changed the base branch from main to next January 13, 2026 13:46
@karpetrosyan karpetrosyan marked this pull request as draft January 13, 2026 14:48
@karpetrosyan karpetrosyan marked this pull request as ready for review January 13, 2026 16:26
@karpetrosyan
Copy link
Contributor Author

@yjp20 would you be able to take a look?

Comment on lines 288 to 293
return this.#generateToolResponse(message);
return this.#generateToolResponse({
role: message.role,
content: message.content,
});
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this change necessary?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It was needed for better comparison with JSON.stringify, but it doesn’t look very reliable anyway 😞, especially without stable stringifying.
I think we can just go with reference comparison—I’ve changed that behavior.

Copy link
Contributor

Choose a reason for hiding this comment

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

If we can, I think we should encode these in the tests rather than an example file and keep the examples a bit more minimal (there's a lot of them already)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I’ve moved pushMessage to the advanced example to avoid adding another example script, and I’ve also added some basic tests for pushMessages.

helpers.md Outdated
- [`examples/tools-helpers-zod.ts`](examples/tools-helpers-zod.ts) - Zod-based tools
- [`examples/tools-helpers-json-schema.ts`](examples/tools-helpers-json-schema.ts) - JSON Schema tools
- [`examples/tools-helpers-advanced.ts`](examples/tools-helpers-advanced.ts) - Advanced tool runner patterns
- [`examples/tools-helpers-pushmessages.ts`](examples/tools-helpers-pushmessages.ts) - Using `pushMessages()` to guide based on tool results
Copy link
Contributor

Choose a reason for hiding this comment

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

we should delete this now, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yep, good catch, thanks

*/
async generateToolResponse() {
const message = (await this.#message) ?? this.params.messages.at(-1);
const message = (this.#mutated ? undefined : await this.#message) ?? this.params.messages.at(-1);
Copy link
Contributor

Choose a reason for hiding this comment

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

Isn't this a breaking change?

I am missing the motivation for this change, could you please add a PR description?

Copy link
Contributor Author

@karpetrosyan karpetrosyan Jan 27, 2026

Choose a reason for hiding this comment

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

this.#message points to the last API response. When the user adds new messages (via pushMessages or setMessagesParams), we shouldn’t use the last API response for generating the tool response, since we might end up putting tool_result blocks somewhere other than right after the tool_use blocks.

I've also updated the PR description

@stainless-app stainless-app bot force-pushed the next branch 2 times, most recently from eeb7fab to 7b4849b Compare January 29, 2026 17:24
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.

3 participants