From 510527c4007a7b109b49e38325404aec4f629fb6 Mon Sep 17 00:00:00 2001 From: Ed_ Date: Thu, 19 Mar 2026 19:53:09 -0400 Subject: [PATCH] feat(backend): Implement multi-take sequence differencing and text formatting utility --- src/app_controller.py | 3 +- src/gui_2.py | 3 +- src/synthesis_formatter.py | 42 ++++++++++++++++++++++ tests/test_synthesis_formatter.py | 59 +++++++++++++++++++++++++++++++ 4 files changed, 105 insertions(+), 2 deletions(-) create mode 100644 src/synthesis_formatter.py create mode 100644 tests/test_synthesis_formatter.py diff --git a/src/app_controller.py b/src/app_controller.py index cafd84f..0e8a4cd 100644 --- a/src/app_controller.py +++ b/src/app_controller.py @@ -2206,7 +2206,8 @@ class AppController: project_manager.branch_discussion(self.project, self.active_discussion, new_name, index) self._switch_discussion(new_name) - def _rename_discussion(self, old_name: str, new_name: str) -> None: disc_sec = self.project.get("discussion", {}) + def _rename_discussion(self, old_name: str, new_name: str) -> None: + disc_sec = self.project.get("discussion", {}) discussions = disc_sec.get("discussions", {}) if old_name not in discussions: return diff --git a/src/gui_2.py b/src/gui_2.py index e5365da..1c3c754 100644 --- a/src/gui_2.py +++ b/src/gui_2.py @@ -2493,7 +2493,8 @@ def hello(): break # Break from inner loop, clipper will re-step imgui.same_line() if imgui.button("Branch"): - self._branch_discussion(i) imgui.same_line() + self._branch_discussion(i) + imgui.same_line() preview = entry["content"].replace("\\n", " ")[:60] if len(entry["content"]) > 60: preview += "..." if not preview.strip() and entry.get("thinking_segments"): diff --git a/src/synthesis_formatter.py b/src/synthesis_formatter.py new file mode 100644 index 0000000..34a3578 --- /dev/null +++ b/src/synthesis_formatter.py @@ -0,0 +1,42 @@ +def format_takes_diff(takes: dict[str, list[dict]]) -> str: + if not takes: + return "" + + histories = list(takes.values()) + if not histories: + return "" + + min_len = min(len(h) for h in histories) + common_prefix_len = 0 + for i in range(min_len): + first_msg = histories[0][i] + if all(h[i] == first_msg for h in histories): + common_prefix_len += 1 + else: + break + + shared_lines = [] + for i in range(common_prefix_len): + msg = histories[0][i] + shared_lines.append(f"{msg.get('role', 'unknown')}: {msg.get('content', '')}") + + shared_text = "=== Shared History ===" + if shared_lines: + shared_text += "\n" + "\n".join(shared_lines) + + variation_lines = [] + if len(takes) > 1: + for take_name, history in takes.items(): + if len(history) > common_prefix_len: + variation_lines.append(f"[{take_name}]") + for i in range(common_prefix_len, len(history)): + msg = history[i] + variation_lines.append(f"{msg.get('role', 'unknown')}: {msg.get('content', '')}") + variation_lines.append("") + else: + # Single take case + pass + + variations_text = "=== Variations ===\n" + "\n".join(variation_lines) + + return shared_text + "\n\n" + variations_text diff --git a/tests/test_synthesis_formatter.py b/tests/test_synthesis_formatter.py new file mode 100644 index 0000000..a42b281 --- /dev/null +++ b/tests/test_synthesis_formatter.py @@ -0,0 +1,59 @@ +import pytest +from src.synthesis_formatter import format_takes_diff + +def test_format_takes_diff_empty(): + assert format_takes_diff({}) == "" + +def test_format_takes_diff_single_take(): + takes = { + "take1": [ + {"role": "user", "content": "hello"}, + {"role": "assistant", "content": "hi"} + ] + } + expected = "=== Shared History ===\nuser: hello\nassistant: hi\n\n=== Variations ===\n" + assert format_takes_diff(takes) == expected + +def test_format_takes_diff_common_prefix(): + takes = { + "take1": [ + {"role": "user", "content": "hello"}, + {"role": "assistant", "content": "hi"}, + {"role": "user", "content": "how are you?"}, + {"role": "assistant", "content": "I am fine."} + ], + "take2": [ + {"role": "user", "content": "hello"}, + {"role": "assistant", "content": "hi"}, + {"role": "user", "content": "what is the time?"}, + {"role": "assistant", "content": "It is noon."} + ] + } + expected = ( + "=== Shared History ===\n" + "user: hello\n" + "assistant: hi\n\n" + "=== Variations ===\n" + "[take1]\n" + "user: how are you?\n" + "assistant: I am fine.\n\n" + "[take2]\n" + "user: what is the time?\n" + "assistant: It is noon.\n" + ) + assert format_takes_diff(takes) == expected + +def test_format_takes_diff_no_common_prefix(): + takes = { + "take1": [{"role": "user", "content": "a"}], + "take2": [{"role": "user", "content": "b"}] + } + expected = ( + "=== Shared History ===\n\n" + "=== Variations ===\n" + "[take1]\n" + "user: a\n\n" + "[take2]\n" + "user: b\n" + ) + assert format_takes_diff(takes) == expected