From 2302a5a053d4ce38e856bc1dae9ec0bda7fd7baa Mon Sep 17 00:00:00 2001 From: "Sharad." Date: Wed, 16 Sep 2026 21:01:44 +0530 Subject: [PATCH] fix: CodexParser skips zero-length messages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Empty-content message items were emitted as empty messages, polluting token totals — unlike the Claude and OpenAI parsers which already skip empties. Skip message items whose text is empty or whitespace-only. Closes #9 --- src/ctxlens/parsers/codex.py | 2 ++ tests/test_parsers.py | 22 ++++++++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/src/ctxlens/parsers/codex.py b/src/ctxlens/parsers/codex.py index 629ad9e..de43208 100644 --- a/src/ctxlens/parsers/codex.py +++ b/src/ctxlens/parsers/codex.py @@ -92,6 +92,8 @@ def _emit_item(self, item: dict, turn: int, out: list[Message]) -> int: if itype == "message": role = item.get("role", "user") text = self._content_text(item.get("content")) + if not text.strip(): + return turn seg = Segment.ASSISTANT if role == "assistant" else Segment.USER if role == "system": seg = Segment.SYSTEM diff --git a/tests/test_parsers.py b/tests/test_parsers.py index 06c806b..e732364 100644 --- a/tests/test_parsers.py +++ b/tests/test_parsers.py @@ -71,6 +71,28 @@ def test_parse_codex(codex_session): assert s.meta.get("session_id") == "codex-abc123" +def test_parse_codex_skips_empty_content_messages(): + raw = json.dumps( + { + "session": {"id": "codex-empty"}, + "instructions": "Be concise.", + "items": [ + {"type": "message", "role": "user", "content": "hello"}, + {"type": "message", "role": "user", "content": ""}, + {"type": "message", "role": "assistant", "content": " "}, + {"type": "message", "role": "assistant", "content": "world"}, + ], + } + ) + s = parse_text(raw, fmt="codex-session") + texts = [m.text for m in s.messages] + assert "hello" in texts + assert "world" in texts + assert "" not in texts + assert " " not in texts + assert len(texts) == len(["hello", "world"]) + 1 # +1 instructions/session + + def test_parse_openai_object_has_tool_defs(openai_chat): s = parse_file(openai_chat) assert any(m.segment == Segment.TOOL_DEFINITIONS for m in s.messages)