Clear python cache when running script: https://github.com/BeehiveInnovations/zen-mcp-server/issues/96
Improved retry error logging
Cleanup
This commit is contained in:
Fahad
2025-06-21 05:56:50 +04:00
parent 76edd30e9a
commit 6fa2d63eac
14 changed files with 141 additions and 154 deletions

View File

@@ -14,9 +14,9 @@ import os
# These values are used in server responses and for tracking releases # These values are used in server responses and for tracking releases
# IMPORTANT: This is the single source of truth for version and author info # IMPORTANT: This is the single source of truth for version and author info
# Semantic versioning: MAJOR.MINOR.PATCH # Semantic versioning: MAJOR.MINOR.PATCH
__version__ = "5.5.0" __version__ = "5.5.1"
# Last update date in ISO format # Last update date in ISO format
__updated__ = "2025-06-20" __updated__ = "2025-06-21"
# Primary maintainer # Primary maintainer
__author__ = "Fahad Gilani" __author__ = "Fahad Gilani"

View File

@@ -214,7 +214,8 @@ class GeminiModelProvider(ModelProvider):
time.sleep(delay) time.sleep(delay)
# If we get here, all retries failed # If we get here, all retries failed
error_msg = f"Gemini API error for model {resolved_name} after {max_retries} attempts: {str(last_exception)}" actual_attempts = attempt + 1 # Convert from 0-based index to human-readable count
error_msg = f"Gemini API error for model {resolved_name} after {actual_attempts} attempt{'s' if actual_attempts > 1 else ''}: {str(last_exception)}"
raise RuntimeError(error_msg) from last_exception raise RuntimeError(error_msg) from last_exception
def count_tokens(self, text: str, model_name: str) -> int: def count_tokens(self, text: str, model_name: str) -> int:

View File

@@ -377,7 +377,8 @@ class OpenAICompatibleProvider(ModelProvider):
break break
# If we get here, all retries failed # If we get here, all retries failed
error_msg = f"o3-pro responses endpoint error after {max_retries} attempts: {str(last_exception)}" actual_attempts = attempt + 1 # Convert from 0-based index to human-readable count
error_msg = f"o3-pro responses endpoint error after {actual_attempts} attempt{'s' if actual_attempts > 1 else ''}: {str(last_exception)}"
logging.error(error_msg) logging.error(error_msg)
raise RuntimeError(error_msg) from last_exception raise RuntimeError(error_msg) from last_exception
@@ -541,9 +542,8 @@ class OpenAICompatibleProvider(ModelProvider):
time.sleep(delay) time.sleep(delay)
# If we get here, all retries failed # If we get here, all retries failed
error_msg = ( actual_attempts = attempt + 1 # Convert from 0-based index to human-readable count
f"{self.FRIENDLY_NAME} API error for model {model_name} after {max_retries} attempts: {str(last_exception)}" error_msg = f"{self.FRIENDLY_NAME} API error for model {model_name} after {actual_attempts} attempt{'s' if actual_attempts > 1 else ''}: {str(last_exception)}"
)
logging.error(error_msg) logging.error(error_msg)
raise RuntimeError(error_msg) from last_exception raise RuntimeError(error_msg) from last_exception

View File

