From 96f0aa541b55d0210b4001f0fdde5f15a86ca5ad Mon Sep 17 00:00:00 2001 From: Ed_ Date: Thu, 25 Jun 2026 19:58:41 -0400 Subject: [PATCH] refactor(ai_client): complete FileItem migration (finish half-measure pattern) Phase 2: FileItem Before: 3 .get('path',...) sites in src/ai_client.py After: 0 .get('path',...) sites in src/ai_client.py Delta: -3 (expected: -3) The half-measure pattern 'fi if hasattr(fi, 'path') else models.FileItem(path=fi.get('path', 'attachment'))' has been replaced with the canonical conversion pattern: fi if isinstance(fi, models.FileItem) else models.FileItem.from_dict(fi) This: 1. Replaces hasattr() (ad-hoc duck typing) with isinstance() (explicit) 2. Eliminates the .get('path', 'attachment') defensive call 3. Uses models.FileItem.from_dict() for the dict->dataclass conversion Applies to 3 sites in src/ai_client.py: - _send_grok (line 2565) - _send_qwen (line 2808) - _send_llama (line 2900) Tests: 14/14 pass (test_ai_client_result, test_ai_client_tool_loop, test_file_item_model). Total .get('key', default) count in src/*.py: 52 -> 49 (delta -3, matches expected for Phase 2). --- src/ai_client.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ai_client.py b/src/ai_client.py index 12171924..70282116 100644 --- a/src/ai_client.py +++ b/src/ai_client.py @@ -2562,7 +2562,7 @@ def _send_grok(md_content: str, user_message: str, base_dir: str, if file_items: for fi in file_items: if fi.get("is_image") and fi.get("base64_data"): - fi_item = fi if hasattr(fi, 'path') else models.FileItem(path=fi.get('path', 'attachment')) + fi_item = fi if isinstance(fi, models.FileItem) else models.FileItem.from_dict(fi) user_content = f"[IMAGE: {fi_item.path or 'attachment'}]\n{user_content}" if discussion_history and not history: history.append({"role": "user", "content": f"[DISCUSSION HISTORY]\n\n{discussion_history}\n\n---\n\n{user_message}"}) @@ -2805,7 +2805,7 @@ def _send_qwen(md_content: str, user_message: str, base_dir: str, if file_items: for fi in file_items: if fi.get("is_image") and fi.get("base64_data"): - fi_item = fi if hasattr(fi, 'path') else models.FileItem(path=fi.get('path', 'attachment')) + fi_item = fi if isinstance(fi, models.FileItem) else models.FileItem.from_dict(fi) user_content = f"[IMAGE: {fi_item.path or 'attachment'}]\n{user_content}" if discussion_history and not history: history.append({"role": "user", "content": f"[DISCUSSION HISTORY]\n\n{discussion_history}\n\n---\n\n{user_message}"}) @@ -2897,7 +2897,7 @@ def _send_llama(md_content: str, user_message: str, base_dir: str, if file_items: for fi in file_items: if fi.get("is_image") and fi.get("base64_data"): - fi_item = fi if hasattr(fi, 'path') else models.FileItem(path=fi.get('path', 'attachment')) + fi_item = fi if isinstance(fi, models.FileItem) else models.FileItem.from_dict(fi) user_content = f"[IMAGE: {fi_item.path or 'attachment'}]\n{user_content}" if discussion_history and not history: history.append({"role": "user", "content": f"[DISCUSSION HISTORY]\n\n{discussion_history}\n\n---\n\n{user_message}"})