Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions renderers/kimi_k25.py
Original file line number Diff line number Diff line change
Expand Up @@ -1183,18 +1183,20 @@ def emit_image(
emit_text("<think></think>", -1)

# Merge prev mm_data (earlier-turn images) with the new turn's items.
# Copy the per-modality lists (not just the outer dict) so appending
# below never mutates the caller's previous_multi_modal_data.
merged_hashes: dict[str, list[str]] = (
dict(previous_multi_modal_data.mm_hashes)
{k: list(v) for k, v in previous_multi_modal_data.mm_hashes.items()}
if previous_multi_modal_data
else {}
)
merged_placeholders: dict[str, list[PlaceholderRange]] = (
dict(previous_multi_modal_data.mm_placeholders)
{k: list(v) for k, v in previous_multi_modal_data.mm_placeholders.items()}
if previous_multi_modal_data
else {}
)
merged_items: dict[str, list[dict[str, Any]]] = (
dict(previous_multi_modal_data.mm_items)
{k: list(v) for k, v in previous_multi_modal_data.mm_items.items()}
if previous_multi_modal_data
else {}
)
Expand Down
8 changes: 5 additions & 3 deletions renderers/qwen35.py
Original file line number Diff line number Diff line change
Expand Up @@ -834,18 +834,20 @@ def flush_buf() -> None:
emit_text("\n\n", -1)

# Merge prev mm_data (images from earlier turns) with the new turn's.
# Copy the per-modality lists (not just the outer dict) so appending
# below never mutates the caller's previous_multi_modal_data.
merged_hashes: dict[str, list[str]] = (
dict(previous_multi_modal_data.mm_hashes)
{k: list(v) for k, v in previous_multi_modal_data.mm_hashes.items()}
if previous_multi_modal_data
else {}
)
merged_placeholders: dict[str, list[PlaceholderRange]] = (
dict(previous_multi_modal_data.mm_placeholders)
{k: list(v) for k, v in previous_multi_modal_data.mm_placeholders.items()}
if previous_multi_modal_data
else {}
)
merged_items: dict[str, list[dict[str, Any]]] = (
dict(previous_multi_modal_data.mm_items)
{k: list(v) for k, v in previous_multi_modal_data.mm_items.items()}
if previous_multi_modal_data
else {}
)
Expand Down
10 changes: 6 additions & 4 deletions renderers/qwen3_vl.py
Original file line number Diff line number Diff line change
Expand Up @@ -828,19 +828,21 @@ def render_media_content(content: Any) -> None:
em.text("assistant\n", is_sampled=False, is_content=False)
em.finalize()

# Merge prev mm_data with the new turn's items.
# Merge prev mm_data with the new turn's items. Copy the per-modality
# lists (not just the outer dict) so appending below never mutates the
# caller's previous_multi_modal_data.
merged_hashes = (
dict(previous_multi_modal_data.mm_hashes)
{k: list(v) for k, v in previous_multi_modal_data.mm_hashes.items()}
if previous_multi_modal_data
else {}
)
merged_placeholders = (
dict(previous_multi_modal_data.mm_placeholders)
{k: list(v) for k, v in previous_multi_modal_data.mm_placeholders.items()}
if previous_multi_modal_data
else {}
)
merged_items = (
dict(previous_multi_modal_data.mm_items)
{k: list(v) for k, v in previous_multi_modal_data.mm_items.items()}
if previous_multi_modal_data
else {}
)
Expand Down
19 changes: 19 additions & 0 deletions tests/test_multimodal.py
Original file line number Diff line number Diff line change
Expand Up @@ -583,6 +583,12 @@ def test_multimodal_bridge_extends_and_carries_mm_data(
]

initial_rendered = renderer.render(initial, add_generation_prompt=True)
prior_mm = initial_rendered.multi_modal_data
prior_counts = (
len(prior_mm.mm_placeholders.get(modality, [])),
len(prior_mm.mm_items.get(modality, [])),
len(prior_mm.mm_hashes.get(modality, [])),
)
# ``previous_completion_ids`` mirrors what a sampler would emit
# starting AFTER the prompt's assistant role opener — i.e. the
# response text followed by ``<|im_end|>``.
Expand Down Expand Up @@ -632,6 +638,19 @@ def test_multimodal_bridge_extends_and_carries_mm_data(
hashes = bridged_mm.mm_hashes.get(modality, [])
assert len(items) == 2 and len(hashes) == 2

# (2b) The prior turn's sidecar is unchanged — the bridge copies the
# per-modality lists, so the carried-forward item doesn't grow the
# caller's previous_multi_modal_data in place.
assert (
(
len(prior_mm.mm_placeholders.get(modality, [])),
len(prior_mm.mm_items.get(modality, [])),
len(prior_mm.mm_hashes.get(modality, [])),
)
== prior_counts
== (1, 1, 1)
), f"{mm_model_name} / {modality}: bridge mutated previous_multi_modal_data"

# (3) Extension contains the new turn's pad run, and its
# placeholder offset lands inside the extension region.
pad_id = tokenizer.convert_tokens_to_ids(kit["placeholder_token"])
Expand Down
Loading