@@ -56,6 +56,14 @@ get_version() {
grep -E '^__version__ = ' config.py 2>/dev/null | sed 's/__version__ = "\(.*\)"/\1/' || echo "unknown" grep -E '^__version__ = ' config.py 2>/dev/null | sed 's/__version__ = "\(.*\)"/\1/' || echo "unknown"
} }
# Clear Python cache files to prevent import issues
clear_python_cache() {
print_info "Clearing Python cache files..."
find . -name "*.pyc" -delete 2>/dev/null || true
find . -name "__pycache__" -type d -exec rm -rf {} + 2>/dev/null || true
print_success "Python cache cleared"
}
# ---------------------------------------------------------------------------- # ----------------------------------------------------------------------------
# Platform Detection Functions # Platform Detection Functions
# ---------------------------------------------------------------------------- # ----------------------------------------------------------------------------
@@ -780,12 +788,14 @@ show_help() {
echo " -v, --version Show version information" echo " -v, --version Show version information"
echo " -f, --follow Follow server logs in real-time" echo " -f, --follow Follow server logs in real-time"
echo " -c, --config Show configuration instructions for Claude clients" echo " -c, --config Show configuration instructions for Claude clients"
echo " --clear-cache Clear Python cache and exit (helpful for import issues)"
echo "" echo ""
echo "Examples:" echo "Examples:"
echo " $0 Setup and start the MCP server" echo " $0 Setup and start the MCP server"
echo " $0 -f Setup and follow logs" echo " $0 -f Setup and follow logs"
echo " $0 -c Show configuration instructions" echo " $0 -c Show configuration instructions"
echo " $0 --version Show version only" echo " $0 --version Show version only"
echo " $0 --clear-cache Clear Python cache (fixes import issues)"
echo "" echo ""
echo "For more information, visit:" echo "For more information, visit:"
echo " https://github.com/BeehiveInnovations/zen-mcp-server" echo " https://github.com/BeehiveInnovations/zen-mcp-server"
@@ -844,6 +854,14 @@ main() {
-f|--follow) -f|--follow)
# Continue with normal setup then follow logs # Continue with normal setup then follow logs
;; ;;
--clear-cache)
# Clear cache and exit
clear_python_cache
print_success "Cache cleared successfully"
echo ""
echo "You can now run './run-server.sh' normally"
exit 0
;;
"") "")
# Normal setup without following logs # Normal setup without following logs
;; ;;
@@ -873,6 +891,9 @@ main() {
# Step 1: Docker cleanup # Step 1: Docker cleanup
cleanup_docker cleanup_docker
# Step 1.5: Clear Python cache to prevent import issues
clear_python_cache
# Step 2: Find Python # Step 2: Find Python
local python_cmd local python_cmd
python_cmd=$(find_python) || exit 1 python_cmd=$(find_python) || exit 1

View File

@@ -167,7 +167,7 @@ This happens every time a user tries to log in. The error occurs in the password
return False return False
response1_data = self._parse_debug_response(response1) response1_data = self._parse_debug_response(response1)
if not self._validate_investigation_response(response1_data, 1, True, "investigation_in_progress"): if not self._validate_investigation_response(response1_data, 1, True, "pause_for_investigation"):
return False return False
self.logger.info(f" ✅ Step 1 successful, continuation_id: {continuation_id}") self.logger.info(f" ✅ Step 1 successful, continuation_id: {continuation_id}")
@@ -184,7 +184,7 @@ This happens every time a user tries to log in. The error occurs in the password
"findings": "Missing 'import hashlib' statement at the top of user_auth.py file. The error occurs because hashlib is used in hash_password() method on line 12 but never imported. Simple one-line fix: add 'import hashlib' after line 2.", "findings": "Missing 'import hashlib' statement at the top of user_auth.py file. The error occurs because hashlib is used in hash_password() method on line 12 but never imported. Simple one-line fix: add 'import hashlib' after line 2.",
"files_checked": [self.error_log_file, self.missing_import_file], "files_checked": [self.error_log_file, self.missing_import_file],
"relevant_files": [self.missing_import_file], "relevant_files": [self.missing_import_file],
"relevant_methods": ["UserAuth.hash_password", "UserAuth.verify_password"], "relevant_context": ["UserAuth.hash_password", "UserAuth.verify_password"],
"hypothesis": "Missing 'import hashlib' statement causes NameError when hash_password method executes", "hypothesis": "Missing 'import hashlib' statement causes NameError when hash_password method executes",
"confidence": "certain", # Use certain - should skip expert analysis "confidence": "certain", # Use certain - should skip expert analysis
"continuation_id": continuation_id, "continuation_id": continuation_id,
@@ -264,7 +264,7 @@ This happens every time a user tries to log in. The error occurs in the password
"findings": "After thorough investigation, identified that the issue is caused by method name typo in Calculator.calculate_total() - calls self.add_number() instead of self.add_numbers(). Simple fix: change line 14 from 'add_number' to 'add_numbers'.", "findings": "After thorough investigation, identified that the issue is caused by method name typo in Calculator.calculate_total() - calls self.add_number() instead of self.add_numbers(). Simple fix: change line 14 from 'add_number' to 'add_numbers'.",
"files_checked": [self.typo_bug_file], "files_checked": [self.typo_bug_file],
"relevant_files": [self.typo_bug_file], "relevant_files": [self.typo_bug_file],
"relevant_methods": ["Calculator.calculate_total", "Calculator.add_numbers"], "relevant_context": ["Calculator.calculate_total", "Calculator.add_numbers"],
"hypothesis": "Method name typo in calculate_total() calls non-existent add_number() instead of add_numbers()", "hypothesis": "Method name typo in calculate_total() calls non-existent add_number() instead of add_numbers()",
"confidence": "certain", # Should always be trusted "confidence": "certain", # Should always be trusted
"model": "flash", "model": "flash",
@@ -318,7 +318,7 @@ This happens every time a user tries to log in. The error occurs in the password
"findings": "IndentationError in data_processor.py line 8 - results.append(processed) is incorrectly indented. Should align with the 'if' statement above it.", "findings": "IndentationError in data_processor.py line 8 - results.append(processed) is incorrectly indented. Should align with the 'if' statement above it.",
"files_checked": [self.indentation_file], "files_checked": [self.indentation_file],
"relevant_files": [self.indentation_file], "relevant_files": [self.indentation_file],
"relevant_methods": ["process_data"], "relevant_context": ["process_data"],
"hypothesis": "Incorrect indentation causes IndentationError in process_data function", "hypothesis": "Incorrect indentation causes IndentationError in process_data function",
"confidence": "high", # Regular high confidence, NOT certain "confidence": "high", # Regular high confidence, NOT certain
"model": "flash", "model": "flash",
@@ -400,7 +400,7 @@ This happens every time a user tries to log in. The error occurs in the password
"findings": "Found the issue: line 8 'results.append(processed)' is indented incorrectly. It should align with the 'if' statement, not be at the same level as the 'for' loop.", "findings": "Found the issue: line 8 'results.append(processed)' is indented incorrectly. It should align with the 'if' statement, not be at the same level as the 'for' loop.",
"files_checked": [self.indentation_file], "files_checked": [self.indentation_file],
"relevant_files": [self.indentation_file], "relevant_files": [self.indentation_file],
"relevant_methods": ["process_data"], "relevant_context": ["process_data"],
"hypothesis": "Line 8 has incorrect indentation level causing IndentationError", "hypothesis": "Line 8 has incorrect indentation level causing IndentationError",
"confidence": "medium", "confidence": "medium",
"continuation_id": continuation_id, "continuation_id": continuation_id,
@@ -423,7 +423,7 @@ This happens every time a user tries to log in. The error occurs in the password
"findings": "Confirmed: line 8 'results.append(processed)' needs to be indented 4 more spaces to align with line 6 'if item > 0:'. This is a simple indentation fix.", "findings": "Confirmed: line 8 'results.append(processed)' needs to be indented 4 more spaces to align with line 6 'if item > 0:'. This is a simple indentation fix.",
"files_checked": [self.indentation_file], "files_checked": [self.indentation_file],
"relevant_files": [self.indentation_file], "relevant_files": [self.indentation_file],
"relevant_methods": ["process_data"], "relevant_context": ["process_data"],
"hypothesis": "IndentationError on line 8 due to incorrect indentation level - needs 4 more spaces", "hypothesis": "IndentationError on line 8 due to incorrect indentation level - needs 4 more spaces",
"confidence": "certain", # Final step with certain "confidence": "certain", # Final step with certain
"continuation_id": continuation_id, "continuation_id": continuation_id,
@@ -455,10 +455,10 @@ This happens every time a user tries to log in. The error occurs in the password
self.logger.error("Expected at least 1 step in complete investigation") self.logger.error("Expected at least 1 step in complete investigation")
return False return False
# Check that investigation summary includes progression # Check that work summary includes progression
investigation_summary = complete_investigation.get("investigation_summary", "") work_summary = complete_investigation.get("work_summary", "")
if "Total steps:" not in investigation_summary and "Steps taken:" not in investigation_summary: if "Total steps:" not in work_summary and "Steps taken:" not in work_summary:
self.logger.error("Investigation summary should show steps information") self.logger.error("Work summary should show steps information")
return False return False
self.logger.info(" ✅ Multi-step investigation with certain ending successful") self.logger.info(" ✅ Multi-step investigation with certain ending successful")

View File

@@ -191,7 +191,7 @@ RuntimeError: dictionary changed size during iteration
"findings": "Found the issue: cleanup_expired_sessions modifies self.active_sessions dictionary while iterating over it with .items(). This causes RuntimeError when del is called during iteration.", "findings": "Found the issue: cleanup_expired_sessions modifies self.active_sessions dictionary while iterating over it with .items(). This causes RuntimeError when del is called during iteration.",
"files_checked": [self.error_file, self.buggy_file], "files_checked": [self.error_file, self.buggy_file],
"relevant_files": [self.buggy_file], "relevant_files": [self.buggy_file],
"relevant_methods": ["SessionManager.cleanup_expired_sessions"], "relevant_context": ["SessionManager.cleanup_expired_sessions"],
"hypothesis": "Dictionary is being modified during iteration causing RuntimeError", "hypothesis": "Dictionary is being modified during iteration causing RuntimeError",
"confidence": "high", "confidence": "high",
"continuation_id": continuation_id, "continuation_id": continuation_id,
@@ -212,8 +212,8 @@ RuntimeError: dictionary changed size during iteration
self.logger.error("Files checked count not properly tracked") self.logger.error("Files checked count not properly tracked")
return False return False
if investigation_status.get("relevant_methods", 0) != 1: if investigation_status.get("relevant_context", 0) != 1:
self.logger.error("Relevant methods not properly tracked") self.logger.error("Relevant context not properly tracked")
return False return False
if investigation_status.get("current_confidence") != "high": if investigation_status.get("current_confidence") != "high":
@@ -288,7 +288,7 @@ RuntimeError: dictionary changed size during iteration
"findings": "Found inefficient nested loops in data processor causing O(n²) complexity", "findings": "Found inefficient nested loops in data processor causing O(n²) complexity",
"files_checked": ["/processor/algorithm.py"], "files_checked": ["/processor/algorithm.py"],
"relevant_files": ["/processor/algorithm.py"], "relevant_files": ["/processor/algorithm.py"],
"relevant_methods": ["DataProcessor.process_batch"], "relevant_context": ["DataProcessor.process_batch"],
"hypothesis": "Inefficient algorithm causing performance issues", "hypothesis": "Inefficient algorithm causing performance issues",
"confidence": "medium", "confidence": "medium",
"backtrack_from_step": 2, # Backtrack from step 2 "backtrack_from_step": 2, # Backtrack from step 2
@@ -331,7 +331,7 @@ RuntimeError: dictionary changed size during iteration
"findings": "Found dictionary modification during iteration", "findings": "Found dictionary modification during iteration",
"files_checked": [self.buggy_file], "files_checked": [self.buggy_file],
"relevant_files": [self.buggy_file], "relevant_files": [self.buggy_file],
"relevant_methods": ["SessionManager.cleanup_expired_sessions"], "relevant_context": ["SessionManager.cleanup_expired_sessions"],
}, },
) )
if not response0 or not continuation_id: if not response0 or not continuation_id:
@@ -350,7 +350,7 @@ RuntimeError: dictionary changed size during iteration
"findings": "Root cause identified: del self.active_sessions[session_id] on line 46 modifies dictionary during iteration starting at line 44. Fix: collect expired IDs first, then delete.", "findings": "Root cause identified: del self.active_sessions[session_id] on line 46 modifies dictionary during iteration starting at line 44. Fix: collect expired IDs first, then delete.",
"files_checked": [self.buggy_file], "files_checked": [self.buggy_file],
"relevant_files": [self.buggy_file], "relevant_files": [self.buggy_file],
"relevant_methods": ["SessionManager.cleanup_expired_sessions"], "relevant_context": ["SessionManager.cleanup_expired_sessions"],
"hypothesis": "Dictionary modification during iteration causes RuntimeError in cleanup_expired_sessions", "hypothesis": "Dictionary modification during iteration causes RuntimeError in cleanup_expired_sessions",
"confidence": "high", "confidence": "high",
"continuation_id": continuation_id, "continuation_id": continuation_id,
@@ -404,11 +404,11 @@ RuntimeError: dictionary changed size during iteration
return False return False
complete_investigation = response_final_data["complete_investigation"] complete_investigation = response_final_data["complete_investigation"]
if not complete_investigation.get("relevant_methods"): if not complete_investigation.get("relevant_context"):
self.logger.error("Missing relevant methods in complete investigation") self.logger.error("Missing relevant context in complete investigation")
return False return False
if "SessionManager.cleanup_expired_sessions" not in complete_investigation["relevant_methods"]: if "SessionManager.cleanup_expired_sessions" not in complete_investigation["relevant_context"]:
self.logger.error("Expected method not found in investigation summary") self.logger.error("Expected method not found in investigation summary")
return False return False
@@ -436,7 +436,7 @@ RuntimeError: dictionary changed size during iteration
"findings": "The bug is on line 44-47: for loop iterates over dict.items() while del modifies the dict inside the loop. Fix is simple: collect expired IDs first, then delete after iteration.", "findings": "The bug is on line 44-47: for loop iterates over dict.items() while del modifies the dict inside the loop. Fix is simple: collect expired IDs first, then delete after iteration.",
"files_checked": [self.buggy_file], "files_checked": [self.buggy_file],
"relevant_files": [self.buggy_file], "relevant_files": [self.buggy_file],
"relevant_methods": ["SessionManager.cleanup_expired_sessions"], "relevant_context": ["SessionManager.cleanup_expired_sessions"],
"hypothesis": "Dictionary modification during iteration causes RuntimeError - fix is straightforward", "hypothesis": "Dictionary modification during iteration causes RuntimeError - fix is straightforward",
"confidence": "certain", # This should skip expert analysis "confidence": "certain", # This should skip expert analysis
"model": "flash", "model": "flash",
@@ -604,7 +604,7 @@ def validate_input(data):
"findings": "Initial analysis of data processing components", "findings": "Initial analysis of data processing components",
"files_checked": [file1, file2], "files_checked": [file1, file2],
"relevant_files": [file1], # This should be referenced, not embedded "relevant_files": [file1], # This should be referenced, not embedded
"relevant_methods": ["process_data"], "relevant_context": ["process_data"],
"hypothesis": "Investigating data flow", "hypothesis": "Investigating data flow",
"confidence": "low", "confidence": "low",
"model": "flash", "model": "flash",
@@ -644,7 +644,7 @@ def validate_input(data):
"findings": "Found potential issues in validation logic", "findings": "Found potential issues in validation logic",
"files_checked": [file1, file2], "files_checked": [file1, file2],
"relevant_files": [file1, file2], # Both files referenced "relevant_files": [file1, file2], # Both files referenced
"relevant_methods": ["process_data", "validate_input"], "relevant_context": ["process_data", "validate_input"],
"hypothesis": "Validation might be too strict", "hypothesis": "Validation might be too strict",
"confidence": "medium", "confidence": "medium",
"model": "flash", "model": "flash",
@@ -690,7 +690,7 @@ def validate_input(data):
"findings": "Root cause: validator is rejecting valid data due to strict type checking", "findings": "Root cause: validator is rejecting valid data due to strict type checking",
"files_checked": [file1, file2], "files_checked": [file1, file2],
"relevant_files": [file1, file2], # Should be fully embedded "relevant_files": [file1, file2], # Should be fully embedded
"relevant_methods": ["process_data", "validate_input"], "relevant_context": ["process_data", "validate_input"],
"hypothesis": "Validation logic is too restrictive for valid edge cases", "hypothesis": "Validation logic is too restrictive for valid edge cases",
"confidence": "high", "confidence": "high",
"model": "flash", "model": "flash",
@@ -797,7 +797,7 @@ class DatabaseServer:
"findings": "Application fails to start with configuration errors", "findings": "Application fails to start with configuration errors",
"files_checked": [config_file], "files_checked": [config_file],
"relevant_files": [config_file], "relevant_files": [config_file],
"relevant_methods": [], "relevant_context": [],
"hypothesis": "Configuration issue causing startup failure", "hypothesis": "Configuration issue causing startup failure",
"confidence": "low", "confidence": "low",
"model": "flash", "model": "flash",
@@ -831,7 +831,7 @@ class DatabaseServer:
"findings": "MAX_CONNECTIONS environment variable contains invalid value, causing CACHE_SIZE calculation to fail", "findings": "MAX_CONNECTIONS environment variable contains invalid value, causing CACHE_SIZE calculation to fail",
"files_checked": [config_file, server_file], "files_checked": [config_file, server_file],
"relevant_files": [config_file, server_file], "relevant_files": [config_file, server_file],
"relevant_methods": ["DatabaseServer.__init__"], "relevant_context": ["DatabaseServer.__init__"],
"hypothesis": "Invalid environment variable causing integer conversion error", "hypothesis": "Invalid environment variable causing integer conversion error",
"confidence": "medium", "confidence": "medium",
"model": "flash", "model": "flash",
@@ -871,7 +871,7 @@ class DatabaseServer:
"findings": "Error occurs in config.py line 8 when MAX_CONNECTIONS is not numeric, then propagates to DatabaseServer.__init__", "findings": "Error occurs in config.py line 8 when MAX_CONNECTIONS is not numeric, then propagates to DatabaseServer.__init__",
"files_checked": [config_file, server_file], "files_checked": [config_file, server_file],
"relevant_files": [config_file, server_file], "relevant_files": [config_file, server_file],
"relevant_methods": ["DatabaseServer.__init__"], "relevant_context": ["DatabaseServer.__init__"],
"hypothesis": "Need proper error handling and validation for environment variables", "hypothesis": "Need proper error handling and validation for environment variables",
"confidence": "high", "confidence": "high",
"model": "flash", "model": "flash",
@@ -905,7 +905,7 @@ class DatabaseServer:
"findings": "Root cause: config.py assumes MAX_CONNECTIONS env var is always a valid integer. Fix: add try/except with default value and proper validation.", "findings": "Root cause: config.py assumes MAX_CONNECTIONS env var is always a valid integer. Fix: add try/except with default value and proper validation.",
"files_checked": [config_file, server_file], "files_checked": [config_file, server_file],
"relevant_files": [config_file, server_file], "relevant_files": [config_file, server_file],
"relevant_methods": ["DatabaseServer.__init__"], "relevant_context": ["DatabaseServer.__init__"],
"hypothesis": "Environment variable validation needed with proper error handling", "hypothesis": "Environment variable validation needed with proper error handling",
"confidence": "high", "confidence": "high",
"model": "flash", "model": "flash",

