From 6ab359deda9fdfbaf2153c0ed0211e8371319668 Mon Sep 17 00:00:00 2001 From: Ed_ Date: Sat, 7 Mar 2026 14:39:40 -0500 Subject: [PATCH] fixes --- src/ai_client.py | 25 +++++++++++++++++++++++++ src/app_controller.py | 36 +++++++++++++++++++++++++++--------- src/gui_2.py | 1 + 3 files changed, 53 insertions(+), 9 deletions(-) diff --git a/src/ai_client.py b/src/ai_client.py index 3068267..945a93f 100644 --- a/src/ai_client.py +++ b/src/ai_client.py @@ -638,12 +638,16 @@ async def _execute_single_tool_call_async( out = "USER REJECTED: tool execution cancelled" if _res is None else await mcp_client.async_dispatch(name, args) else: out = await mcp_client.async_dispatch(name, args) + if tool_log_callback: + tool_log_callback(f"# MCP TOOL: {name}\n{json.dumps(args, indent=1)}", out) elif name == TOOL_NAME: scr = cast(str, args.get("script", "")) _append_comms("OUT", "tool_call", {"name": TOOL_NAME, "id": call_id, "script": scr}) out = await asyncio.to_thread(_run_script, scr, base_dir, qa_callback, patch_callback) else: out = f"ERROR: unknown tool '{name}'" + if tool_log_callback: + tool_log_callback(f"ERROR: {name}", out) return (name, call_id, out, name) @@ -1495,6 +1499,26 @@ def _ensure_minimax_client() -> None: raise ValueError("MiniMax API key not found in credentials.toml") _minimax_client = OpenAI(api_key=api_key, base_url="https://api.minimax.chat/v1") +def _repair_deepseek_history(history: list[dict[str, Any]]) -> None: + if not history: + return + last = history[-1] + if last.get("role") != "assistant": + return + tool_calls = last.get("tool_calls", []) + if not tool_calls: + return + call_ids = [tc.get("id") for tc in tool_calls if tc.get("id")] + for cid in call_ids: + # Check if already present in tail (to be safe, though usually missing if we're here) + already_has = any(m.get("role") == "tool" and m.get("tool_call_id") == cid for m in history[-len(call_ids)-1:]) + if not already_has: + history.append({ + "role": "tool", + "tool_call_id": cid, + "content": "ERROR: Session was interrupted before tool result was recorded.", + }) + def _send_deepseek(md_content: str, user_message: str, base_dir: str, file_items: list[dict[str, Any]] | None = None, discussion_history: str = "", @@ -1522,6 +1546,7 @@ def _send_deepseek(md_content: str, user_message: str, base_dir: str, # Update history following Anthropic pattern with _deepseek_history_lock: + _repair_deepseek_history(_deepseek_history) if discussion_history and not _deepseek_history: user_content = f"[DISCUSSION HISTORY]\n\n{discussion_history}\n\n---\n\n{user_message}" else: diff --git a/src/app_controller.py b/src/app_controller.py index a06d81d..f60add4 100644 --- a/src/app_controller.py +++ b/src/app_controller.py @@ -159,6 +159,7 @@ class AppController: "output_tokens": 0, "cache_read_input_tokens": 0, "cache_creation_input_tokens": 0, + "total_tokens": 0, "last_latency": 0.0 } self.mma_tier_usage: Dict[str, Dict[str, Any]] = { @@ -673,6 +674,21 @@ class AppController: with self._disc_entries_lock: self.disc_entries.append(item) + def _process_pending_tool_calls(self) -> bool: + """Drains pending tool calls into the tool log. Returns True if any were processed.""" + with self._pending_tool_calls_lock: + items = self._pending_tool_calls[:] + self._pending_tool_calls.clear() + if not items: + return False + for item in items: + self._append_tool_log( + item.get("script", ""), + item.get("result", ""), + source_tier=item.get("source_tier") + ) + return True + def _test_callback_func_write_to_file(self, data: str) -> None: """A dummy function that a custom_callback would execute for testing.""" callback_path = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "tests", "artifacts", "temp_callback_output.txt") @@ -996,15 +1012,7 @@ class AppController: def _handle_request_event(self, event: events.UserRequestEvent) -> None: """Processes a UserRequestEvent by calling the AI client.""" ai_client.set_current_tier(None) # Ensure main discussion is untagged - if self.ui_auto_add_history: - with self._pending_history_adds_lock: - self._pending_history_adds.append({ - "role": "User", - "content": event.prompt, - "collapsed": True, - "ts": project_manager.now_ts() - }) - # Clear response area for new turn + # Clear response area for new turn self.ai_response = "" csp = filter(bool, [self.ui_global_system_prompt.strip(), self.ui_project_system_prompt.strip()]) ai_client.set_custom_system_prompt("\n\n".join(csp)) @@ -1063,6 +1071,16 @@ class AppController: "model": model }) + if kind == "request": + if self.ui_auto_add_history: + with self._pending_history_adds_lock: + self._pending_history_adds.append({ + "role": "User", + "content": payload.get("message", ""), + "collapsed": payload.get("collapsed", False), + "ts": entry.get("ts", project_manager.now_ts()) + }) + if kind in ("tool_result", "tool_call"): role = "Tool" if kind == "tool_result" else "Vendor API" content = "" diff --git a/src/gui_2.py b/src/gui_2.py index 1d05491..e62ad2a 100644 --- a/src/gui_2.py +++ b/src/gui_2.py @@ -262,6 +262,7 @@ class App: sys.stderr.flush() self._process_pending_gui_tasks() self._process_pending_history_adds() + self.controller._process_pending_tool_calls() self._render_track_proposal_modal() self._render_patch_modal() # Auto-save (every 60s)