diff --git a/CHANGELOG.md b/CHANGELOG.md index a47b8dc..9e15f1c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,9 @@ - Keeps system/developer messages, original user query, and most recent items - Drops oldest tool call/outputs from the middle when conversation grows too long - Prevents `status=incomplete` errors on providers with smaller context windows +- **Truncates large tool outputs (>8000 chars)** to prevent model output token exhaustion + - Crof's models return `incomplete` when tool results contain too much text (e.g., full HTML pages) + - Truncated outputs include `[truncated N chars]` suffix so the model knows data was cut - Added request/response logging to `~/.cache/codex-proxy/requests.log` for debugging - Proxy stderr no longer discarded by launcher (visible in terminal for debugging) diff --git a/codex-launcher_2.1.2_all.deb b/codex-launcher_2.1.2_all.deb index 4837cff..e57cfa8 100644 Binary files a/codex-launcher_2.1.2_all.deb and b/codex-launcher_2.1.2_all.deb differ diff --git a/src/translate-proxy.py b/src/translate-proxy.py index 6ea3743..a34b911 100755 --- a/src/translate-proxy.py +++ b/src/translate-proxy.py @@ -166,12 +166,24 @@ def forwarded_headers(request_headers, extra=None, browser_ua=False): return headers _MAX_INPUT_ITEMS = 30 +_MAX_TOOL_OUTPUT_CHARS = 8000 def _trim_input(input_data): - if not isinstance(input_data, list) or len(input_data) <= _MAX_INPUT_ITEMS: + if not isinstance(input_data, list): return input_data + out = [] + for item in input_data: + if item.get("type") == "function_call_output": + o = item.get("output", "") + if len(o) > _MAX_TOOL_OUTPUT_CHARS: + item = dict(item) + item["output"] = o[:_MAX_TOOL_OUTPUT_CHARS] + f"\n... [truncated {len(o) - _MAX_TOOL_OUTPUT_CHARS} chars]" + print(f"[trim] tool output truncated {len(o)} -> {_MAX_TOOL_OUTPUT_CHARS}", file=sys.stderr) + out.append(item) + if len(out) <= _MAX_INPUT_ITEMS: + return out head_end = 0 - for i, item in enumerate(input_data): + for i, item in enumerate(out): t = item.get("type") if t == "message" and item.get("role") in ("developer", "system"): head_end = i + 1 @@ -179,12 +191,12 @@ def _trim_input(input_data): head_end = i + 1 else: break - head = input_data[:head_end] + head = out[:head_end] tail_keep = _MAX_INPUT_ITEMS - len(head) - tail = input_data[-tail_keep:] - trimmed = len(input_data) - len(head) - len(tail) + tail = out[-tail_keep:] + trimmed = len(out) - len(head) - len(tail) if trimmed > 0: - print(f"[trim] {len(input_data)} items -> {len(head) + len(tail)} (dropped {trimmed} old items)", file=sys.stderr) + print(f"[trim] {len(out)} items -> {len(head) + len(tail)} (dropped {trimmed} old items)", file=sys.stderr) return head + tail # ═══════════════════════════════════════════════════════════════════