View File

@@ -1,16 +0,0 @@
{
"database": {
"host": "localhost",
"port": 5432,
"name": "testdb",
"ssl": true
},
"cache": {
"redis_url": "redis://localhost:6379",
"ttl": 3600
},
"logging": {
"level": "INFO",
"format": "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
}
}

View File

@@ -1,32 +0,0 @@
"""
Sample Python module for testing MCP conversation continuity
"""
def fibonacci(n):
"""Calculate fibonacci number recursively"""
if n <= 1:
return n
return fibonacci(n-1) + fibonacci(n-2)
def factorial(n):
"""Calculate factorial iteratively"""
result = 1
for i in range(1, n + 1):
result *= i
return result
class Calculator:
"""Simple calculator class"""
def __init__(self):
self.history = []
def add(self, a, b):
result = a + b
self.history.append(f"{a} + {b} = {result}")
return result
def multiply(self, a, b):
result = a * b
self.history.append(f"{a} * {b} = {result}")
return result

View File

@@ -30,15 +30,14 @@ class TestDebugTool:
findings="Found potential null reference in user authentication flow", findings="Found potential null reference in user authentication flow",
files_checked=["/src/UserService.java"], files_checked=["/src/UserService.java"],
relevant_files=["/src/UserService.java"], relevant_files=["/src/UserService.java"],
relevant_methods=["authenticate", "validateUser"], relevant_context=["authenticate", "validateUser"],
confidence="medium", confidence="medium",
hypothesis="Null pointer occurs when user object is not properly validated", hypothesis="Null pointer occurs when user object is not properly validated",
) )
assert step_request.step_number == 1 assert step_request.step_number == 1
assert step_request.confidence == "medium" assert step_request.confidence == "medium"
assert len(step_request.relevant_methods) == 2 assert len(step_request.relevant_context) == 2
assert len(step_request.relevant_context) == 2 # Should be mapped from relevant_methods
def test_input_schema_generation(self): def test_input_schema_generation(self):
"""Test that input schema is generated correctly.""" """Test that input schema is generated correctly."""
@@ -51,33 +50,31 @@ class TestDebugTool:
assert "total_steps" in schema["properties"] assert "total_steps" in schema["properties"]
assert "next_step_required" in schema["properties"] assert "next_step_required" in schema["properties"]
assert "findings" in schema["properties"] assert "findings" in schema["properties"]
assert "relevant_methods" in schema["properties"] assert "relevant_context" in schema["properties"]
# Verify field types # Verify field types
assert schema["properties"]["step"]["type"] == "string" assert schema["properties"]["step"]["type"] == "string"
assert schema["properties"]["step_number"]["type"] == "integer" assert schema["properties"]["step_number"]["type"] == "integer"
assert schema["properties"]["next_step_required"]["type"] == "boolean" assert schema["properties"]["next_step_required"]["type"] == "boolean"
assert schema["properties"]["relevant_methods"]["type"] == "array" assert schema["properties"]["relevant_context"]["type"] == "array"
def test_model_category_for_debugging(self): def test_model_category_for_debugging(self):
"""Test that debug tool correctly identifies as extended reasoning category.""" """Test that debug tool correctly identifies as extended reasoning category."""
tool = DebugIssueTool() tool = DebugIssueTool()
assert tool.get_model_category() == ToolModelCategory.EXTENDED_REASONING assert tool.get_model_category() == ToolModelCategory.EXTENDED_REASONING
def test_field_mapping_relevant_methods_to_context(self): def test_relevant_context_handling(self):
"""Test that relevant_methods maps to relevant_context internally.""" """Test that relevant_context is handled correctly."""
request = DebugInvestigationRequest( request = DebugInvestigationRequest(
step="Test investigation", step="Test investigation",
step_number=1, step_number=1,
total_steps=2, total_steps=2,
next_step_required=True, next_step_required=True,
findings="Test findings", findings="Test findings",
relevant_methods=["method1", "method2"], relevant_context=["method1", "method2"],
) )
# External API should have relevant_methods # Should have relevant_context directly
assert request.relevant_methods == ["method1", "method2"]
# Internal processing should map to relevant_context
assert request.relevant_context == ["method1", "method2"] assert request.relevant_context == ["method1", "method2"]
# Test step data preparation # Test step data preparation

