diff --git a/dspy/primitives/repl_types.py b/dspy/primitives/repl_types.py index b724a75de4..2ca3ee6701 100644 --- a/dspy/primitives/repl_types.py +++ b/dspy/primitives/repl_types.py @@ -58,8 +58,9 @@ def from_value( value_str = str(jsonable) is_truncated = len(value_str) > preview_chars if is_truncated: - half = preview_chars // 2 - preview = value_str[:half] + "..." + value_str[-half:] + head_chars = preview_chars // 2 + tail_chars = preview_chars - head_chars + preview = value_str[:head_chars] + "..." + value_str[-tail_chars:] else: preview = value_str @@ -113,9 +114,10 @@ def format_output(output: str, max_output_chars: int = 10_000) -> str: """Format output with head+tail truncation, preserving true length in header.""" raw_len = len(output) if raw_len > max_output_chars: - half = max_output_chars // 2 + head_chars = max_output_chars // 2 + tail_chars = max_output_chars - head_chars omitted = raw_len - max_output_chars - output = output[:half] + f"\n\n... ({omitted:,} characters omitted) ...\n\n" + output[-half:] + output = output[:head_chars] + f"\n\n... ({omitted:,} characters omitted) ...\n\n" + output[-tail_chars:] return f"Output ({raw_len:,} chars):\n{output}" def format(self, index: int, max_output_chars: int = 10_000) -> str: diff --git a/tests/predict/test_rlm.py b/tests/predict/test_rlm.py index e5a25497d0..f58cc8b654 100644 --- a/tests/predict/test_rlm.py +++ b/tests/predict/test_rlm.py @@ -426,6 +426,15 @@ def test_repl_entry_format_truncation(self): # True original length shown in header assert "200 chars" in formatted + @pytest.mark.parametrize( + ("limit", "head", "tail"), + [(1, "", "f"), (5, "ab", "def")], + ) + def test_repl_entry_truncation_preserves_exact_odd_limit(self, limit, head, tail): + formatted = REPLEntry.format_output("abcdef", max_output_chars=limit) + + assert formatted == f"Output (6 chars):\n{head}\n\n... ({6 - limit} characters omitted) ...\n\n{tail}" + def test_repl_entry_format_no_truncation(self): """Test REPLEntry.format() passes short output through without truncation.""" output = "a" * 50 @@ -458,6 +467,15 @@ def test_repl_variable_truncation(self): assert var.preview.endswith("b" * 25) assert "..." in var.preview + @pytest.mark.parametrize( + ("limit", "expected"), + [(1, "...f"), (5, "ab...def")], + ) + def test_repl_variable_truncation_preserves_exact_odd_limit(self, limit, expected): + var = REPLVariable.from_value("value", "abcdef", preview_chars=limit) + + assert var.preview == expected + def test_repl_variable_with_field_info(self): """Test REPLVariable includes desc and constraints from field_info.""" import dspy