Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add realtime visualization dashboard #38

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,11 @@ research_dir/*
state_saves/*
__pycache__/*
Figure*.png
testrun.py
testrun.py
# Agent logs
agent_logs/*
!agent_logs/.gitkeep
__pycache__/
agent_logs/
research_dir/
*.pyc
36 changes: 30 additions & 6 deletions ai_lab_repo.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ def perform_research(self):
Loop through all research phases
@return: None
"""
create_visualization_interface()
for phase, subtasks in self.phases:
phase_start_time = time.time() # Start timing the phase
if self.verbose: print(f"{'*'*50}\nBeginning phase: {phase}\n{'*'*50}")
Expand Down Expand Up @@ -198,6 +199,8 @@ def perform_research(self):
phase_duration = phase_end_time - phase_start_time
print(f"Subtask '{subtask}' completed in {phase_duration:.2f} seconds.")
self.statistics_per_phase[subtask]["time"] = phase_duration
if os.path.exists("agent_logs/mle_solver_latest.json"):
print(f"\nVisualization available at research_dir/visualization.html\n")

def report_refinement(self):
"""
Expand Down Expand Up @@ -526,6 +529,33 @@ def human_in_loop(self, phase, phase_prod):
else: print("Invalid response, type Y or N")
return False

def create_visualization_interface():
"""Create a simple React interface for visualizations"""
visualization_html = """
<!DOCTYPE html>
<html>
<head>
<title>Agent Laboratory Visualization</title>
<script src="https://unpkg.com/react@17/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>
</head>
<body>
<div id="root"></div>
<script type="text/javascript">
import { AgentVisualizer } from './components/AgentVisualizer';

ReactDOM.render(
React.createElement(AgentVisualizer, { agentName: "mle_solver" }),
document.getElementById('root')
);
</script>
</body>
</html>
"""

# Save the visualization interface
with open("research_dir/visualization.html", "w") as f:
f.write(visualization_html)


def parse_arguments():
Expand Down Expand Up @@ -728,9 +758,3 @@ def parse_arguments():
)

lab.perform_research()






175 changes: 175 additions & 0 deletions components/AgentVisualizer.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
import React, { useState, useEffect } from 'react';
import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, Legend, ResponsiveContainer } from 'recharts';

// Card components
const Card = ({ children, className = '' }) => (
<div className={`bg-white p-6 rounded-lg shadow-md ${className}`}>{children}</div>
);

const CardHeader = ({ children }) => (
<div className="mb-4">{children}</div>
);

const CardContent = ({ children }) => (
<div>{children}</div>
);

const CardTitle = ({ children }) => (
<h2 className="text-xl font-bold text-gray-700">{children}</h2>
);

const AgentLabVisualization = () => {
const [logData, setLogData] = useState([]);
const [selectedStep, setSelectedStep] = useState(null);
const [error, setError] = useState(null);
const [loading, setLoading] = useState(true);

useEffect(() => {
const loadData = async () => {
try {
const response = await window.fs.readFile('agent_logs/mle_solver_latest.json');
const data = JSON.parse(new TextDecoder().decode(response));
setLogData(data);
} catch (err) {
console.error('Error loading log data:', err);
setError(err.message);
} finally {
setLoading(false);
}
};

loadData();
}, []);

const formatScore = (score) => (score * 100).toFixed(1) + '%';

if (loading) {
return (
<div className="w-full h-screen flex items-center justify-center">
<div className="text-lg text-gray-600">Loading data...</div>
</div>
);
}

if (error) {
return (
<div className="w-full h-screen flex items-center justify-center">
<div className="text-lg text-red-600">Error: {error}</div>
</div>
);
}

return (
<div className="w-full mx-auto p-4 bg-gray-50 min-h-screen">
<h1 className="text-3xl font-bold mb-6 text-gray-800">Agent Lab Progress</h1>

<Card className="mb-6">
<CardHeader>
<CardTitle>Score Progress Over Time</CardTitle>
</CardHeader>
<CardContent>
<div className="h-80">
<ResponsiveContainer width="100%" height="100%">
<LineChart data={logData}>
<CartesianGrid strokeDasharray="3 3" stroke="#e5e7eb" />
<XAxis
dataKey="step"
tick={{ fill: '#4b5563' }}
stroke="#9ca3af"
/>
<YAxis
domain={[0, 1]}
tickFormatter={formatScore}
tick={{ fill: '#4b5563' }}
stroke="#9ca3af"
/>
<Tooltip
formatter={formatScore}
contentStyle={{
backgroundColor: 'white',
border: '1px solid #e5e7eb',
borderRadius: '0.375rem'
}}
/>
<Legend />
<Line
type="monotone"
dataKey="score"
stroke="#2563eb"
strokeWidth={2}
dot={{ stroke: '#2563eb', strokeWidth: 2 }}
activeDot={{ r: 6 }}
name="Score"
/>
</LineChart>
</ResponsiveContainer>
</div>
</CardContent>
</Card>

<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<Card>
<CardHeader>
<CardTitle>Commands</CardTitle>
</CardHeader>
<CardContent>
<div className="h-96 overflow-y-auto">
{logData.map((entry, idx) => (
<div
key={idx}
onClick={() => setSelectedStep(idx)}
className={`p-4 rounded-md cursor-pointer transition duration-200 ${
selectedStep === idx
? 'bg-blue-50 border border-blue-200'
: 'hover:bg-gray-50 border border-transparent'
}`}
>
<div className="flex justify-between items-center">
<span className="font-medium text-gray-700">Step {entry.step}</span>
<span className="text-sm text-gray-500">{entry.command}</span>
</div>
<div className="text-sm text-gray-600 mt-1">
Score: {formatScore(entry.score)}
</div>
</div>
))}
</div>
</CardContent>
</Card>

<Card>
<CardHeader>
<CardTitle>Step Details</CardTitle>
</CardHeader>
<CardContent>
{selectedStep !== null && logData[selectedStep] ? (
<div className="space-y-6">
<div>
<h3 className="font-medium text-gray-700">Code Changes</h3>
<pre className="bg-gray-50 p-4 rounded-md mt-2 overflow-x-auto border border-gray-200">
<code className="text-sm">{logData[selectedStep].code_lines.join('\n')}</code>
</pre>
</div>
<div>
<h3 className="font-medium text-gray-700">Model Response</h3>
<p className="text-gray-600 mt-2 leading-relaxed">
{logData[selectedStep].model_response}
</p>
</div>
<div className="text-sm text-gray-500">
{new Date(logData[selectedStep].timestamp).toLocaleString()}
</div>
</div>
) : (
<div className="text-center text-gray-500 py-8">
Select a step to view details
</div>
)}
</CardContent>
</Card>
</div>
</div>
);
};

export default AgentLabVisualization;
Loading