feat: Dashboard improvements and configuration updates
Dashboard: - Enhanced terminal components (ClaudeTerminal, GlobalClaudeTerminal) - Improved MarkdownRenderer for better documentation display - Updated convergence plots (ConvergencePlot, PlotlyConvergencePlot) - Refined Home, Analysis, Dashboard, Setup, Results pages - Added StudyContext improvements - Updated vite.config for better dev experience Configuration: - Updated CLAUDE.md with latest instructions - Enhanced launch_dashboard.py - Updated config.py settings 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -20,9 +20,43 @@ router = APIRouter()
|
||||
_terminal_sessions: dict = {}
|
||||
|
||||
# Path to Atomizer root (for loading prompts)
|
||||
ATOMIZER_ROOT = os.path.dirname(os.path.dirname(os.path.dirname(
|
||||
os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
)))
|
||||
# Go up 5 levels: terminal.py -> routes -> api -> backend -> atomizer-dashboard -> Atomizer
|
||||
_file_path = os.path.abspath(__file__)
|
||||
ATOMIZER_ROOT = os.path.normpath(os.path.dirname(os.path.dirname(os.path.dirname(
|
||||
os.path.dirname(os.path.dirname(_file_path))
|
||||
))))
|
||||
STUDIES_DIR = os.path.join(ATOMIZER_ROOT, "studies")
|
||||
# Debug: print the resolved path at module load
|
||||
print(f"[Terminal] ATOMIZER_ROOT resolved to: {ATOMIZER_ROOT}")
|
||||
|
||||
|
||||
def resolve_study_path(study_id: str) -> str:
|
||||
"""Find study folder by scanning topic directories.
|
||||
|
||||
Returns relative path from ATOMIZER_ROOT (e.g., 'studies/M1_Mirror/m1_mirror_adaptive_V14').
|
||||
"""
|
||||
# First check direct path (flat structure)
|
||||
direct_path = os.path.join(STUDIES_DIR, study_id)
|
||||
if os.path.isdir(direct_path):
|
||||
setup_dir = os.path.join(direct_path, "1_setup")
|
||||
config_file = os.path.join(direct_path, "optimization_config.json")
|
||||
if os.path.exists(setup_dir) or os.path.exists(config_file):
|
||||
return f"studies/{study_id}"
|
||||
|
||||
# Scan topic folders for nested structure
|
||||
if os.path.isdir(STUDIES_DIR):
|
||||
for topic_name in os.listdir(STUDIES_DIR):
|
||||
topic_path = os.path.join(STUDIES_DIR, topic_name)
|
||||
if os.path.isdir(topic_path) and not topic_name.startswith('.'):
|
||||
study_path = os.path.join(topic_path, study_id)
|
||||
if os.path.isdir(study_path):
|
||||
setup_dir = os.path.join(study_path, "1_setup")
|
||||
config_file = os.path.join(study_path, "optimization_config.json")
|
||||
if os.path.exists(setup_dir) or os.path.exists(config_file):
|
||||
return f"studies/{topic_name}/{study_id}"
|
||||
|
||||
# Fallback to flat path
|
||||
return f"studies/{study_id}"
|
||||
|
||||
|
||||
def get_session_prompt(study_name: str = None) -> str:
|
||||
@@ -54,19 +88,21 @@ def get_session_prompt(study_name: str = None) -> str:
|
||||
]
|
||||
|
||||
if study_name:
|
||||
# Resolve actual study path (handles nested folder structure)
|
||||
study_path = resolve_study_path(study_name)
|
||||
prompt_lines.extend([
|
||||
f"## Current Study: `{study_name}`",
|
||||
"",
|
||||
f"**Directory**: `studies/{study_name}/`",
|
||||
f"**Directory**: `{study_path}/`",
|
||||
"",
|
||||
"Key files:",
|
||||
f"- `studies/{study_name}/1_setup/optimization_config.json` - Configuration",
|
||||
f"- `studies/{study_name}/2_results/study.db` - Optuna database",
|
||||
f"- `studies/{study_name}/README.md` - Study documentation",
|
||||
f"- `{study_path}/1_setup/optimization_config.json` - Configuration",
|
||||
f"- `{study_path}/3_results/study.db` - Optuna database",
|
||||
f"- `{study_path}/README.md` - Study documentation",
|
||||
"",
|
||||
"Quick status check:",
|
||||
"```bash",
|
||||
f"python -c \"import optuna; s=optuna.load_study('{study_name}', 'sqlite:///studies/{study_name}/2_results/study.db'); print(f'Trials: {{len(s.trials)}}, Best: {{s.best_value}}')\"",
|
||||
f"python -c \"import optuna; s=optuna.load_study('{study_name}', 'sqlite:///{study_path}/3_results/study.db'); print(f'Trials: {{len(s.trials)}}, Best: {{s.best_value}}')\"",
|
||||
"```",
|
||||
"",
|
||||
])
|
||||
@@ -99,8 +135,10 @@ def get_session_prompt(study_name: str = None) -> str:
|
||||
try:
|
||||
from winpty import PtyProcess
|
||||
HAS_WINPTY = True
|
||||
except ImportError:
|
||||
print("[Terminal] winpty is available")
|
||||
except ImportError as e:
|
||||
HAS_WINPTY = False
|
||||
print(f"[Terminal] winpty not available: {e}")
|
||||
|
||||
|
||||
class TerminalSession:
|
||||
@@ -120,6 +158,15 @@ class TerminalSession:
|
||||
self.websocket = websocket
|
||||
self._running = True
|
||||
|
||||
# Validate working directory exists
|
||||
if not os.path.isdir(self.working_dir):
|
||||
await self.websocket.send_json({
|
||||
"type": "error",
|
||||
"message": f"Working directory does not exist: {self.working_dir}"
|
||||
})
|
||||
self._running = False
|
||||
return
|
||||
|
||||
try:
|
||||
if self._use_winpty:
|
||||
# Use winpty for proper PTY on Windows
|
||||
@@ -143,7 +190,8 @@ class TerminalSession:
|
||||
)
|
||||
elif sys.platform == "win32":
|
||||
# Fallback: Windows without winpty - use subprocess
|
||||
# Run claude with --dangerously-skip-permissions for non-interactive mode
|
||||
import shutil
|
||||
claude_cmd = shutil.which("claude") or "claude"
|
||||
self.process = subprocess.Popen(
|
||||
["cmd.exe", "/k", claude_cmd],
|
||||
stdin=subprocess.PIPE,
|
||||
@@ -157,6 +205,8 @@ class TerminalSession:
|
||||
else:
|
||||
# On Unix, use pty
|
||||
import pty
|
||||
import shutil
|
||||
claude_cmd = shutil.which("claude") or "claude"
|
||||
master_fd, slave_fd = pty.openpty()
|
||||
self.process = subprocess.Popen(
|
||||
[claude_cmd],
|
||||
@@ -467,6 +517,9 @@ async def get_context(study_id: str = None):
|
||||
"""
|
||||
prompt = get_session_prompt(study_id)
|
||||
|
||||
# Resolve study path for nested folder structure
|
||||
study_path = resolve_study_path(study_id) if study_id else None
|
||||
|
||||
return {
|
||||
"study_id": study_id,
|
||||
"prompt": prompt,
|
||||
@@ -476,8 +529,8 @@ async def get_context(study_id: str = None):
|
||||
".claude/skills/02_CONTEXT_LOADER.md",
|
||||
],
|
||||
"study_files": [
|
||||
f"studies/{study_id}/1_setup/optimization_config.json",
|
||||
f"studies/{study_id}/2_results/study.db",
|
||||
f"studies/{study_id}/README.md",
|
||||
] if study_id else []
|
||||
f"{study_path}/1_setup/optimization_config.json",
|
||||
f"{study_path}/3_results/study.db",
|
||||
f"{study_path}/README.md",
|
||||
] if study_path else []
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user