Files
my-pal-mcp-server/tools/debug.py
2025-06-19 17:49:39 +04:00

725 lines
38 KiB
Python

"""
Debug Issue tool - Root cause analysis and debugging assistance with systematic investigation
"""
import json
import logging
from typing import TYPE_CHECKING, Any, Optional
from pydantic import Field
if TYPE_CHECKING:
from tools.models import ToolModelCategory
from config import TEMPERATURE_ANALYTICAL
from systemprompts import DEBUG_ISSUE_PROMPT
from .base import BaseTool, ToolRequest
logger = logging.getLogger(__name__)
# Field descriptions for the investigation steps
DEBUG_INVESTIGATION_FIELD_DESCRIPTIONS = {
"step": (
"Describe what you're currently investigating by thinking deeply about the issue and its possible causes. "
"In step 1, clearly state the issue and begin forming an investigative direction. CRITICAL: Remember that "
"reported symptoms might originate from code far from where they manifest. Also be aware that after thorough "
"investigation, you might find NO BUG EXISTS - it could be a misunderstanding or expectation mismatch. "
"Consider not only obvious failures, but also subtle contributing factors like upstream logic, invalid inputs, "
"missing preconditions, or hidden side effects. Map out the flow of related functions or modules. Identify "
"call paths where input values or branching logic could cause instability. In concurrent systems, watch for "
"race conditions, shared state, or timing dependencies. In all later steps, continue exploring with precision: "
"trace deeper dependencies, verify hypotheses, and adapt your understanding as you uncover more evidence."
),
"step_number": (
"The index of the current step in the investigation sequence, beginning at 1. Each step should build upon or "
"revise the previous one."
),
"total_steps": (
"Your current estimate for how many steps will be needed to complete the investigation. Adjust as new findings emerge."
),
"next_step_required": (
"Set to true if you plan to continue the investigation with another step. False means you believe the root "
"cause is known or the investigation is complete."
),
"findings": (
"Summarize everything discovered in this step. Include new clues, unexpected behavior, evidence from code or "
"logs, or disproven theories. Be specific and avoid vague language—document what you now know and how it "
"affects your hypothesis. IMPORTANT: If you find no evidence supporting the reported issue after thorough "
"investigation, document this clearly. Finding 'no bug' is a valid outcome if the investigation was comprehensive. "
"In later steps, confirm or disprove past findings with reason."
),
"files_checked": (
"List all files (as absolute paths, do not clip or shrink file names) examined during the investigation so far. "
"Include even files ruled out, as this tracks your exploration path."
),
"relevant_files": (
"Subset of files_checked (as full absolute paths) that contain code directly relevant to the issue. Only list "
"those that are directly tied to the root cause or its effects. This could include the cause, trigger, or "
"place of manifestation."
),
"relevant_methods": (
"List methods or functions that are central to the issue, in the format 'ClassName.methodName' or 'functionName'. "
"Prioritize those that influence or process inputs, drive branching, or pass state between modules."
),
"hypothesis": (
"A concrete theory for what's causing the issue based on the evidence so far. This can include suspected "
"failures, incorrect assumptions, or violated constraints. VALID HYPOTHESES INCLUDE: 'No bug found - possible "
"user misunderstanding' or 'Symptoms appear unrelated to any code issue' if evidence supports this. When "
"no bug is found, consider suggesting: 'Recommend discussing with thought partner/engineering assistant for "
"clarification of expected behavior.' You are encouraged to revise or abandon hypotheses in later steps as "
"needed based on evidence."
),
"confidence": (
"Indicate your current confidence in the hypothesis. Use: 'exploring' (starting out), 'low' (early idea), "
"'medium' (some supporting evidence), 'high' (strong evidence), 'certain' (only when the root cause and minimal "
"fix are both confirmed). Do NOT use 'certain' unless the issue can be fully resolved with a fix, use 'high' "
"instead when in doubt. Using 'certain' prevents you from taking assistance from another thought-partner."
),
"backtrack_from_step": (
"If an earlier finding or hypothesis needs to be revised or discarded, specify the step number from which to "
"start over. Use this to acknowledge investigative dead ends and correct the course."
),
"continuation_id": "Continuation token used for linking multi-step investigations and continuing conversations after discovery.",
"images": (
"Optional list of absolute paths to screenshots or UI visuals that clarify the issue. "
"Only include if they materially assist understanding or hypothesis formulation."
),
}
DEBUG_FIELD_DESCRIPTIONS = {
"initial_issue": "Describe the original problem that triggered the investigation.",
"investigation_summary": (
"Full overview of the systematic investigation process. Reflect deep thinking and each step's contribution to narrowing down the issue."
),
"findings": "Final list of critical insights and discoveries across all steps.",
"files": "Essential files referenced during investigation (must be full absolute paths).",
"error_context": "Logs, tracebacks, or execution details that support the root cause hypothesis.",
"relevant_methods": "List of all methods/functions identified as directly involved.",
"hypothesis": "Final, most likely explanation of the root cause based on evidence.",
"images": "Optional screenshots or visual materials that helped diagnose the issue.",
}
class DebugInvestigationRequest(ToolRequest):
"""Request model for debug investigation steps"""
# Required fields for each investigation step
step: str = Field(..., description=DEBUG_INVESTIGATION_FIELD_DESCRIPTIONS["step"])
step_number: int = Field(..., description=DEBUG_INVESTIGATION_FIELD_DESCRIPTIONS["step_number"])
total_steps: int = Field(..., description=DEBUG_INVESTIGATION_FIELD_DESCRIPTIONS["total_steps"])
next_step_required: bool = Field(..., description=DEBUG_INVESTIGATION_FIELD_DESCRIPTIONS["next_step_required"])
# Investigation tracking fields
findings: str = Field(..., description=DEBUG_INVESTIGATION_FIELD_DESCRIPTIONS["findings"])
files_checked: list[str] = Field(
default_factory=list, description=DEBUG_INVESTIGATION_FIELD_DESCRIPTIONS["files_checked"]
)
relevant_files: list[str] = Field(
default_factory=list, description=DEBUG_INVESTIGATION_FIELD_DESCRIPTIONS["relevant_files"]
)
relevant_methods: list[str] = Field(
default_factory=list, description=DEBUG_INVESTIGATION_FIELD_DESCRIPTIONS["relevant_methods"]
)
hypothesis: Optional[str] = Field(None, description=DEBUG_INVESTIGATION_FIELD_DESCRIPTIONS["hypothesis"])
confidence: Optional[str] = Field("low", description=DEBUG_INVESTIGATION_FIELD_DESCRIPTIONS["confidence"])
# Optional backtracking field
backtrack_from_step: Optional[int] = Field(
None, description=DEBUG_INVESTIGATION_FIELD_DESCRIPTIONS["backtrack_from_step"]
)
# Optional continuation field
continuation_id: Optional[str] = Field(None, description=DEBUG_INVESTIGATION_FIELD_DESCRIPTIONS["continuation_id"])
# Optional images for visual debugging
images: Optional[list[str]] = Field(default=None, description=DEBUG_INVESTIGATION_FIELD_DESCRIPTIONS["images"])
# Override inherited fields to exclude them from schema (except model which needs to be available)
temperature: Optional[float] = Field(default=None, exclude=True)
thinking_mode: Optional[str] = Field(default=None, exclude=True)
use_websearch: Optional[bool] = Field(default=None, exclude=True)
class DebugIssueTool(BaseTool):
"""Advanced debugging tool with systematic self-investigation"""
def __init__(self):
super().__init__()
self.investigation_history = []
self.consolidated_findings = {
"files_checked": set(),
"relevant_files": set(),
"relevant_methods": set(),
"findings": [],
"hypotheses": [],
"images": [],
}
def get_name(self) -> str:
return "debug"
def get_description(self) -> str:
return (
"DEBUG & ROOT CAUSE ANALYSIS - Systematic self-investigation followed by expert analysis. "
"This tool guides you through a step-by-step investigation process where you:\n\n"
"1. Start with step 1: describe the issue to investigate\n"
"2. STOP and investigate using appropriate tools\n"
"3. Report findings in step 2 with concrete evidence from actual code\n"
"4. Continue investigating between each debug step\n"
"5. Track findings, relevant files, and methods throughout\n"
"6. Update hypotheses as understanding evolves\n"
"7. Once investigation is complete, receive expert analysis\n\n"
"IMPORTANT: This tool enforces investigation between steps:\n"
"- After each debug call, you MUST investigate before calling debug again\n"
"- Each step must include NEW evidence from code examination\n"
"- No recursive debug calls without actual investigation work\n"
"- The tool will specify which step number to use next\n"
"- Follow the required_actions list for investigation guidance\n\n"
"Perfect for: complex bugs, mysterious errors, performance issues, "
"race conditions, memory leaks, integration problems."
)
def get_input_schema(self) -> dict[str, Any]:
schema = {
"type": "object",
"properties": {
# Investigation step fields
"step": {
"type": "string",
"description": DEBUG_INVESTIGATION_FIELD_DESCRIPTIONS["step"],
},
"step_number": {
"type": "integer",
"description": DEBUG_INVESTIGATION_FIELD_DESCRIPTIONS["step_number"],
"minimum": 1,
},
"total_steps": {
"type": "integer",
"description": DEBUG_INVESTIGATION_FIELD_DESCRIPTIONS["total_steps"],
"minimum": 1,
},
"next_step_required": {
"type": "boolean",
"description": DEBUG_INVESTIGATION_FIELD_DESCRIPTIONS["next_step_required"],
},
"findings": {
"type": "string",
"description": DEBUG_INVESTIGATION_FIELD_DESCRIPTIONS["findings"],
},
"files_checked": {
"type": "array",
"items": {"type": "string"},
"description": DEBUG_INVESTIGATION_FIELD_DESCRIPTIONS["files_checked"],
},
"relevant_files": {
"type": "array",
"items": {"type": "string"},
"description": DEBUG_INVESTIGATION_FIELD_DESCRIPTIONS["relevant_files"],
},
"relevant_methods": {
"type": "array",
"items": {"type": "string"},
"description": DEBUG_INVESTIGATION_FIELD_DESCRIPTIONS["relevant_methods"],
},
"hypothesis": {
"type": "string",
"description": DEBUG_INVESTIGATION_FIELD_DESCRIPTIONS["hypothesis"],
},
"confidence": {
"type": "string",
"enum": ["exploring", "low", "medium", "high", "certain"],
"description": DEBUG_INVESTIGATION_FIELD_DESCRIPTIONS["confidence"],
},
"backtrack_from_step": {
"type": "integer",
"description": DEBUG_INVESTIGATION_FIELD_DESCRIPTIONS["backtrack_from_step"],
"minimum": 1,
},
"continuation_id": {
"type": "string",
"description": DEBUG_INVESTIGATION_FIELD_DESCRIPTIONS["continuation_id"],
},
"images": {
"type": "array",
"items": {"type": "string"},
"description": DEBUG_INVESTIGATION_FIELD_DESCRIPTIONS["images"],
},
# Add model field for proper model selection
"model": self.get_model_field_schema(),
},
# Required fields for investigation
"required": ["step", "step_number", "total_steps", "next_step_required", "findings"]
+ (["model"] if self.is_effective_auto_mode() else []),
}
return schema
def get_system_prompt(self) -> str:
return DEBUG_ISSUE_PROMPT
def get_default_temperature(self) -> float:
return TEMPERATURE_ANALYTICAL
def get_model_category(self) -> "ToolModelCategory":
"""Debug requires deep analysis and reasoning"""
from tools.models import ToolModelCategory
return ToolModelCategory.EXTENDED_REASONING
def get_request_model(self):
return DebugInvestigationRequest
def requires_model(self) -> bool:
"""
Debug tool requires a model for expert analysis after investigation.
"""
return True
async def execute(self, arguments: dict[str, Any]) -> list:
"""
Override execute to implement self-investigation pattern.
Investigation Flow:
1. Claude calls debug with investigation steps
2. Tool tracks findings, files, methods progressively
3. Once investigation is complete, tool calls AI model for expert analysis
4. Returns structured response combining investigation + expert analysis
"""
from mcp.types import TextContent
from utils.conversation_memory import add_turn, create_thread
try:
# Validate request
request = DebugInvestigationRequest(**arguments)
# Adjust total steps if needed
if request.step_number > request.total_steps:
request.total_steps = request.step_number
# Handle continuation
continuation_id = request.continuation_id
# Create thread for first step
if not continuation_id and request.step_number == 1:
# Clean arguments to remove non-serializable fields
clean_args = {k: v for k, v in arguments.items() if k not in ["_model_context", "_resolved_model_name"]}
continuation_id = create_thread("debug", clean_args)
# Store initial issue description
self.initial_issue = request.step
# Handle backtracking first if requested
if request.backtrack_from_step:
# Remove findings after the backtrack point
self.investigation_history = [
s for s in self.investigation_history if s["step_number"] < request.backtrack_from_step
]
# Reprocess consolidated findings to match truncated history
self._reprocess_consolidated_findings()
# Log if step number needs correction
expected_step_number = len(self.investigation_history) + 1
if request.step_number != expected_step_number:
logger.debug(
f"Step number adjusted from {request.step_number} to {expected_step_number} after backtracking"
)
# Process investigation step
step_data = {
"step": request.step,
"step_number": request.step_number,
"findings": request.findings,
"files_checked": request.files_checked,
"relevant_files": request.relevant_files,
"relevant_methods": request.relevant_methods,
"hypothesis": request.hypothesis,
"confidence": request.confidence,
"images": request.images,
}
# Store in history
self.investigation_history.append(step_data)
# Update consolidated findings
self.consolidated_findings["files_checked"].update(request.files_checked)
self.consolidated_findings["relevant_files"].update(request.relevant_files)
self.consolidated_findings["relevant_methods"].update(request.relevant_methods)
self.consolidated_findings["findings"].append(f"Step {request.step_number}: {request.findings}")
if request.hypothesis:
self.consolidated_findings["hypotheses"].append(
{"step": request.step_number, "hypothesis": request.hypothesis, "confidence": request.confidence}
)
if request.images:
self.consolidated_findings["images"].extend(request.images)
# Build response
response_data = {
"status": "investigation_in_progress",
"step_number": request.step_number,
"total_steps": request.total_steps,
"next_step_required": request.next_step_required,
"investigation_status": {
"files_checked": len(self.consolidated_findings["files_checked"]),
"relevant_files": len(self.consolidated_findings["relevant_files"]),
"relevant_methods": len(self.consolidated_findings["relevant_methods"]),
"hypotheses_formed": len(self.consolidated_findings["hypotheses"]),
"images_collected": len(set(self.consolidated_findings["images"])),
"current_confidence": request.confidence,
},
}
if continuation_id:
response_data["continuation_id"] = continuation_id
# If investigation is complete, decide whether to call expert analysis or proceed with minimal fix
if not request.next_step_required:
response_data["investigation_complete"] = True
# Check if Claude has absolute certainty and can proceed with minimal fix
if request.confidence == "certain":
# Trust Claude's judgment completely - if it says certain, skip expert analysis
response_data["status"] = "certain_confidence_proceed_with_fix"
investigation_summary = self._prepare_investigation_summary()
response_data["complete_investigation"] = {
"initial_issue": getattr(self, "initial_issue", request.step),
"steps_taken": len(self.investigation_history),
"files_examined": list(self.consolidated_findings["files_checked"]),
"relevant_files": list(self.consolidated_findings["relevant_files"]),
"relevant_methods": list(self.consolidated_findings["relevant_methods"]),
"investigation_summary": investigation_summary,
"final_hypothesis": request.hypothesis,
"confidence_level": "certain",
}
response_data["next_steps"] = (
"Investigation complete with CERTAIN confidence. You have identified the exact "
"root cause and a minimal fix. MANDATORY: Present the user with the root cause analysis"
"and IMMEDIATELY proceed with implementing the simple fix without requiring further "
"consultation. Focus on the precise, minimal change needed."
)
response_data["skip_expert_analysis"] = True
response_data["expert_analysis"] = {
"status": "skipped_due_to_certain_confidence",
"reason": "Claude identified exact root cause with minimal fix requirement",
}
else:
# Standard expert analysis for certain/high/medium/low/exploring confidence
response_data["status"] = "calling_expert_analysis"
# Prepare consolidated investigation summary
investigation_summary = self._prepare_investigation_summary()
# Call the AI model with full context
expert_analysis = await self._call_expert_analysis(
initial_issue=getattr(self, "initial_issue", request.step),
investigation_summary=investigation_summary,
relevant_files=list(self.consolidated_findings["relevant_files"]),
relevant_methods=list(self.consolidated_findings["relevant_methods"]),
final_hypothesis=request.hypothesis,
error_context=self._extract_error_context(),
images=list(set(self.consolidated_findings["images"])), # Unique images
model_info=arguments.get("_model_context"), # Use pre-resolved model context from server.py
arguments=arguments, # Pass arguments for model resolution
request=request, # Pass request for model resolution
)
# Combine investigation and expert analysis
response_data["expert_analysis"] = expert_analysis
response_data["complete_investigation"] = {
"initial_issue": getattr(self, "initial_issue", request.step),
"steps_taken": len(self.investigation_history),
"files_examined": list(self.consolidated_findings["files_checked"]),
"relevant_files": list(self.consolidated_findings["relevant_files"]),
"relevant_methods": list(self.consolidated_findings["relevant_methods"]),
"investigation_summary": investigation_summary,
}
response_data["next_steps"] = (
"INVESTIGATION IS COMPLETE. YOU MUST now summarize and present ALL key findings, confirmed "
"hypotheses, and exact recommended fixes. Clearly identify the most likely root cause and "
"provide concrete, actionable implementation guidance. Highlight affected code paths and display "
"reasoning that led to this conclusion—make it easy for a developer to understand exactly where "
"the problem lies."
)
else:
# CRITICAL: Force Claude to actually investigate before calling debug again
response_data["status"] = "pause_for_investigation"
response_data["investigation_required"] = True
if request.step_number == 1:
# Initial investigation tasks
response_data["required_actions"] = [
"Search for code related to the reported issue or symptoms",
"Examine relevant files and understand the current implementation",
"Understand the project structure and locate relevant modules",
"Identify how the affected functionality is supposed to work",
]
response_data["next_steps"] = (
f"MANDATORY: DO NOT call the debug tool again immediately. You MUST first investigate "
f"the codebase using appropriate tools. CRITICAL AWARENESS: The reported symptoms might be "
f"caused by issues elsewhere in the code, not where symptoms appear. Also, after thorough "
f"investigation, it's possible NO BUG EXISTS - the issue might be a misunderstanding or "
f"user expectation mismatch. Search broadly, examine implementations, understand the logic flow. "
f"Only call debug again AFTER gathering concrete evidence. When you call debug next time, "
f"use step_number: {request.step_number + 1} and report specific files examined and findings discovered."
)
elif request.step_number >= 2 and request.confidence in ["exploring", "low"]:
# Need deeper investigation
response_data["required_actions"] = [
"Examine the specific files you've identified as relevant",
"Trace method calls and data flow through the system",
"Check for edge cases, boundary conditions, and assumptions in the code",
"Look for related configuration, dependencies, or external factors",
]
response_data["next_steps"] = (
f"STOP! Do NOT call debug again yet. Based on your findings, you've identified potential areas "
f"but need concrete evidence. MANDATORY ACTIONS before calling debug step {request.step_number + 1}:\n"
f"1. Examine ALL files in your relevant_files list\n"
f"2. Trace how data flows through {', '.join(request.relevant_methods[:3]) if request.relevant_methods else 'the identified components'}\n"
f"3. Look for logic errors, incorrect assumptions, missing validations\n"
f"4. Check interactions between components and external dependencies\n"
f"Only call debug again with step_number: {request.step_number + 1} AFTER completing these investigations."
)
elif request.confidence in ["medium", "high"]:
# Close to root cause - need confirmation
response_data["required_actions"] = [
"Examine the exact code sections where you believe the issue occurs",
"Trace the execution path that leads to the failure",
"Verify your hypothesis with concrete code evidence",
"Check for any similar patterns elsewhere in the codebase",
]
response_data["next_steps"] = (
f"WAIT! Your hypothesis needs verification. DO NOT call debug immediately. REQUIRED ACTIONS:\n"
f"1. Examine the exact lines where the issue occurs\n"
f"2. Trace backwards: how does data get to this point? What transforms it?\n"
f"3. Check all assumptions: are inputs validated? Are nulls handled?\n"
f"4. Look for the EXACT line where expected != actual behavior\n"
f"REMEMBER: If you cannot find concrete evidence of a bug causing the reported symptoms, "
f"'no bug found' is a valid conclusion. Consider suggesting discussion with your thought partner "
f"or engineering assistant for clarification. Document findings with specific file:line references, "
f"then call debug with step_number: {request.step_number + 1}."
)
else:
# General investigation needed
response_data["required_actions"] = [
"Continue examining the code paths identified in your hypothesis",
"Gather more evidence using appropriate investigation tools",
"Test edge cases and boundary conditions",
"Look for patterns that confirm or refute your theory",
]
response_data["next_steps"] = (
f"PAUSE INVESTIGATION. Before calling debug step {request.step_number + 1}, you MUST examine code. "
f"Required: Read files from your files_checked list, search for patterns in your hypothesis, "
f"trace execution flow. Your next debug call (step_number: {request.step_number + 1}) must include "
f"NEW evidence from actual code examination, not just theories. If no bug evidence is found, suggesting "
f"collaboration with thought partner is valuable. NO recursive debug calls without investigation work!"
)
# Store in conversation memory
if continuation_id:
add_turn(
thread_id=continuation_id,
role="assistant",
content=json.dumps(response_data, indent=2),
tool_name="debug",
files=list(self.consolidated_findings["relevant_files"]),
images=request.images,
)
return [TextContent(type="text", text=json.dumps(response_data, indent=2))]
except Exception as e:
logger.error(f"Error in debug investigation: {e}", exc_info=True)
error_data = {
"status": "investigation_failed",
"error": str(e),
"step_number": arguments.get("step_number", 0),
}
return [TextContent(type="text", text=json.dumps(error_data, indent=2))]
def _reprocess_consolidated_findings(self):
"""Reprocess consolidated findings after backtracking"""
self.consolidated_findings = {
"files_checked": set(),
"relevant_files": set(),
"relevant_methods": set(),
"findings": [],
"hypotheses": [],
"images": [],
}
for step in self.investigation_history:
self.consolidated_findings["files_checked"].update(step.get("files_checked", []))
self.consolidated_findings["relevant_files"].update(step.get("relevant_files", []))
self.consolidated_findings["relevant_methods"].update(step.get("relevant_methods", []))
self.consolidated_findings["findings"].append(f"Step {step['step_number']}: {step['findings']}")
if step.get("hypothesis"):
self.consolidated_findings["hypotheses"].append(
{
"step": step["step_number"],
"hypothesis": step["hypothesis"],
"confidence": step.get("confidence", "low"),
}
)
if step.get("images"):
self.consolidated_findings["images"].extend(step["images"])
def _prepare_investigation_summary(self) -> str:
"""Prepare a comprehensive summary of the investigation"""
summary_parts = [
"=== SYSTEMATIC INVESTIGATION SUMMARY ===",
f"Total steps: {len(self.investigation_history)}",
f"Files examined: {len(self.consolidated_findings['files_checked'])}",
f"Relevant files identified: {len(self.consolidated_findings['relevant_files'])}",
f"Methods/functions involved: {len(self.consolidated_findings['relevant_methods'])}",
"",
"=== INVESTIGATION PROGRESSION ===",
]
for finding in self.consolidated_findings["findings"]:
summary_parts.append(finding)
if self.consolidated_findings["hypotheses"]:
summary_parts.extend(
[
"",
"=== HYPOTHESIS EVOLUTION ===",
]
)
for hyp in self.consolidated_findings["hypotheses"]:
summary_parts.append(f"Step {hyp['step']} ({hyp['confidence']} confidence): {hyp['hypothesis']}")
return "\n".join(summary_parts)
def _extract_error_context(self) -> Optional[str]:
"""Extract error context from investigation findings"""
error_patterns = ["error", "exception", "stack trace", "traceback", "failure"]
error_context_parts = []
for finding in self.consolidated_findings["findings"]:
if any(pattern in finding.lower() for pattern in error_patterns):
error_context_parts.append(finding)
return "\n".join(error_context_parts) if error_context_parts else None
async def _call_expert_analysis(
self,
initial_issue: str,
investigation_summary: str,
relevant_files: list[str],
relevant_methods: list[str],
final_hypothesis: Optional[str],
error_context: Optional[str],
images: list[str],
model_info: Optional[Any] = None,
arguments: Optional[dict] = None,
request: Optional[Any] = None,
) -> dict:
"""Call AI model for expert analysis of the investigation"""
# Set up model context when we actually need it for expert analysis
# Use the same model resolution logic as the base class
if model_info:
# Use pre-resolved model context from server.py (normal case)
self._model_context = model_info
model_name = model_info.model_name
else:
# Use centralized model resolution from base class
if arguments and request:
try:
model_name, model_context = self._resolve_model_context(arguments, request)
self._model_context = model_context
except ValueError as e:
# Model resolution failed, return error
return {"error": f"Model resolution failed: {str(e)}", "status": "model_resolution_error"}
else:
# Last resort fallback if no arguments/request provided
from config import DEFAULT_MODEL
from utils.model_context import ModelContext
model_name = DEFAULT_MODEL
self._model_context = ModelContext(model_name)
# Store model name for use by other methods
self._current_model_name = model_name
provider = self.get_model_provider(model_name)
# Prepare the debug prompt with all investigation context
prompt_parts = [
f"=== ISSUE DESCRIPTION ===\n{initial_issue}\n=== END DESCRIPTION ===",
f"\n=== CLAUDE'S INVESTIGATION FINDINGS ===\n{investigation_summary}\n=== END FINDINGS ===",
]
if error_context:
prompt_parts.append(f"\n=== ERROR CONTEXT/STACK TRACE ===\n{error_context}\n=== END CONTEXT ===")
if relevant_methods:
prompt_parts.append(
"\n=== RELEVANT METHODS/FUNCTIONS ===\n"
+ "\n".join(f"- {method}" for method in relevant_methods)
+ "\n=== END METHODS ==="
)
if final_hypothesis:
prompt_parts.append(f"\n=== FINAL HYPOTHESIS ===\n{final_hypothesis}\n=== END HYPOTHESIS ===")
if images:
prompt_parts.append(
"\n=== VISUAL DEBUGGING INFORMATION ===\n"
+ "\n".join(f"- {img}" for img in images)
+ "\n=== END VISUAL INFORMATION ==="
)
# Add file content if we have relevant files
if relevant_files:
file_content, _ = self._prepare_file_content_for_prompt(relevant_files, None, "Essential debugging files")
if file_content:
prompt_parts.append(
f"\n=== ESSENTIAL FILES FOR DEBUGGING ===\n{file_content}\n=== END ESSENTIAL FILES ==="
)
full_prompt = "\n".join(prompt_parts)
# Generate AI response
try:
full_analysis_prompt = f"{self.get_system_prompt()}\n\n{full_prompt}\n\nPlease debug this issue following the structured format in the system prompt."
# Prepare generation kwargs
generation_kwargs = {
"prompt": full_analysis_prompt,
"model_name": model_name,
"system_prompt": "", # Already included in prompt
"temperature": self.get_default_temperature(),
"thinking_mode": "high", # High thinking for debug analysis
}
# Add images if available
if images:
generation_kwargs["images"] = images
model_response = provider.generate_content(**generation_kwargs)
if model_response.content:
# Try to parse as JSON
try:
analysis_result = json.loads(model_response.content.strip())
return analysis_result
except json.JSONDecodeError:
# Return as text if not valid JSON
return {
"status": "analysis_complete",
"raw_analysis": model_response.content,
"parse_error": "Response was not valid JSON",
}
else:
return {"error": "No response from model", "status": "empty_response"}
except Exception as e:
logger.error(f"Error calling expert analysis: {e}", exc_info=True)
return {"error": str(e), "status": "analysis_error"}
# Stub implementations for base class requirements
async def prepare_prompt(self, request) -> str:
return "" # Not used - execute() is overridden
def format_response(self, response: str, request, model_info: dict = None) -> str:
return response # Not used - execute() is overridden