mirror of
https://github.com/leonvanzyl/autocoder.git
synced 2026-02-02 07:23:35 +00:00
refactor: optimize token usage, deduplicate code, fix bugs across agents
Token reduction (~40% per session, ~2.3M fewer tokens per 200-feature project): - Agent-type-specific tool lists: coding 9, testing 5, init 5 (was 19 for all) - Right-sized max_turns: coding 300, testing 100 (was 1000 for all) - Trimmed coding prompt template (~150 lines removed) - Streamlined testing prompt with batch support - YOLO mode now strips browser testing instructions from prompt - Added Grep, WebFetch, WebSearch to expand project session Performance improvements: - Rate limit retries start at ~15s with jitter (was fixed 60s) - Post-spawn delay reduced to 0.5s (was 2s) - Orchestrator consolidated to 1 DB query per loop (was 5-7) - Testing agents batch 3 features per session (was 1) - Smart context compaction preserves critical state, discards noise Bug fixes: - Removed ghost feature_release_testing MCP tool (wasted tokens every test session) - Forward all 9 Vertex AI env vars to chat sessions (was missing 3) - Fix DetachedInstanceError risk in test batch ORM access - Prevent duplicate testing of same features in parallel mode Code deduplication: - _get_project_path(): 9 copies -> 1 shared utility (project_helpers.py) - validate_project_name(): 9 copies -> 2 variants in 1 file (validation.py) - ROOT_DIR: 10 copies -> 1 definition (chat_constants.py) - API_ENV_VARS: 4 copies -> 1 source of truth (env_constants.py) Security hardening: - Unified sensitive directory blocklist (14 dirs, was two divergent lists) - Cached get_blocked_paths() for O(1) directory listing checks - Terminal security warning when ALLOW_REMOTE=1 exposes WebSocket - 20 new security tests for EXTRA_READ_PATHS blocking - Extracted _validate_command_list() and _validate_pkill_processes() helpers Type safety: - 87 mypy errors -> 0 across 58 source files - Installed types-PyYAML for proper yaml stub types - Fixed SQLAlchemy Column[T] coercions across all routers Dead code removed: - 13 files deleted (~2,679 lines): unused UI components, debug logs, outdated docs - 7 unused npm packages removed (Radix UI components with 0 imports) - AgentAvatar.tsx reduced from 615 -> 119 lines (SVGs extracted to mascotData.tsx) New CLI options: - --testing-batch-size (1-5) for parallel mode test batching - --testing-feature-ids for direct multi-feature testing Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -16,8 +16,11 @@ from typing import Set
|
||||
from fastapi import WebSocket, WebSocketDisconnect
|
||||
|
||||
from .schemas import AGENT_MASCOTS
|
||||
from .services.chat_constants import ROOT_DIR
|
||||
from .services.dev_server_manager import get_devserver_manager
|
||||
from .services.process_manager import get_manager
|
||||
from .utils.project_helpers import get_project_path as _get_project_path
|
||||
from .utils.validation import is_valid_project_name as validate_project_name
|
||||
|
||||
# Lazy imports
|
||||
_count_passing_tests = None
|
||||
@@ -95,11 +98,13 @@ class AgentTracker:
|
||||
|
||||
# Coding agent start: "Started coding agent for feature #X"
|
||||
if line.startswith("Started coding agent for feature #"):
|
||||
try:
|
||||
feature_id = int(re.search(r'#(\d+)', line).group(1))
|
||||
return await self._handle_agent_start(feature_id, line, agent_type="coding")
|
||||
except (AttributeError, ValueError):
|
||||
pass
|
||||
m = re.search(r'#(\d+)', line)
|
||||
if m:
|
||||
try:
|
||||
feature_id = int(m.group(1))
|
||||
return await self._handle_agent_start(feature_id, line, agent_type="coding")
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
# Testing agent start: "Started testing agent for feature #X (PID xxx)"
|
||||
testing_start_match = TESTING_AGENT_START_PATTERN.match(line)
|
||||
@@ -116,12 +121,14 @@ class AgentTracker:
|
||||
|
||||
# Coding agent complete: "Feature #X completed/failed" (without "testing" keyword)
|
||||
if line.startswith("Feature #") and ("completed" in line or "failed" in line) and "testing" not in line:
|
||||
try:
|
||||
feature_id = int(re.search(r'#(\d+)', line).group(1))
|
||||
is_success = "completed" in line
|
||||
return await self._handle_agent_complete(feature_id, is_success, agent_type="coding")
|
||||
except (AttributeError, ValueError):
|
||||
pass
|
||||
m = re.search(r'#(\d+)', line)
|
||||
if m:
|
||||
try:
|
||||
feature_id = int(m.group(1))
|
||||
is_success = "completed" in line
|
||||
return await self._handle_agent_complete(feature_id, is_success, agent_type="coding")
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
# Check for feature-specific output lines: [Feature #X] content
|
||||
# Both coding and testing agents use this format now
|
||||
@@ -444,7 +451,7 @@ class OrchestratorTracker:
|
||||
timestamp = datetime.now().isoformat()
|
||||
|
||||
# Add to recent events (keep last 5)
|
||||
event = {
|
||||
event: dict[str, str | int] = {
|
||||
'eventType': event_type,
|
||||
'message': message,
|
||||
'timestamp': timestamp,
|
||||
@@ -487,17 +494,6 @@ class OrchestratorTracker:
|
||||
self.recent_events.clear()
|
||||
|
||||
|
||||
def _get_project_path(project_name: str) -> Path:
|
||||
"""Get project path from registry."""
|
||||
import sys
|
||||
root = Path(__file__).parent.parent
|
||||
if str(root) not in sys.path:
|
||||
sys.path.insert(0, str(root))
|
||||
|
||||
from registry import get_project_path
|
||||
return get_project_path(project_name)
|
||||
|
||||
|
||||
def _get_count_passing_tests():
|
||||
"""Lazy import of count_passing_tests."""
|
||||
global _count_passing_tests
|
||||
@@ -564,15 +560,6 @@ class ConnectionManager:
|
||||
# Global connection manager
|
||||
manager = ConnectionManager()
|
||||
|
||||
# Root directory
|
||||
ROOT_DIR = Path(__file__).parent.parent
|
||||
|
||||
|
||||
def validate_project_name(name: str) -> bool:
|
||||
"""Validate project name to prevent path traversal."""
|
||||
return bool(re.match(r'^[a-zA-Z0-9_-]{1,50}$', name))
|
||||
|
||||
|
||||
async def poll_progress(websocket: WebSocket, project_name: str, project_dir: Path):
|
||||
"""Poll database for progress changes and send updates."""
|
||||
count_passing_tests = _get_count_passing_tests()
|
||||
@@ -652,7 +639,7 @@ async def project_websocket(websocket: WebSocket, project_name: str):
|
||||
agent_index, _ = await agent_tracker.get_agent_info(feature_id)
|
||||
|
||||
# Send the raw log line with optional feature/agent attribution
|
||||
log_msg = {
|
||||
log_msg: dict[str, str | int] = {
|
||||
"type": "log",
|
||||
"line": line,
|
||||
"timestamp": datetime.now().isoformat(),
|
||||
|
||||
Reference in New Issue
Block a user