WIP: Regression hell

This commit is contained in:
2026-03-06 21:22:21 -05:00
parent 528f0a04c3
commit f65e9b40b2
14 changed files with 359 additions and 97 deletions

View File

@@ -252,7 +252,12 @@ def _classify_gemini_error(exc: Exception) -> ProviderError:
return ProviderError("unknown", "gemini", exc)
def _classify_deepseek_error(exc: Exception) -> ProviderError:
body = str(exc).lower()
body = ""
if isinstance(exc, requests.exceptions.HTTPError) and exc.response is not None:
body = exc.response.text.lower()
else:
body = str(exc).lower()
if "429" in body or "rate" in body:
return ProviderError("rate_limit", "deepseek", exc)
if "401" in body or "403" in body or "auth" in body or "api key" in body:
@@ -263,6 +268,13 @@ def _classify_deepseek_error(exc: Exception) -> ProviderError:
return ProviderError("quota", "deepseek", exc)
if "connection" in body or "timeout" in body or "network" in body:
return ProviderError("network", "deepseek", exc)
if "400" in body or "bad request" in body:
# Try to wrap the original error with the response body for better debugging
if body:
new_exc = Exception(f"Bad Request (400): {body}")
return ProviderError("unknown", "deepseek", new_exc)
return ProviderError("unknown", "deepseek", exc)
def set_provider(provider: str, model: str) -> None:
@@ -637,6 +649,54 @@ def _build_file_diff_text(changed_items: list[dict[str, Any]]) -> str:
parts.append(f"### `{path}` (no changes detected)")
return "\n\n---\n\n".join(parts)
def _build_deepseek_tools() -> list[dict[str, Any]]:
mcp_tools: list[dict[str, Any]] = []
for spec in mcp_client.MCP_TOOL_SPECS:
if _agent_tools.get(spec["name"], True):
mcp_tools.append({
"type": "function",
"function": {
"name": spec["name"],
"description": spec["description"],
"parameters": spec["parameters"],
}
})
tools_list = mcp_tools
if _agent_tools.get(TOOL_NAME, True):
powershell_tool: dict[str, Any] = {
"type": "function",
"function": {
"name": TOOL_NAME,
"description": (
"Run a PowerShell script within the project base_dir. "
"Use this to create, edit, rename, or delete files and directories. "
"The working directory is set to base_dir automatically. "
"Always prefer targeted edits over full rewrites where possible. "
"stdout and stderr are returned to you as the result."
),
"parameters": {
"type": "object",
"properties": {
"script": {
"type": "string",
"description": "The PowerShell script to execute."
}
},
"required": ["script"]
}
}
}
tools_list.append(powershell_tool)
return tools_list
_CACHED_DEEPSEEK_TOOLS: Optional[list[dict[str, Any]]] = None
def _get_deepseek_tools() -> list[dict[str, Any]]:
global _CACHED_DEEPSEEK_TOOLS
if _CACHED_DEEPSEEK_TOOLS is None:
_CACHED_DEEPSEEK_TOOLS = _build_deepseek_tools()
return _CACHED_DEEPSEEK_TOOLS
def _content_block_to_dict(block: Any) -> dict[str, Any]:
if isinstance(block, dict):
return block
@@ -1356,33 +1416,56 @@ def _send_deepseek(md_content: str, user_message: str, base_dir: str,
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json",
}
current_api_messages: list[dict[str, Any]] = []
is_reasoner = _model == "deepseek-reasoner"
# Update history following Anthropic pattern
with _deepseek_history_lock:
for msg in _deepseek_history:
current_api_messages.append(msg)
initial_user_message_content = user_message
if discussion_history:
initial_user_message_content = f"[DISCUSSION HISTORY]\n\n{discussion_history}\n\n---\n\n{user_message}"
current_api_messages.append({"role": "user", "content": initial_user_message_content})
request_payload: dict[str, Any] = {
"model": _model,
"messages": current_api_messages,
"temperature": _temperature,
"max_tokens": _max_tokens,
"stream": stream,
}
sys_msg = {"role": "system", "content": f"{_get_combined_system_prompt()}\n\n<context>\n{md_content}\n</context>"}
request_payload["messages"].insert(0, sys_msg)
if discussion_history and not _deepseek_history:
user_content = f"[DISCUSSION HISTORY]\n\n{discussion_history}\n\n---\n\n{user_message}"
else:
user_content = user_message
_deepseek_history.append({"role": "user", "content": user_content})
all_text_parts: list[str] = []
_cumulative_tool_bytes = 0
round_idx = 0
while round_idx <= MAX_TOOL_ROUNDS + 1:
for round_idx in range(MAX_TOOL_ROUNDS + 2):
current_api_messages: list[dict[str, Any]] = []
with _deepseek_history_lock:
for msg in _deepseek_history:
current_api_messages.append(msg)
sys_msg = {"role": "system", "content": f"{_get_combined_system_prompt()}\n\n<context>\n{md_content}\n</context>"}
current_api_messages.insert(0, sys_msg)
request_payload: dict[str, Any] = {
"model": _model,
"messages": current_api_messages,
"stream": stream,
}
if not is_reasoner:
request_payload["temperature"] = _temperature
request_payload["max_tokens"] = _max_tokens
tools = _get_deepseek_tools()
if tools:
request_payload["tools"] = tools
events.emit("request_start", payload={"provider": "deepseek", "model": _model, "round": round_idx, "streaming": stream})
try:
response = requests.post(api_url, headers=headers, json=request_payload, timeout=60, stream=stream)
response = requests.post(api_url, headers=headers, json=request_payload, timeout=120, stream=stream)
response.raise_for_status()
except requests.exceptions.RequestException as e:
raise _classify_deepseek_error(e) from e
assistant_text = ""
tool_calls_raw = []
reasoning_content = ""
finish_reason = "stop"
usage = {}
if stream:
aggregated_content = ""
aggregated_tool_calls: list[dict[str, Any]] = []
@@ -1443,10 +1526,12 @@ def _send_deepseek(md_content: str, user_message: str, base_dir: str,
reasoning_content = message.get("reasoning_content", "")
finish_reason = choice.get("finish_reason", "stop")
usage = response_data.get("usage", {})
thinking_tags = ""
if reasoning_content:
thinking_tags = f"<thinking>\n{reasoning_content}\n</thinking>\n"
full_assistant_text = thinking_tags + assistant_text
with _deepseek_history_lock:
msg_to_store: dict[str, Any] = {"role": "assistant", "content": assistant_text}
if reasoning_content:
@@ -1454,8 +1539,10 @@ def _send_deepseek(md_content: str, user_message: str, base_dir: str,
if tool_calls_raw:
msg_to_store["tool_calls"] = tool_calls_raw
_deepseek_history.append(msg_to_store)
if full_assistant_text:
all_text_parts.append(full_assistant_text)
_append_comms("IN", "response", {
"round": round_idx,
"stop_reason": finish_reason,
@@ -1464,6 +1551,7 @@ def _send_deepseek(md_content: str, user_message: str, base_dir: str,
"usage": usage,
"streaming": stream
})
if finish_reason != "tool_calls" and not tool_calls_raw:
break
if round_idx > MAX_TOOL_ROUNDS:
@@ -1507,16 +1595,11 @@ def _send_deepseek(md_content: str, user_message: str, base_dir: str,
"content": f"SYSTEM WARNING: Cumulative tool output exceeded {_MAX_TOOL_OUTPUT_BYTES // 1000}KB budget. Provide your final answer now."
})
_append_comms("OUT", "request", {"message": f"[TOOL OUTPUT BUDGET EXCEEDED: {_cumulative_tool_bytes} bytes]"})
with _deepseek_history_lock:
for tr in tool_results_for_history:
_deepseek_history.append(tr)
next_messages: list[dict[str, Any]] = []
with _deepseek_history_lock:
for msg in _deepseek_history:
next_messages.append(msg)
next_messages.insert(0, sys_msg)
request_payload["messages"] = next_messages
round_idx += 1
return "\n\n".join(all_text_parts) if all_text_parts else "(No text returned)"
except Exception as e:
raise _classify_deepseek_error(e) from e