View File

@@ -19,7 +19,7 @@ class TestThinkDeepTool:
def test_tool_metadata(self, tool): def test_tool_metadata(self, tool):
"""Test tool metadata""" """Test tool metadata"""
assert tool.get_name() == "thinkdeep" assert tool.get_name() == "thinkdeep"
assert "EXTENDED THINKING" in tool.get_description() assert "COMPREHENSIVE INVESTIGATION & REASONING" in tool.get_description()
assert tool.get_default_temperature() == 0.7 assert tool.get_default_temperature() == 0.7
schema = tool.get_input_schema() schema = tool.get_input_schema()

View File

@@ -37,7 +37,8 @@ CODEREVIEW_WORKFLOW_FIELD_DESCRIPTIONS = {
"step": ( "step": (
"Describe what you're currently investigating for code review by thinking deeply about the code structure, " "Describe what you're currently investigating for code review by thinking deeply about the code structure, "
"patterns, and potential issues. In step 1, clearly state your review plan and begin forming a systematic " "patterns, and potential issues. In step 1, clearly state your review plan and begin forming a systematic "
"approach after thinking carefully about what needs to be analyzed. CRITICAL: Remember to thoroughly examine " "approach after thinking carefully about what needs to be analyzed. You must begin by passing the file path "
"for the initial code you are about to review in relevant_files. CRITICAL: Remember to thoroughly examine "
"code quality, security implications, performance concerns, and architectural patterns. Consider not only " "code quality, security implications, performance concerns, and architectural patterns. Consider not only "
"obvious bugs and issues but also subtle concerns like over-engineering, unnecessary complexity, design " "obvious bugs and issues but also subtle concerns like over-engineering, unnecessary complexity, design "
"patterns that could be simplified, areas where architecture might not scale well, missing abstractions, " "patterns that could be simplified, areas where architecture might not scale well, missing abstractions, "

View File

@@ -18,7 +18,7 @@ Key features:
import logging import logging
from typing import TYPE_CHECKING, Any, Optional from typing import TYPE_CHECKING, Any, Optional
from pydantic import Field, model_validator from pydantic import Field
if TYPE_CHECKING: if TYPE_CHECKING:
from tools.models import ToolModelCategory from tools.models import ToolModelCategory
@@ -127,9 +127,6 @@ class DebugInvestigationRequest(WorkflowRequest):
relevant_context: list[str] = Field( relevant_context: list[str] = Field(
default_factory=list, description=DEBUG_INVESTIGATION_FIELD_DESCRIPTIONS["relevant_context"] default_factory=list, description=DEBUG_INVESTIGATION_FIELD_DESCRIPTIONS["relevant_context"]
) )
relevant_methods: list[str] = Field(
default_factory=list, description=DEBUG_INVESTIGATION_FIELD_DESCRIPTIONS["relevant_context"], exclude=True
)
hypothesis: Optional[str] = Field(None, description=DEBUG_INVESTIGATION_FIELD_DESCRIPTIONS["hypothesis"]) hypothesis: Optional[str] = Field(None, description=DEBUG_INVESTIGATION_FIELD_DESCRIPTIONS["hypothesis"])
confidence: Optional[str] = Field("low", description=DEBUG_INVESTIGATION_FIELD_DESCRIPTIONS["confidence"]) confidence: Optional[str] = Field("low", description=DEBUG_INVESTIGATION_FIELD_DESCRIPTIONS["confidence"])
@@ -146,14 +143,6 @@ class DebugInvestigationRequest(WorkflowRequest):
thinking_mode: Optional[str] = Field(default=None, exclude=True) thinking_mode: Optional[str] = Field(default=None, exclude=True)
use_websearch: Optional[bool] = Field(default=None, exclude=True) use_websearch: Optional[bool] = Field(default=None, exclude=True)
@model_validator(mode="after")
def map_relevant_methods_to_context(self):
"""Map relevant_methods from external input to relevant_context for internal processing."""
# If relevant_context is empty but relevant_methods has values, use relevant_methods
if not self.relevant_context and self.relevant_methods:
self.relevant_context = self.relevant_methods[:]
return self
class DebugIssueTool(WorkflowTool): class DebugIssueTool(WorkflowTool):
""" """
@@ -261,11 +250,6 @@ class DebugIssueTool(WorkflowTool):
"minimum": 1, "minimum": 1,
"description": DEBUG_INVESTIGATION_FIELD_DESCRIPTIONS["backtrack_from_step"], "description": DEBUG_INVESTIGATION_FIELD_DESCRIPTIONS["backtrack_from_step"],
}, },
"relevant_methods": {
"type": "array",
"items": {"type": "string"},
"description": DEBUG_INVESTIGATION_FIELD_DESCRIPTIONS["relevant_context"],
},
"images": { "images": {
"type": "array", "type": "array",
"items": {"type": "string"}, "items": {"type": "string"},
@@ -350,7 +334,7 @@ class DebugIssueTool(WorkflowTool):
if error_context: if error_context:
context_parts.append(f"\n=== ERROR CONTEXT/STACK TRACE ===\n{error_context}\n=== END CONTEXT ===") context_parts.append(f"\n=== ERROR CONTEXT/STACK TRACE ===\n{error_context}\n=== END CONTEXT ===")
# Add relevant methods if available (map relevant_context back to relevant_methods) # Add relevant methods/functions if available
if consolidated_findings.relevant_context: if consolidated_findings.relevant_context:
methods_text = "\n".join(f"- {method}" for method in consolidated_findings.relevant_context) methods_text = "\n".join(f"- {method}" for method in consolidated_findings.relevant_context)
context_parts.append(f"\n=== RELEVANT METHODS/FUNCTIONS ===\n{methods_text}\n=== END METHODS ===") context_parts.append(f"\n=== RELEVANT METHODS/FUNCTIONS ===\n{methods_text}\n=== END METHODS ===")
@@ -466,7 +450,7 @@ class DebugIssueTool(WorkflowTool):
def prepare_step_data(self, request) -> dict: def prepare_step_data(self, request) -> dict:
""" """
Map debug-specific fields: relevant_methods -> relevant_context for internal processing. Prepare debug-specific step data for processing.
""" """
step_data = { step_data = {
"step": request.step, "step": request.step,
@@ -603,21 +587,12 @@ class DebugIssueTool(WorkflowTool):
# Rename status field to match debug tool # Rename status field to match debug tool
if f"{tool_name}_status" in response_data: if f"{tool_name}_status" in response_data:
response_data["investigation_status"] = response_data.pop(f"{tool_name}_status") response_data["investigation_status"] = response_data.pop(f"{tool_name}_status")
# Map relevant_context back to relevant_methods in status
if "relevant_context" in response_data["investigation_status"]:
response_data["investigation_status"]["relevant_methods"] = response_data["investigation_status"].pop(
"relevant_context"
)
# Add debug-specific status fields # Add debug-specific status fields
response_data["investigation_status"]["hypotheses_formed"] = len(self.consolidated_findings.hypotheses) response_data["investigation_status"]["hypotheses_formed"] = len(self.consolidated_findings.hypotheses)
# Map relevant_context back to relevant_methods in complete investigation # Rename complete investigation data
if f"complete_{tool_name}" in response_data: if f"complete_{tool_name}" in response_data:
response_data["complete_investigation"] = response_data.pop(f"complete_{tool_name}") response_data["complete_investigation"] = response_data.pop(f"complete_{tool_name}")
if "relevant_context" in response_data["complete_investigation"]:
response_data["complete_investigation"]["relevant_methods"] = response_data[
"complete_investigation"
].pop("relevant_context")
# Map the completion flag to match original debug tool # Map the completion flag to match original debug tool
if f"{tool_name}_complete" in response_data: if f"{tool_name}_complete" in response_data:

View File

@@ -139,14 +139,14 @@ class ThinkDeepTool(WorkflowTool):
name = "thinkdeep" name = "thinkdeep"
description = ( description = (
"EXTENDED THINKING & REASONING - Your deep thinking partner for complex problems. " "COMPREHENSIVE INVESTIGATION & REASONING - Multi-stage workflow for complex problem analysis. "
"Use this when you need to think deeper about a problem, extend your analysis, explore alternatives, " "Use this when you need structured evidence-based investigation, systematic hypothesis testing, or expert validation. "
"or validate approaches. Perfect for: architecture decisions, complex bugs, performance challenges, " "Perfect for: architecture decisions, complex bugs, performance challenges, security analysis. "
"security analysis. I'll challenge assumptions, find edge cases, and provide alternative solutions. " "Provides methodical investigation with assumption validation, alternative solution exploration, and rigorous analysis. "
"IMPORTANT: Choose the appropriate thinking_mode based on task complexity - 'low' for quick analysis, " "IMPORTANT: Choose the appropriate mode based on task complexity - 'low' for quick investigation, "
"'medium' for standard problems, 'high' for complex issues (default), 'max' for extremely complex " "'medium' for standard problems, 'high' for complex issues (default), 'max' for extremely complex "
"challenges requiring deepest analysis. When in doubt, err on the side of a higher mode for truly " "challenges requiring exhaustive investigation. When in doubt, err on the side of a higher mode for thorough "
"deep thought and evaluation. Note: If you're not currently using a top-tier model such as Opus 4 or above, " "systematic analysis and expert validation. Note: If you're not currently using a top-tier model such as Opus 4 or above, "
"these tools can provide enhanced capabilities." "these tools can provide enhanced capabilities."
) )
@@ -218,11 +218,21 @@ class ThinkDeepTool(WorkflowTool):
Customize the workflow response for thinkdeep-specific needs Customize the workflow response for thinkdeep-specific needs
""" """
# Store request parameters for later use in expert analysis # Store request parameters for later use in expert analysis
self.stored_request_params = { self.stored_request_params = {}
"temperature": getattr(request, "temperature", None), try:
"thinking_mode": getattr(request, "thinking_mode", None), self.stored_request_params["temperature"] = request.temperature
"use_websearch": getattr(request, "use_websearch", None), except AttributeError:
} self.stored_request_params["temperature"] = None
try:
self.stored_request_params["thinking_mode"] = request.thinking_mode
except AttributeError:
self.stored_request_params["thinking_mode"] = None
try:
self.stored_request_params["use_websearch"] = request.use_websearch
except AttributeError:
self.stored_request_params["use_websearch"] = None
# Add thinking-specific context to response # Add thinking-specific context to response
response_data.update( response_data.update(
@@ -307,8 +317,8 @@ Your role is to validate the thinking process, identify any gaps, challenge assu
additional insights or alternative perspectives. additional insights or alternative perspectives.
ANALYSIS SCOPE: ANALYSIS SCOPE:
- Problem Context: {getattr(request, 'problem_context', 'General analysis')} - Problem Context: {self._get_problem_context(request)}
- Focus Areas: {', '.join(getattr(request, 'focus_areas', ['comprehensive analysis']))} - Focus Areas: {', '.join(self._get_focus_areas(request))}
- Investigation Confidence: {request.confidence} - Investigation Confidence: {request.confidence}
- Steps Completed: {request.step_number} of {request.total_steps} - Steps Completed: {request.step_number} of {request.total_steps}
@@ -350,22 +360,48 @@ but also acknowledge strong insights and valid conclusions.
def get_request_temperature(self, request) -> float: def get_request_temperature(self, request) -> float:
"""Use stored temperature from initial request.""" """Use stored temperature from initial request."""
if hasattr(self, "stored_request_params") and self.stored_request_params.get("temperature") is not None: try:
return self.stored_request_params["temperature"] stored_params = self.stored_request_params
if stored_params and stored_params.get("temperature") is not None:
return stored_params["temperature"]
except AttributeError:
pass
return super().get_request_temperature(request) return super().get_request_temperature(request)
def get_request_thinking_mode(self, request) -> str: def get_request_thinking_mode(self, request) -> str:
"""Use stored thinking mode from initial request.""" """Use stored thinking mode from initial request."""
if hasattr(self, "stored_request_params") and self.stored_request_params.get("thinking_mode") is not None: try:
return self.stored_request_params["thinking_mode"] stored_params = self.stored_request_params
if stored_params and stored_params.get("thinking_mode") is not None:
return stored_params["thinking_mode"]
except AttributeError:
pass
return super().get_request_thinking_mode(request) return super().get_request_thinking_mode(request)
def get_request_use_websearch(self, request) -> bool: def get_request_use_websearch(self, request) -> bool:
"""Use stored use_websearch from initial request.""" """Use stored use_websearch from initial request."""
if hasattr(self, "stored_request_params") and self.stored_request_params.get("use_websearch") is not None: try:
return self.stored_request_params["use_websearch"] stored_params = self.stored_request_params
if stored_params and stored_params.get("use_websearch") is not None:
return stored_params["use_websearch"]
except AttributeError:
pass
return super().get_request_use_websearch(request) return super().get_request_use_websearch(request)
def _get_problem_context(self, request) -> str:
"""Get problem context from request. Override for custom context handling."""
try:
return request.problem_context or "General analysis"
except AttributeError:
return "General analysis"
def _get_focus_areas(self, request) -> list[str]:
"""Get focus areas from request. Override for custom focus area handling."""
try:
return request.focus_areas or ["comprehensive analysis"]
except AttributeError:
return ["comprehensive analysis"]
def get_required_actions(self, step_number: int, confidence: str, findings: str, total_steps: int) -> list[str]: def get_required_actions(self, step_number: int, confidence: str, findings: str, total_steps: int) -> list[str]:
""" """
Return required actions for the current thinking step. Return required actions for the current thinking step.
@@ -413,14 +449,20 @@ but also acknowledge strong insights and valid conclusions.
""" """
Determine if expert analysis should be called based on confidence and completion. Determine if expert analysis should be called based on confidence and completion.
""" """
if request and hasattr(request, "confidence"): if request:
try:
# Don't call expert analysis if confidence is "certain" # Don't call expert analysis if confidence is "certain"
if request.confidence == "certain": if request.confidence == "certain":
return False return False
except AttributeError:
pass
# Call expert analysis if investigation is complete (when next_step_required is False) # Call expert analysis if investigation is complete (when next_step_required is False)
if request and hasattr(request, "next_step_required"): if request:
try:
return not request.next_step_required return not request.next_step_required
except AttributeError:
pass
# Fallback: call expert analysis if we have meaningful findings # Fallback: call expert analysis if we have meaningful findings
return ( return (

View File

@@ -677,8 +677,6 @@ class BaseWorkflowMixin(ABC):
def prepare_step_data(self, request) -> dict: def prepare_step_data(self, request) -> dict:
""" """
Prepare step data from request. Tools can override to customize field mapping. Prepare step data from request. Tools can override to customize field mapping.
For example, debug tool maps relevant_methods to relevant_context.
""" """
step_data = { step_data = {
"step": request.step, "step": request.step,