Fix tool_use/tool_result mismatch error when resuming after clarification
Browse files- Add validation in Claude client to remove dangling tool_use blocks
- Recreate fresh agent instances when resuming after clarification
- Prevents stale conversation state from causing API errors
- Dockerfile +1 -1
- chainlit_app.py +2 -2
- codepilot/agents/orchestrator.py +9 -1
- codepilot/llm/claude_client.py +16 -0
Dockerfile
CHANGED
|
@@ -1,5 +1,5 @@
|
|
| 1 |
# HuggingFace Spaces Dockerfile for CodePilot
|
| 2 |
-
# BUILD_VERSION:
|
| 3 |
FROM python:3.11-slim
|
| 4 |
|
| 5 |
# Set working directory
|
|
|
|
| 1 |
# HuggingFace Spaces Dockerfile for CodePilot
|
| 2 |
+
# BUILD_VERSION: 13 (v3.3.4 resume-fix - fix tool_use/tool_result bug)
|
| 3 |
FROM python:3.11-slim
|
| 4 |
|
| 5 |
# Set working directory
|
chainlit_app.py
CHANGED
|
@@ -20,8 +20,8 @@ from concurrent.futures import ThreadPoolExecutor
|
|
| 20 |
# ============================================================
|
| 21 |
# STARTUP VERSION CHECK - Change this to detect if rebuild worked
|
| 22 |
# ============================================================
|
| 23 |
-
APP_VERSION = "3.3.
|
| 24 |
-
BUILD_ID = "2024-12-19-
|
| 25 |
print("=" * 60)
|
| 26 |
print(f"[STARTUP] CodePilot Chainlit App")
|
| 27 |
print(f"[STARTUP] APP_VERSION: {APP_VERSION}")
|
|
|
|
| 20 |
# ============================================================
|
| 21 |
# STARTUP VERSION CHECK - Change this to detect if rebuild worked
|
| 22 |
# ============================================================
|
| 23 |
+
APP_VERSION = "3.3.4-resume-fix"
|
| 24 |
+
BUILD_ID = "2024-12-19-v12"
|
| 25 |
print("=" * 60)
|
| 26 |
print(f"[STARTUP] CodePilot Chainlit App")
|
| 27 |
print(f"[STARTUP] APP_VERSION: {APP_VERSION}")
|
codepilot/agents/orchestrator.py
CHANGED
|
@@ -9,7 +9,7 @@ The orchestrator is the "brain" that:
|
|
| 9 |
"""
|
| 10 |
|
| 11 |
# VERSION CHECK - If you see this, new code is running!
|
| 12 |
-
ORCHESTRATOR_VERSION = "3.3.
|
| 13 |
print(f"[ORCHESTRATOR] ========== LOADING VERSION {ORCHESTRATOR_VERSION} ==========")
|
| 14 |
|
| 15 |
from enum import Enum
|
|
@@ -299,6 +299,14 @@ class Orchestrator:
|
|
| 299 |
Returns:
|
| 300 |
Result dict from continued workflow
|
| 301 |
"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 302 |
self.provide_user_answers(user_answers)
|
| 303 |
return self._run_full_workflow(self.context.task_description)
|
| 304 |
|
|
|
|
| 9 |
"""
|
| 10 |
|
| 11 |
# VERSION CHECK - If you see this, new code is running!
|
| 12 |
+
ORCHESTRATOR_VERSION = "3.3.4-resume-fix"
|
| 13 |
print(f"[ORCHESTRATOR] ========== LOADING VERSION {ORCHESTRATOR_VERSION} ==========")
|
| 14 |
|
| 15 |
from enum import Enum
|
|
|
|
| 299 |
Returns:
|
| 300 |
Result dict from continued workflow
|
| 301 |
"""
|
| 302 |
+
print(f"[ORCHESTRATOR] Resuming after clarification...")
|
| 303 |
+
|
| 304 |
+
# BUGFIX: Recreate agent instances to ensure fresh conversation state
|
| 305 |
+
# This prevents any stale tool_use/tool_result state from previous runs
|
| 306 |
+
self.planner = PlannerAgent(model="claude-sonnet-4-5-20250929")
|
| 307 |
+
self.coder = CoderAgent(model="claude-sonnet-4-5-20250929")
|
| 308 |
+
self.reviewer = ReviewerAgent(model="claude-sonnet-4-5-20250929")
|
| 309 |
+
|
| 310 |
self.provide_user_answers(user_answers)
|
| 311 |
return self._run_full_workflow(self.context.task_description)
|
| 312 |
|
codepilot/llm/claude_client.py
CHANGED
|
@@ -122,6 +122,22 @@ class ClaudeClient:
|
|
| 122 |
"content": pending_tool_results
|
| 123 |
})
|
| 124 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 125 |
# Build request parameters
|
| 126 |
request_params = {
|
| 127 |
"model": self.model,
|
|
|
|
| 122 |
"content": pending_tool_results
|
| 123 |
})
|
| 124 |
|
| 125 |
+
# BUGFIX: Validate no dangling tool_use blocks at end of conversation
|
| 126 |
+
# Claude API requires tool_result immediately after tool_use
|
| 127 |
+
if conversation_messages:
|
| 128 |
+
last_msg = conversation_messages[-1]
|
| 129 |
+
if last_msg.get("role") == "assistant":
|
| 130 |
+
content = last_msg.get("content", [])
|
| 131 |
+
if isinstance(content, list):
|
| 132 |
+
has_tool_use = any(
|
| 133 |
+
isinstance(b, dict) and b.get("type") == "tool_use"
|
| 134 |
+
for b in content
|
| 135 |
+
)
|
| 136 |
+
if has_tool_use:
|
| 137 |
+
# Dangling tool_use - remove this message to prevent API error
|
| 138 |
+
print(f"[CLAUDE] Warning: Removing dangling assistant message with tool_use (no tool_result)")
|
| 139 |
+
conversation_messages = conversation_messages[:-1]
|
| 140 |
+
|
| 141 |
# Build request parameters
|
| 142 |
request_params = {
|
| 143 |
"model": self.model,
|