Skip to content

Commit 91d72b4

Browse files
OBennerclaudeTest User
authored
Performance Profiling Agent (#63)
* auto-claude: subtask-1-1 - Create performance profiler analysis module * auto-claude: subtask-2-1 - Create performance profiler prompt * auto-claude: subtask-2-2 - Add performance_profiler to AGENT_CONFIGS * auto-claude: subtask-2-3 - Create performance profiler agent module - Added run_performance_profiler function in agents/performance_profiler.py - Added get_performance_profiler_prompt function in prompts_pkg/prompts.py - Exported new function in prompts_pkg/__init__.py - Follows patterns from planner.py and coder.py - Verification passes successfully Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> * auto-claude: subtask-3-1 - Export performance profiler from agents module * fix: address SonarCloud unused variable warnings - Replace unused `response` with `_` in agents/performance_profiler.py - Replace unused `line` with `_line` in analysis/performance_profiler.py - Third SonarCloud issue (collapsible if) already resolved by develop merge Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: add explicit import for run_performance_profiler (CodeQL) CodeQL flagged py/undefined-export because run_performance_profiler was in __all__ but only defined via __getattr__ lazy import. Add explicit re-export to match the pattern used by other agents. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com> Co-authored-by: Test User <test@example.com>
1 parent 89f4492 commit 91d72b4

7 files changed

Lines changed: 1693 additions & 0 deletions

File tree

apps/backend/agents/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
from .memory_manager import get_graphiti_context as get_graphiti_context
2828
from .memory_manager import save_session_memory as save_session_memory
2929
from .memory_manager import save_session_to_graphiti as save_session_to_graphiti
30+
from .performance_profiler import run_performance_profiler as run_performance_profiler
3031
from .planner import run_followup_planner as run_followup_planner
3132
from .session import post_session_processing as post_session_processing
3233
from .session import run_agent_session as run_agent_session
@@ -43,6 +44,7 @@
4344
# Main API
4445
"run_autonomous_agent",
4546
"run_followup_planner",
47+
"run_performance_profiler",
4648
"run_code_review_session",
4749
"run_documentation_generator_session",
4850
# Memory
Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
"""
2+
Performance Profiler Agent Module
3+
==================================
4+
5+
Specialized agent for analyzing and optimizing application performance.
6+
Identifies bottlenecks, profiles runtime and memory usage, suggests optimizations,
7+
and can implement performance improvements autonomously.
8+
"""
9+
10+
import logging
11+
from pathlib import Path
12+
13+
from core.client import create_client
14+
from phase_config import get_phase_model, get_phase_thinking_budget
15+
from phase_event import ExecutionPhase, emit_phase
16+
from task_logger import (
17+
LogPhase,
18+
get_task_logger,
19+
)
20+
from ui import (
21+
BuildState,
22+
Icons,
23+
StatusManager,
24+
bold,
25+
box,
26+
highlight,
27+
icon,
28+
muted,
29+
print_status,
30+
)
31+
32+
from .session import run_agent_session, save_token_stats
33+
34+
logger = logging.getLogger(__name__)
35+
36+
37+
async def run_performance_profiler(
38+
project_dir: Path,
39+
spec_dir: Path,
40+
model: str,
41+
verbose: bool = False,
42+
) -> bool:
43+
"""
44+
Run the performance profiler agent to analyze and optimize code performance.
45+
46+
The profiler agent will:
47+
- Profile runtime performance and memory usage
48+
- Identify bottlenecks and optimization opportunities
49+
- Suggest specific optimizations
50+
- Implement optimizations with user approval
51+
- Provide before/after performance comparisons
52+
- Track performance trends over time
53+
54+
Args:
55+
project_dir: Root directory for the project
56+
spec_dir: Directory containing the spec
57+
model: Claude model to use
58+
verbose: Whether to show detailed output
59+
60+
Returns:
61+
bool: True if profiling completed successfully
62+
"""
63+
from prompts_pkg import get_performance_profiler_prompt
64+
65+
# Initialize status manager for ccstatusline
66+
status_manager = StatusManager(project_dir)
67+
status_manager.set_active(spec_dir.name, BuildState.BUILDING)
68+
emit_phase(ExecutionPhase.PERFORMANCE_PROFILING, "Profiling performance")
69+
70+
# Initialize task logger for persistent logging
71+
task_logger = get_task_logger(spec_dir)
72+
73+
# Show header
74+
content = [
75+
bold(f"{icon(Icons.GEAR)} PERFORMANCE PROFILER SESSION"),
76+
"",
77+
f"Spec: {highlight(spec_dir.name)}",
78+
muted("Analyzing and optimizing application performance."),
79+
"",
80+
muted("The agent will profile your code and suggest optimizations."),
81+
]
82+
print()
83+
print(box(content, width=70, style="heavy"))
84+
print()
85+
86+
# Start performance profiling phase in task logger
87+
if task_logger:
88+
task_logger.start_phase(
89+
LogPhase.CODING, "Starting performance profiling session..."
90+
)
91+
task_logger.set_session(1)
92+
93+
# Create client with phase-specific model and thinking budget
94+
# Respects task_metadata.json configuration when no CLI override
95+
profiler_model = get_phase_model(spec_dir, "performance_profiling", model)
96+
profiler_thinking_budget = get_phase_thinking_budget(
97+
spec_dir, "performance_profiling"
98+
)
99+
client = create_client(
100+
project_dir,
101+
spec_dir,
102+
profiler_model,
103+
agent_type="performance_profiler",
104+
max_thinking_tokens=profiler_thinking_budget,
105+
)
106+
107+
# Generate performance profiler prompt
108+
prompt = get_performance_profiler_prompt(spec_dir)
109+
110+
print_status("Running performance profiler...", "progress")
111+
print()
112+
113+
try:
114+
# Run single profiling session
115+
async with client:
116+
status, _, usage_metadata = await run_agent_session(
117+
client, prompt, spec_dir, verbose, phase=LogPhase.CODING
118+
)
119+
120+
# Save token statistics for performance profiling phase
121+
if usage_metadata:
122+
try:
123+
input_tokens = usage_metadata.get("input_tokens", 0)
124+
output_tokens = usage_metadata.get("output_tokens", 0)
125+
126+
if (
127+
"input_tokens" not in usage_metadata
128+
or "output_tokens" not in usage_metadata
129+
):
130+
logger.debug(
131+
"Usage metadata missing expected token keys; defaulting to 0s: %s",
132+
usage_metadata,
133+
)
134+
135+
saved = save_token_stats(
136+
spec_dir,
137+
"performance_profiling",
138+
input_tokens,
139+
output_tokens,
140+
)
141+
if saved:
142+
logger.debug(
143+
"Performance profiling token stats saved: %d in, %d out",
144+
input_tokens,
145+
output_tokens,
146+
)
147+
except Exception as e:
148+
logger.warning(
149+
"Failed to save performance profiling token stats: %s", e
150+
)
151+
152+
# End profiling phase in task logger
153+
if task_logger:
154+
task_logger.end_phase(
155+
LogPhase.CODING,
156+
success=(status != "error"),
157+
message="Performance profiling session completed",
158+
)
159+
160+
if status == "error":
161+
print()
162+
print_status("Performance profiling failed", "error")
163+
status_manager.update(state=BuildState.ERROR)
164+
return False
165+
166+
# Success
167+
print()
168+
content = [
169+
bold(f"{icon(Icons.SUCCESS)} PERFORMANCE PROFILING COMPLETE"),
170+
"",
171+
muted("Performance analysis and optimizations completed."),
172+
muted("Review the profiling results and optimization suggestions."),
173+
]
174+
print(box(content, width=70, style="heavy"))
175+
print()
176+
status_manager.update(state=BuildState.COMPLETE)
177+
return True
178+
179+
except Exception as e:
180+
print()
181+
print_status(f"Performance profiling error: {e}", "error")
182+
if task_logger:
183+
task_logger.log_error(f"Performance profiling error: {e}", LogPhase.CODING)
184+
status_manager.update(state=BuildState.ERROR)
185+
return False

apps/backend/agents/tools_pkg/models.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -401,6 +401,22 @@ def is_electron_mcp_enabled() -> bool:
401401
"thinking_default": "high",
402402
},
403403
# ═══════════════════════════════════════════════════════════════════════
404+
# PERFORMANCE PROFILING
405+
# ═══════════════════════════════════════════════════════════════════════
406+
"performance_profiler": {
407+
"tools": BASE_READ_TOOLS + BASE_WRITE_TOOLS + WEB_TOOLS,
408+
"mcp_servers": ["context7", "graphiti", "auto-claude"],
409+
"mcp_servers_optional": ["linear"],
410+
"auto_claude_tools": [
411+
TOOL_UPDATE_SUBTASK_STATUS,
412+
TOOL_GET_BUILD_PROGRESS,
413+
TOOL_RECORD_DISCOVERY,
414+
TOOL_RECORD_GOTCHA,
415+
TOOL_GET_SESSION_CONTEXT,
416+
],
417+
"thinking_default": "high",
418+
},
419+
# ═══════════════════════════════════════════════════════════════════════
404420
# DOCUMENTATION GENERATION
405421
# ═══════════════════════════════════════════════════════════════════════
406422
"documentation_generator": {

0 commit comments

Comments
 (0)