This commit is contained in:
2026-03-07 14:39:40 -05:00
parent a856d73f95
commit 6ab359deda
3 changed files with 53 additions and 9 deletions

View File

@@ -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) out = "USER REJECTED: tool execution cancelled" if _res is None else await mcp_client.async_dispatch(name, args)
else: else:
out = await mcp_client.async_dispatch(name, args) 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: elif name == TOOL_NAME:
scr = cast(str, args.get("script", "")) scr = cast(str, args.get("script", ""))
_append_comms("OUT", "tool_call", {"name": TOOL_NAME, "id": call_id, "script": scr}) _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) out = await asyncio.to_thread(_run_script, scr, base_dir, qa_callback, patch_callback)
else: else:
out = f"ERROR: unknown tool '{name}'" out = f"ERROR: unknown tool '{name}'"
if tool_log_callback:
tool_log_callback(f"ERROR: {name}", out)
return (name, call_id, out, name) 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") raise ValueError("MiniMax API key not found in credentials.toml")
_minimax_client = OpenAI(api_key=api_key, base_url="https://api.minimax.chat/v1") _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, def _send_deepseek(md_content: str, user_message: str, base_dir: str,
file_items: list[dict[str, Any]] | None = None, file_items: list[dict[str, Any]] | None = None,
discussion_history: str = "", discussion_history: str = "",
@@ -1522,6 +1546,7 @@ def _send_deepseek(md_content: str, user_message: str, base_dir: str,
# Update history following Anthropic pattern # Update history following Anthropic pattern
with _deepseek_history_lock: with _deepseek_history_lock:
_repair_deepseek_history(_deepseek_history)
if discussion_history and not _deepseek_history: if discussion_history and not _deepseek_history:
user_content = f"[DISCUSSION HISTORY]\n\n{discussion_history}\n\n---\n\n{user_message}" user_content = f"[DISCUSSION HISTORY]\n\n{discussion_history}\n\n---\n\n{user_message}"
else: else:

View File

@@ -159,6 +159,7 @@ class AppController:
"output_tokens": 0, "output_tokens": 0,
"cache_read_input_tokens": 0, "cache_read_input_tokens": 0,
"cache_creation_input_tokens": 0, "cache_creation_input_tokens": 0,
"total_tokens": 0,
"last_latency": 0.0 "last_latency": 0.0
} }
self.mma_tier_usage: Dict[str, Dict[str, Any]] = { self.mma_tier_usage: Dict[str, Dict[str, Any]] = {
@@ -673,6 +674,21 @@ class AppController:
with self._disc_entries_lock: with self._disc_entries_lock:
self.disc_entries.append(item) 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: def _test_callback_func_write_to_file(self, data: str) -> None:
"""A dummy function that a custom_callback would execute for testing.""" """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") 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: def _handle_request_event(self, event: events.UserRequestEvent) -> None:
"""Processes a UserRequestEvent by calling the AI client.""" """Processes a UserRequestEvent by calling the AI client."""
ai_client.set_current_tier(None) # Ensure main discussion is untagged ai_client.set_current_tier(None) # Ensure main discussion is untagged
if self.ui_auto_add_history: # Clear response area for new turn
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
self.ai_response = "" self.ai_response = ""
csp = filter(bool, [self.ui_global_system_prompt.strip(), self.ui_project_system_prompt.strip()]) 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)) ai_client.set_custom_system_prompt("\n\n".join(csp))
@@ -1063,6 +1071,16 @@ class AppController:
"model": model "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"): if kind in ("tool_result", "tool_call"):
role = "Tool" if kind == "tool_result" else "Vendor API" role = "Tool" if kind == "tool_result" else "Vendor API"
content = "" content = ""

View File

@@ -262,6 +262,7 @@ class App:
sys.stderr.flush() sys.stderr.flush()
self._process_pending_gui_tasks() self._process_pending_gui_tasks()
self._process_pending_history_adds() self._process_pending_history_adds()
self.controller._process_pending_tool_calls()
self._render_track_proposal_modal() self._render_track_proposal_modal()
self._render_patch_modal() self._render_patch_modal()
# Auto-save (every 60s) # Auto-save (every 60s)