fix(langgraph): give a message in a tuple inside an Overwrite an id too

Co-authored-by: Harsh Shroff <29751900+HarshShroff@users.noreply.github.com>
This commit is contained in:
Elior Nataf Lackritz
2026-10-11 22:48:14 -04:00
co-authored by Harsh Shroff
parent a3471dcc12
commit 46cff507a9
2 changed files with 11 additions and 3 deletions
+6 -1
View File
@@ -432,7 +432,7 @@ def ensure_message_ids(value: Any) -> None:
BaseMessages; every get_state() replay then produces a different UUID and
the same message appears with a different ID in each LangSmith trace.
Handles four input shapes:
Handles five input shapes:
- BaseMessage objects: assign a UUID if id is None.
- Dicts with a known "role" (OpenAI-style) or "type" (LangChain format) at
the root level: stamp "id" into the dict in-place. The reducer's
@@ -440,6 +440,8 @@ def ensure_message_ids(value: Any) -> None:
- Lists of the above: apply the same logic to each element, replacing dict
items with coerced BaseMessages so the shared list reference seen by
checkpoint_pending_writes and the background thread both get typed messages.
- Tuples of the above: apply the same logic to each element. A tuple can't
take a coerced message, so its dicts only get "id" stamped.
- An `Overwrite` of any of the above: apply the same logic to its value.
Mutating synchronously here (before the background thread is submitted) is
@@ -461,6 +463,9 @@ def ensure_message_ids(value: Any) -> None:
if msg.id is None:
msg.id = str(uuid4())
value[i] = msg
elif isinstance(value, tuple):
for item in value:
ensure_message_ids(item)
else:
is_overwrite, overwrite_value = _get_overwrite(value)
if is_overwrite:
@@ -141,11 +141,14 @@ def test_delta_channel_dict_style_message_gets_stable_id() -> None:
)
def test_delta_channel_message_in_an_overwrite_gets_stable_id() -> None:
@pytest.mark.parametrize("container", [list, tuple])
def test_delta_channel_message_in_an_overwrite_gets_stable_id(container: type) -> None:
graph = _build_graph(InMemorySaver())
config = {"configurable": {"thread_id": "overwrite-id-stability"}}
graph.invoke({"messages": Overwrite([HumanMessage(content="hello")])}, config)
graph.invoke(
{"messages": Overwrite(container([HumanMessage(content="hello")]))}, config
)
ids = [
next(