Skip to content

Add support for locally exported trajectories #2

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

Merged
merged 15 commits into from
Mar 7, 2025
Merged
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
26 changes: 26 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
name: Test

on:
push:
branches: [ main, add-openhands-support ]
pull_request:
branches: [ main ]

jobs:
test:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4

- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'

- name: Install dependencies
run: npm ci

- name: Run tests
run: npm test -- --watchAll=false
25 changes: 23 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ A modern web application for visualizing OpenHands Resolver and their execution
- **GitHub Authentication**: Secure GitHub token authentication with appropriate scopes
- **Repository Selection**: Easy navigation between repositories
- **Workflow Run Timeline**: Interactive timeline visualization of workflow execution steps
- **OpenHands Support**: Upload and visualize trajectories exported from OpenHands
- **Dark/Light Mode**: Full support for system and user preferences
- **Responsive Design**: Works seamlessly on desktop and mobile devices
- **Keyboard Navigation**: Efficient keyboard shortcuts for timeline traversal
Expand Down Expand Up @@ -41,9 +42,10 @@ A modern web application for visualizing OpenHands Resolver and their execution

4. Open [http://localhost:3000](http://localhost:3000) in your browser

### GitHub Token Setup
### Usage

To use Trajectory Visualizer, you'll need a GitHub personal access token with the following scopes:
#### GitHub Workflow Visualization
To use Trajectory Visualizer with GitHub workflows, you'll need a GitHub personal access token with the following scopes:
- `repo` - Full control of private repositories
- `workflow` - Update GitHub Action workflows

Expand All @@ -52,6 +54,20 @@ Follow these steps to create a token:
2. Generate a new token with the required scopes
3. Copy the token and paste it into the application when prompted

#### OpenHands Trajectory Visualization
You can also visualize trajectories exported from OpenHands:

1. In OpenHands, use the download button next to the thumbs up/down buttons to export a trajectory
2. In Trajectory Visualizer, click "Upload OpenHands Trajectory"
3. Drag and drop the downloaded JSON file or click to select it
4. The trajectory will be displayed in the timeline view

The timeline shows:
- Actions (commands, edits, searches) in blue
- Observations (messages, errors) in gray/red
- Each step shows the timestamp, title, content, and any metadata
- You can navigate through steps using arrow keys or clicking

## Technology Stack

- **Frontend**: React, TypeScript, Tailwind CSS
Expand All @@ -68,8 +84,13 @@ trajectory-visualizer/
│ ├── public/
│ ├── src/
│ │ ├── components/
│ │ │ ├── upload/ # OpenHands trajectory upload
│ │ │ ├── timeline/ # Timeline visualization
│ │ │ └── ...
│ │ ├── services/
│ │ ├── types/
│ │ ├── utils/
│ │ │ └── openhands-converter.ts # OpenHands format converter
│ │ └── App.tsx
│ ├── package.json
│ └── tsconfig.json
Expand Down
66 changes: 64 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"path-browserify": "^1.0.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-dropzone": "^14.3.8",
"react-icons": "^5.5.0",
"react-markdown": "^9.0.1",
"react-router-dom": "^7.2.0",
Expand Down
101 changes: 96 additions & 5 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { WorkflowRunsList } from './components/workflow-runs';
import RunDetails from './components/RunDetails';
import RunDetailsSkeleton from './components/loading/RunDetailsSkeleton';
import { WorkflowRun } from './types';
import { UploadTrajectory } from './components/upload/UploadTrajectory';

const TokenPrompt: React.FC<{ isDark?: boolean }> = ({ isDark = false }) => {
const [token, setToken] = useState('');
Expand Down Expand Up @@ -52,10 +53,10 @@ const TokenPrompt: React.FC<{ isDark?: boolean }> = ({ isDark = false }) => {
</svg>
</div>
<h2 className={`text-2xl font-semibold ${isDark ? 'text-white' : 'text-gray-900'} mb-2`}>
GitHub Token Required
Configure GitHub Token
</h2>
<p className={`${isDark ? 'text-gray-300' : 'text-gray-500'} mb-6 text-sm max-w-sm mx-auto`}>
To access GitHub repositories and workflow details, please configure your GitHub token with 'repo' scope.
To access GitHub repositories and workflow details, configure your GitHub token with 'repo' scope. This is optional if you only want to visualize local trajectories.
</p>

<div className={`${isDark ? 'bg-gray-800 border-gray-700' : 'bg-white border-gray-200'} rounded-lg shadow-sm border p-4 mb-6 w-full max-w-sm mx-auto`}>
Expand Down Expand Up @@ -279,6 +280,7 @@ const App: React.FC = () => {
const { owner, repo } = useParams();
const navigate = useNavigate();
const location = useLocation();
const [uploadedContent, setUploadedContent] = useState<any | null>(null);

// Check if we should restore from localStorage on initial load
useEffect(() => {
Expand Down Expand Up @@ -325,6 +327,11 @@ const App: React.FC = () => {
// This prevents trying to find runs from previous repositories
navigate(`/${newOwner}/${newRepo}`);
};

// Handle trajectory upload
const handleTrajectoryUpload = (content: any) => {
setUploadedContent(content);
};

return (
<div className="h-screen max-h-screen flex flex-col overflow-hidden bg-gray-50 dark:bg-gray-900">
Expand Down Expand Up @@ -360,17 +367,101 @@ const App: React.FC = () => {
</svg>
)}
</button>
{localStorage.getItem('github_token') && <RepositorySelector onSelectRepository={handleRepositorySelect} />}
{localStorage.getItem('github_token') && (
<div className="flex items-center gap-4">
<RepositorySelector onSelectRepository={handleRepositorySelect} />
{uploadedContent && (
<button
onClick={() => setUploadedContent(null)}
className="text-sm text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-300"
>
Back to Repository
</button>
)}
</div>
)}
</div>
</div>
</div>
</header>

{/* Main Content */}
<main className="max-w-[1920px] mx-auto px-4 sm:px-6 lg:px-8 py-4 flex-grow overflow-hidden">
{!localStorage.getItem('github_token') ? (
{!owner && !repo && !uploadedContent ? (
<div className="min-h-[calc(100vh-8rem)] flex items-center justify-center">
<div className="max-w-3xl mx-auto space-y-8">
<div>
<h2 className={`text-lg font-medium mb-4 ${isDark ? 'text-white' : 'text-gray-900'}`}>
Upload OpenHands Trajectory
</h2>
<UploadTrajectory onUpload={handleTrajectoryUpload} />
</div>

{!localStorage.getItem('github_token') ? (
<div>
<h2 className={`text-lg font-medium mb-4 ${isDark ? 'text-white' : 'text-gray-900'}`}>
Configure GitHub Token (Optional)
</h2>
<TokenPrompt isDark={isDark} />
</div>
) : (
<div>
<h2 className={`text-lg font-medium mb-4 ${isDark ? 'text-white' : 'text-gray-900'}`}>
Or Select GitHub Repository
</h2>
<RepositorySelector onSelectRepository={handleRepositorySelect} />
</div>
)}
</div>
</div>
) : !localStorage.getItem('github_token') && owner && repo ? (
<div className="min-h-[calc(100vh-8rem)] flex items-center justify-center">
<TokenPrompt />
<TokenPrompt isDark={isDark} />
</div>
) : !owner && !repo && !uploadedContent ? (
<div className="min-h-[calc(100vh-8rem)] flex items-center justify-center">
<div className="max-w-3xl mx-auto space-y-8">
<div>
<h2 className={`text-lg font-medium mb-4 ${isDark ? 'text-white' : 'text-gray-900'}`}>
Select GitHub Repository
</h2>
<RepositorySelector onSelectRepository={handleRepositorySelect} />
</div>

<div>
<h2 className={`text-lg font-medium mb-4 ${isDark ? 'text-white' : 'text-gray-900'}`}>
Or Upload OpenHands Trajectory
</h2>
<UploadTrajectory onUpload={handleTrajectoryUpload} />
</div>
</div>
</div>
) : uploadedContent ? (
<div className="h-[calc(100vh-8rem)] overflow-hidden">
<RunDetails
owner="local"
repo="trajectory"
run={{
id: 0,
name: 'Local Trajectory',
head_branch: '',
head_sha: '',
run_number: 0,
event: 'local',
status: 'completed',
conclusion: 'success',
workflow_id: 0,
created_at: new Date().toISOString(),
updated_at: new Date().toISOString(),
html_url: '',
run_attempt: 1,
run_started_at: new Date().toISOString(),
jobs_url: '',
logs_url: '',
workflow_name: 'Local Trajectory'
}}
initialContent={uploadedContent}
/>
</div>
) : owner && repo ? (
<div className="grid grid-cols-1 lg:grid-cols-4 gap-4 h-full">
Expand Down
Loading