diff --git a/conductor/tracks/thinking_trace_handling_20260313/plan.md b/conductor/tracks/thinking_trace_handling_20260313/plan.md index 7a90ca6..98c4811 100644 --- a/conductor/tracks/thinking_trace_handling_20260313/plan.md +++ b/conductor/tracks/thinking_trace_handling_20260313/plan.md @@ -7,14 +7,13 @@ - [x] Task: Implement: Update parsing logic in `src/ai_client.py` or a dedicated utility to extract segments from raw provider responses. - [ ] Task: Conductor - User Manual Verification 'Phase 1: Core Parsing & Model Update' (Protocol in workflow.md) - [ ] Task: Write Tests: Verify that `ProjectManager` correctly serializes and deserializes messages with thinking segments to/from TOML history files. -- [ ] Task: Implement: Update `src/project_manager.py` to handle the new `ChatMessage` schema during session save/load. -- [ ] Task: Implement: Ensure `src/aggregate.py` or relevant context builders include thinking traces in the "Discussion History" sent back to the AI. -- [ ] Task: Conductor - User Manual Verification 'Phase 2: Persistence & History Integration' (Protocol in workflow.md) +- [ ] Task: Conductor - User Manual Verification 'Phase 1: Core Parsing & Model Update' (Protocol in workflow.md) -## Phase 3: GUI Rendering - Comms & Discussion -- [ ] Task: Write Tests: Verify the GUI rendering logic correctly handles messages with and without thinking segments. -- [ ] Task: Implement: Create a reusable `_render_thinking_trace` helper in `src/gui_2.py` using a collapsible header (e.g., `imgui.collapsing_header`). -- [ ] Task: Implement: Integrate the thinking trace renderer into the **Comms History** panel in `src/gui_2.py`. +## Phase 2: Persistence & History Integration +- [x] Task: Write Tests: Verify that `ProjectManager` correctly serializes and deserializes messages with thinking segments to/from TOML history files. +- [x] Task: Implement: Update `src/project_manager.py` to handle the new `ChatMessage` schema during session save/load. +- [x] Task: Implement: Ensure `src/aggregate.py` or relevant context builders include thinking traces in the "Discussion History" sent back to the AI. +- [ ] Task: Conductor - User Manual Verification 'Phase 2: Persistence & History Integration' (Protocol in workflow.md) - [ ] Task: Implement: Integrate the thinking trace renderer into the **Discussion Hub** message loop in `src/gui_2.py`. - [ ] Task: Conductor - User Manual Verification 'Phase 3: GUI Rendering - Comms & Discussion' (Protocol in workflow.md) diff --git a/tests/test_thinking_persistence.py b/tests/test_thinking_persistence.py new file mode 100644 index 0000000..c30f61c --- /dev/null +++ b/tests/test_thinking_persistence.py @@ -0,0 +1,94 @@ +import pytest +import tempfile +import os +from pathlib import Path +from src import project_manager +from src.models import ThinkingSegment + + +def test_save_and_load_history_with_thinking_segments(): + with tempfile.TemporaryDirectory() as tmpdir: + project_path = Path(tmpdir) / "test_project" + project_path.mkdir() + + project_file = project_path / "test_project.toml" + project_file.write_text("[project]\nname = 'test'\n") + + history_data = { + "entries": [ + { + "role": "AI", + "content": "Here's the response", + "thinking_segments": [ + {"content": "Let me think about this...", "marker": "thinking"} + ], + "ts": "2026-03-13T10:00:00", + "collapsed": False, + }, + { + "role": "User", + "content": "Hello", + "ts": "2026-03-13T09:00:00", + "collapsed": False, + }, + ] + } + + project_manager.save_project( + {"project": {"name": "test"}}, project_file, disc_data=history_data + ) + + loaded = project_manager.load_history(project_file) + + assert "entries" in loaded + assert len(loaded["entries"]) == 2 + + ai_entry = loaded["entries"][0] + assert ai_entry["role"] == "AI" + assert ai_entry["content"] == "Here's the response" + assert "thinking_segments" in ai_entry + assert len(ai_entry["thinking_segments"]) == 1 + assert ( + ai_entry["thinking_segments"][0]["content"] == "Let me think about this..." + ) + + user_entry = loaded["entries"][1] + assert user_entry["role"] == "User" + assert "thinking_segments" not in user_entry + + +def test_entry_to_str_with_thinking(): + entry = { + "role": "AI", + "content": "Response text", + "thinking_segments": [{"content": "Thinking...", "marker": "thinking"}], + "ts": "2026-03-13T10:00:00", + } + result = project_manager.entry_to_str(entry) + assert "@2026-03-13T10:00:00" in result + assert "AI:" in result + assert "Response text" in result + + +def test_str_to_entry_with_thinking(): + raw = "@2026-03-13T10:00:00\nAI:\nResponse text" + roles = ["User", "AI", "Vendor API", "System", "Reasoning"] + result = project_manager.str_to_entry(raw, roles) + assert result["role"] == "AI" + assert result["content"] == "Response text" + assert "ts" in result + + +def test_clean_nones_removes_thinking(): + entry = {"role": "AI", "content": "Test", "thinking_segments": None, "ts": None} + cleaned = project_manager.clean_nones(entry) + assert "thinking_segments" not in cleaned + assert "ts" not in cleaned + + +if __name__ == "__main__": + test_save_and_load_history_with_thinking_segments() + test_entry_to_str_with_thinking() + test_str_to_entry_with_thinking() + test_clean_nones_removes_thinking() + print("All project_manager thinking tests passed!")