fix(langgraph): preModelHook llmInputMessages should not keep incrementing messages (#1407)

This commit is contained in:
David Duong
2025-07-16 16:01:43 +02:00
committed by GitHub
parent 430ae93031
commit 02f9e023ea
3 changed files with 48 additions and 16 deletions
+5
View File
@@ -0,0 +1,5 @@
---
"@langchain/langgraph": patch
---
fix(langgraph): preModelHook `llmInputMessages` should not keep concatenating messages
@@ -457,7 +457,7 @@ type WithStateGraphNodes<K extends string, Graph> = Graph extends StateGraph<
const PreHookAnnotation = Annotation.Root({
llmInputMessages: Annotation<BaseMessage[], Messages>({
reducer: messagesStateReducer,
reducer: (_, update) => messagesStateReducer([], update),
default: () => [],
}),
});
+42 -15
View File
@@ -1135,33 +1135,60 @@ describe("createReactAgent with ToolNode", () => {
describe("createReactAgent with hooks", () => {
it("preModelHook", async () => {
const llm = new FakeToolCallingChatModel({
responses: [new AIMessage({ id: "0", content: "Hello!" })],
responses: [
new AIMessage({ id: "0", content: "Hello!" }),
new AIMessage({ id: "1", content: "Hello again!" }),
],
});
const llmSpy = vi.spyOn(llm, "_generate");
const checkpointer = new MemorySaver();
// Test `llm_input_messages`
let agent = createReactAgent({
llm,
tools: [],
preModelHook: () => ({
llmInputMessages: [
new HumanMessage({ id: "human", content: "pre-hook" }),
],
}),
preModelHook: () => ({ llmInputMessages: "pre-hook" }),
checkpointer,
});
expect("pre_model_hook" in agent.nodes).toBe(true);
expect(await agent.invoke({ messages: [new HumanMessage("hi?")] })).toEqual(
{
messages: [
new _AnyIdHumanMessage("hi?"),
new AIMessage({ id: "0", content: "Hello!" }),
],
}
);
expect(
await agent.invoke(
{ messages: [new HumanMessage("hi?")] },
{ configurable: { thread_id: "1" } }
)
).toEqual({
messages: [
new _AnyIdHumanMessage("hi?"),
new AIMessage({ id: "0", content: "Hello!" }),
],
});
expect(llmSpy).toHaveBeenCalledWith(
[new HumanMessage({ id: "human", content: "pre-hook" })],
[new _AnyIdHumanMessage({ content: "pre-hook" })],
expect.anything(),
undefined
);
llmSpy.mockClear();
// Second invocation
expect(
await agent.invoke(
{ messages: [new HumanMessage("hi again")] },
{ configurable: { thread_id: "1" } }
)
).toEqual({
messages: [
new _AnyIdHumanMessage("hi?"),
new AIMessage({ id: "0", content: "Hello!" }),
new _AnyIdHumanMessage("hi again"),
new AIMessage({ id: "1", content: "Hello again!" }),
],
});
expect(llmSpy).toHaveBeenCalledWith(
[new _AnyIdHumanMessage("pre-hook")],
expect.anything(),
undefined
);