From e28350e2c43f89e4e0fd3356ea2b31113d6d9b6f Mon Sep 17 00:00:00 2001 From: Sean Turner Date: Fri, 24 Apr 2026 09:23:49 +0100 Subject: [PATCH] fix: allow repeated truncation passes for multi-message oversized payloads Remove one-shot bad_request_truncated guard so truncation retries on each 400 until nothing remains to truncate. Also scan all messages per pass instead of stopping at the first hit. Addresses review feedback from Greptile on #460. Co-Authored-By: Claude Opus 4.6 --- strix/llm/llm.py | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/strix/llm/llm.py b/strix/llm/llm.py index 6e951e2a0..a1a6012eb 100644 --- a/strix/llm/llm.py +++ b/strix/llm/llm.py @@ -161,7 +161,6 @@ class LLM: max_retries = int(Config.get("strix_llm_max_retries") or "5") bad_request_retried = False - bad_request_truncated = False for attempt in range(max_retries + 1): try: @@ -178,11 +177,9 @@ class LLM: continue truncate_enabled = Config.get("strix_truncate_on_oversize") or "" if ( - not bad_request_truncated - and truncate_enabled.lower() in ("1", "true", "yes") + truncate_enabled.lower() in ("1", "true", "yes") and self._truncate_large_tool_results(messages) ): - bad_request_truncated = True if attempt >= max_retries: self._raise_error(e) continue @@ -339,14 +336,10 @@ class LLM: def _truncate_large_tool_results( messages: list[dict[str, Any]], max_chars: int = 2000 ) -> bool: - """Aggressively truncate large tool results in messages to recover from BadRequestError. + """Truncate large tool_result XML blocks to recover from BadRequestError. - Scans messages in reverse for tool_result XML blocks that exceed max_chars and - replaces their content with a truncated version plus a skip notice. Returns True - if any truncation was performed (caller should retry the request). - - Note: All oversized tool_result blocks within a single message are truncated - in one pass — this is intentional to maximise payload size reduction per retry. + Scans all messages for tool_result blocks exceeding max_chars and truncates them. + Called repeatedly on each 400 until it returns False (nothing left to truncate). """ truncated_any = False pattern = re.compile( @@ -370,10 +363,7 @@ class LLM: content = msg.get("content") if not isinstance(content, str) or "" not in content: continue - msg["content"] = pattern.sub(_truncate_match, content) - if truncated_any: - break return truncated_any