fix(add_messages): guard fast path against None IDs and intra-right duplicates

Two bugs in the fast-path optimisation:

1. The left-side type guard checked isinstance(BaseMessage) but not
   id is not None. Messages without IDs (e.g. HumanMessage(content="hi"))
   would skip ID assignment and return None IDs.

2. The pure-append short-circuit only checked for overlaps between
   right and left, not duplicates within right itself. A right list
   containing two messages with the same ID would bypass the slow-path
   deduplication and return both.

Fixes:
- Add left_seq[0].id is not None to the type-guard condition.
- Replace the any() overlap check with a set-intersection check that
  also verifies len(right_id_set) == len(right_msgs) (no intra-right
  duplicates) before taking the fast return.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Sydney Runkle
2026-04-24 07:30:51 -04:00
co-authored by Claude Sonnet 4.6
parent 2a974d1b73
commit 3ef54c1ec8
+5 -3
View File
@@ -198,6 +198,7 @@ def add_messages(
if (
left_seq
and isinstance(left_seq[0], BaseMessage)
and left_seq[0].id is not None
and not isinstance(left_seq[0], BaseMessageChunk)
):
left_msgs = left_seq
@@ -228,11 +229,12 @@ def add_messages(
if remove_all_idx is not None:
return right_msgs[remove_all_idx + 1 :]
# Optimization 2: pure-append fast path — no removals and no ID overlaps.
# Builds one set over left instead of copying left + building a full dict.
# Optimization 2: pure-append fast path — no removals, no ID overlaps with
# left, and no duplicate IDs within right (all imply a dedup/update is needed).
if not has_remove:
left_ids = {m.id for m in left_msgs}
if not any(m.id in left_ids for m in right_msgs):
right_id_set = {m.id for m in right_msgs}
if len(right_id_set) == len(right_msgs) and not (right_id_set & left_ids):
result = left_msgs + right_msgs
if format == "langchain-openai":
return _format_messages(result)