Skip to content
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
37 changes: 36 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@ BACKEND := backend
FRONTEND := frontend
PORT ?= 3000
DOCKER_USER ?= xprilion
VERSION ?= 0.2.0
VERSION ?= 0.3.0
DOCKER_COMPOSE := docker compose
LOGO_SRC := assets/full-logo.png

# ─── Setup ────────────────────────────────────────────────

Expand Down Expand Up @@ -240,6 +241,40 @@ docs-docker: ## Run docs in Docker (port 4000)
docs-build: ## Build docs to site/docs/.vitepress/dist
cd site && npx vitepress build docs

# ─── Logo Generation ─────────────────────────────────────

.PHONY: logo
logo: ## Generate all logo sizes from assets/full-logo.png
@echo "Generating logo sizes from $(LOGO_SRC)..."
@mkdir -p $(FRONTEND)/public site/docs/public
@# Favicons for webapp
sips -z 16 16 $(LOGO_SRC) --out $(FRONTEND)/public/favicon-16x16.png
sips -z 32 32 $(LOGO_SRC) --out $(FRONTEND)/public/favicon-32x32.png
sips -z 180 180 $(LOGO_SRC) --out $(FRONTEND)/public/apple-touch-icon.png
sips -z 192 192 $(LOGO_SRC) --out $(FRONTEND)/public/logo-192.png
sips -z 512 512 $(LOGO_SRC) --out $(FRONTEND)/public/logo-512.png
@# Generate .ico from 32x32 (copy as favicon.ico for browsers)
cp $(FRONTEND)/public/favicon-32x32.png $(FRONTEND)/public/favicon.ico
@# Logo for header/nav
sips -z 64 64 $(LOGO_SRC) --out $(FRONTEND)/public/logo-64.png
@# OG image (1200x630 for social sharing - pad/crop as needed)
sips -z 630 630 $(LOGO_SRC) --out $(FRONTEND)/public/og-image-square.png
sips -p 630 1200 $(FRONTEND)/public/og-image-square.png --out $(FRONTEND)/public/og-image.png
rm $(FRONTEND)/public/og-image-square.png
@# Copy to docs site
cp $(FRONTEND)/public/favicon-16x16.png site/docs/public/
cp $(FRONTEND)/public/favicon-32x32.png site/docs/public/
cp $(FRONTEND)/public/favicon.ico site/docs/public/
cp $(FRONTEND)/public/apple-touch-icon.png site/docs/public/
cp $(FRONTEND)/public/logo-64.png site/docs/public/
cp $(FRONTEND)/public/logo-192.png site/docs/public/
cp $(FRONTEND)/public/logo-512.png site/docs/public/
cp $(FRONTEND)/public/og-image.png site/docs/public/
@# Full logo for README
cp $(LOGO_SRC) assets/logo.png
sips -z 200 200 $(LOGO_SRC) --out assets/logo-200.png
@echo "Done! Logo sizes generated in frontend/public/, site/docs/public/, and assets/"

# ─── Cleanup ─────────────────────────────────────────────

.PHONY: clean
Expand Down
11 changes: 9 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
# OpenMLR
<p align="center">
<img src="assets/logo-200.png" alt="OpenMLR Logo" width="120" />
</p>

A self-hosted ML research agent that plans, researches, writes papers, and executes code — all in one conversation.
<h1 align="center">OpenMLR</h1>

<p align="center">
A self-hosted ML research agent that plans, researches, writes papers, and executes code — all in one conversation.
</p>

[![Deploy to Render](https://render.com/images/deploy-to-render-button.svg)](https://render.com/deploy?repo=https://github.com/xprilion/OpenMLR)
[![Deploy to Heroku](https://www.herokucdn.com/deploy/button.svg)](https://www.heroku.com/deploy?template=https://github.com/xprilion/OpenMLR)
Expand All @@ -16,6 +22,7 @@ A self-hosted ML research agent that plans, researches, writes papers, and execu
- **Plan + Execute modes** — Plan mode gathers context; Execute mode does the work. Toggle with `Cmd+M`.
- **Paper research** — OpenAlex, Semantic Scholar, arXiv, CrossRef, Papers With Code. Reads full papers, crawls citation graphs.
- **Paper writing** — Section-by-section drafting with auto-save. Export to Markdown/LaTeX.
- **Compute environments** — Execute code on local Docker, SSH remotes, or Modal cloud. Probe GPU/CPU capabilities.
- **Background jobs** — Celery + Redis. Close the browser, come back later.
- **Multi-provider LLMs** — OpenAI, Anthropic, OpenRouter, plus local models (Ollama, LM Studio).
- **MCP servers** — Connect external tools via the Model Context Protocol.
Expand Down
Binary file added assets/full-logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/logo-200.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion backend/openmlr/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@


@asynccontextmanager
async def lifespan(app: FastAPI):

Check notice on line 22 in backend/openmlr/app.py

View workflow job for this annotation

GitHub Actions / Qodana for Python

Shadowing names from outer scopes

Shadows name 'app' from outer scope
"""Startup: create tables & shared state. Shutdown: teardown sessions."""
import logging
logger = logging.getLogger("openmlr.app")
Expand Down Expand Up @@ -52,7 +52,7 @@
app = FastAPI(
title="OpenMLR",
description="ML research intern — reads papers, trains models, writes papers",
version="2.0.0",
version="0.3.0",
lifespan=lifespan,
)

Expand Down Expand Up @@ -86,7 +86,7 @@

# ── Global error handler ────────────────────────────────
@app.exception_handler(Exception)
async def global_exception_handler(request: Request, exc: Exception):

Check notice on line 89 in backend/openmlr/app.py

View workflow job for this annotation

GitHub Actions / Qodana for Python

Unused local symbols

Parameter 'request' value is not used
import logging
logger = logging.getLogger(__name__)
logger.exception(f"Unhandled exception: {exc}")
Expand Down
2 changes: 1 addition & 1 deletion backend/openmlr/routes/health.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

router = APIRouter(tags=["health"])

VERSION = "0.1.0"
VERSION = "0.3.0"


@router.get("/api/health")
Expand Down
2 changes: 1 addition & 1 deletion backend/pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "openmlr"
version = "0.2.0"
version = "0.3.0"
description = "OpenMLR — an ML research intern that reads papers, trains models, and ships code"
requires-python = ">=3.12"
license = { text = "MIT" }
Expand Down
2 changes: 1 addition & 1 deletion backend/tests/test_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ async def test_app_title(self):
assert app.title == "OpenMLR"

async def test_app_version(self):
assert app.version == "2.0.0"
assert app.version == "0.3.0"

async def test_app_routers_registered(self):
route_paths = [r.path for r in app.routes]
Expand Down
25 changes: 25 additions & 0 deletions frontend/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,31 @@
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>OpenMLR</title>
<meta name="description" content="AI-powered ML Research Agent that plans tasks, researches papers, writes drafts, and executes code" />

<!-- Favicons -->
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png" />
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png" />
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" />
<link rel="icon" href="/favicon.ico" />

<!-- PWA -->
<link rel="manifest" href="/manifest.json" />
<meta name="theme-color" content="#3b82f6" />

<!-- Open Graph -->
<meta property="og:type" content="website" />
<meta property="og:title" content="OpenMLR - ML Research Agent" />
<meta property="og:description" content="AI-powered ML Research Agent that plans tasks, researches papers, writes drafts, and executes code" />
<meta property="og:image" content="/og-image.png" />
<meta property="og:url" content="https://openmlr.dev" />
<meta property="og:site_name" content="OpenMLR" />

<!-- Twitter Card -->
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content="OpenMLR - ML Research Agent" />
<meta name="twitter:description" content="AI-powered ML Research Agent that plans tasks, researches papers, writes drafts, and executes code" />
<meta name="twitter:image" content="/og-image.png" />
</head>
<body>
<div id="root"></div>
Expand Down
Binary file added frontend/public/apple-touch-icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added frontend/public/favicon-16x16.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added frontend/public/favicon-32x32.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added frontend/public/favicon.ico
Binary file not shown.
Binary file added frontend/public/logo-192.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added frontend/public/logo-512.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added frontend/public/logo-64.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
21 changes: 21 additions & 0 deletions frontend/public/manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"name": "OpenMLR",
"short_name": "OpenMLR",
"description": "AI-powered ML Research Agent that plans tasks, researches papers, writes drafts, and executes code",
"start_url": "/",
"display": "standalone",
"background_color": "#0f172a",
"theme_color": "#3b82f6",
"icons": [
{
"src": "/logo-192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "/logo-512.png",
"sizes": "512x512",
"type": "image/png"
}
]
}
Binary file added frontend/public/og-image.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions frontend/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@
const { uuid: routeUuid } = useParams<{ uuid: string }>();

const [messages, setMessages] = useState<Message[]>([]);
const [approvalEvent, setApprovalEvent] = useState<any>(null);

Check warning on line 91 in frontend/src/App.tsx

View workflow job for this annotation

GitHub Actions / Frontend Lint

Unexpected any. Specify a different type
const [conversations, setConversations] = useState<Conversation[]>([]);
const [currentConvUuid, setCurrentConvUuid] = useState<string | null>(routeUuid || null);
const [convStatuses, setConvStatuses] = useState<Record<string, ConvStatus>>({});
Expand All @@ -101,8 +101,8 @@
const [viewingReport, setViewingReport] = useState<Resource | null>(null);
const [inputMode, setInputMode] = useState<Mode>('plan');
const [inputText, setInputText] = useState('');
const [computeNodes, setComputeNodes] = useState<any[]>([]);

Check warning on line 104 in frontend/src/App.tsx

View workflow job for this annotation

GitHub Actions / Frontend Lint

Unexpected any. Specify a different type
const [activeCompute, setActiveCompute] = useState<any>(null);

Check warning on line 105 in frontend/src/App.tsx

View workflow job for this annotation

GitHub Actions / Frontend Lint

Unexpected any. Specify a different type

// Ref to always have current conv UUID in SSE callback (avoids stale closure)
const currentConvUuidRef = useRef<string | null>(currentConvUuid);
Expand Down Expand Up @@ -172,14 +172,14 @@
}
};
init();
}, []);

Check warning on line 175 in frontend/src/App.tsx

View workflow job for this annotation

GitHub Actions / Frontend Lint

React Hook useEffect has missing dependencies: 'currentConvUuid', 'loadComputeNodes', 'loadConversations', 'navigate', 'routeUuid', 'setModel', and 'switchConv'. Either include them or remove the dependency array. If 'setModel' changes too often, find the parent component that defines it and wrap that definition in useCallback

// Handle navigation to a different conversation via URL change
useEffect(() => {
if (routeUuid && routeUuid !== currentConvUuid) {
switchConv(routeUuid);
}
}, [routeUuid]);

Check warning on line 182 in frontend/src/App.tsx

View workflow job for this annotation

GitHub Actions / Frontend Lint

React Hook useEffect has missing dependencies: 'currentConvUuid' and 'switchConv'. Either include them or remove the dependency array

const setCurrentConvStatus = useCallback((status: ConvStatus) => {
setCurrentConvUuid((uuid) => {
Expand All @@ -199,8 +199,8 @@
setApprovalEvent(null); setQuestionsPayload(null);

// Load persisted tasks and resources from database
setTasks(data.tasks?.map((t: any) => ({ title: t.title, status: t.status })) || []);

Check warning on line 202 in frontend/src/App.tsx

View workflow job for this annotation

GitHub Actions / Frontend Lint

Unexpected any. Specify a different type
setResources(data.resources?.map((r: any) => ({

Check warning on line 203 in frontend/src/App.tsx

View workflow job for this annotation

GitHub Actions / Frontend Lint

Unexpected any. Specify a different type
title: r.title,
url: r.url || '',
type: r.type,
Expand All @@ -212,7 +212,7 @@
setRightPanelOpen(true);
}

setMessages(data.messages?.map((m: any) => {

Check warning on line 215 in frontend/src/App.tsx

View workflow job for this annotation

GitHub Actions / Frontend Lint

Unexpected any. Specify a different type
if (m.role === 'tool') {
const meta = m.metadata || {};
return { id: nextId(), role: 'tool' as const, content: '', metadata: { tool: meta.tool || 'tool', args: '', output: m.content, outputSuccess: meta.success !== false } };
Expand Down Expand Up @@ -276,7 +276,7 @@
try {
const data = await api.getConversation(uuid);
if (data.messages) {
setMessages(data.messages.map((m: any) => {

Check warning on line 279 in frontend/src/App.tsx

View workflow job for this annotation

GitHub Actions / Frontend Lint

Unexpected any. Specify a different type
if (m.role === 'tool') {
const meta = m.metadata || {};
return { id: nextId(), role: 'tool' as const, content: '', metadata: { tool: meta.tool || 'tool', args: '', output: m.content, outputSuccess: meta.success !== false } };
Expand All @@ -285,7 +285,7 @@
}));
}
if (data.tasks?.length > 0 || data.resources?.length > 0) {
setTasks(data.tasks?.map((t: any) => ({ title: t.title, status: t.status })) || []);

Check warning on line 288 in frontend/src/App.tsx

View workflow job for this annotation

GitHub Actions / Frontend Lint

Unexpected any. Specify a different type
setResources(data.resources?.map((r: any) => ({ title: r.title, url: r.url || '', type: r.type, id: r.id })) || []);
setRightPanelOpen(true);
}
Expand Down Expand Up @@ -555,6 +555,7 @@
{/* Header */}
<header className="flex items-center justify-between px-3 sm:px-6 h-14 bg-surface border-b border-border shrink-0 gap-2 sm:gap-4">
<div className="flex items-center gap-2 sm:gap-3 shrink-0">
<img src="/logo-64.png" alt="OpenMLR" className="w-7 h-7 sm:w-8 sm:h-8" />
<span className="font-bold text-base sm:text-lg text-primary tracking-tight">OpenMLR</span>
<span
className={`w-2 h-2 rounded-full transition-colors duration-300 ${connected ? 'bg-success' : 'bg-error'}`}
Expand Down
7 changes: 5 additions & 2 deletions frontend/src/components/LoginPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,11 @@ export function LoginPage({ onAuth }: Props) {
return (
<div className="min-h-screen bg-bg flex items-center justify-center p-6">
<div className="w-full max-w-sm bg-surface rounded-2xl border border-border p-8 shadow-xl">
<h1 className="text-2xl font-bold text-primary text-center mb-1">OpenMLR</h1>
<p className="text-text-dim text-center mb-8">ML Research Intern</p>
<div className="flex flex-col items-center mb-6">
<img src="/logo-192.png" alt="OpenMLR" className="w-20 h-20 mb-3" />
<h1 className="text-2xl font-bold text-primary text-center">OpenMLR</h1>
<p className="text-text-dim text-center">ML Research Intern</p>
</div>

{isFirstUser && (
<div className="bg-primary/10 border border-primary/30 rounded-lg p-4 mb-6 text-sm text-text text-center">
Expand Down
15 changes: 14 additions & 1 deletion site/docs/.vitepress/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,15 @@ export default defineConfig({
cleanUrls: true,

head: [
// Favicons
["link", { rel: "icon", type: "image/png", sizes: "32x32", href: "/favicon-32x32.png" }],
["link", { rel: "icon", type: "image/png", sizes: "16x16", href: "/favicon-16x16.png" }],
["link", { rel: "apple-touch-icon", sizes: "180x180", href: "/apple-touch-icon.png" }],
["link", { rel: "icon", href: "/favicon.ico" }],

// Basic meta
["meta", { name: "author", content: "Anubhav Singh" }],
["meta", { name: "theme-color", content: "#3eaf7c" }],
["meta", { name: "theme-color", content: "#3b82f6" }],
[
"meta",
{
Expand Down Expand Up @@ -48,6 +54,9 @@ export default defineConfig({
},
],
["meta", { property: "og:url", content: "https://openmlr.dev" }],
["meta", { property: "og:image", content: "https://openmlr.dev/og-image.png" }],
["meta", { property: "og:image:width", content: "1200" }],
["meta", { property: "og:image:height", content: "630" }],

// Twitter Card
["meta", { name: "twitter:card", content: "summary_large_image" }],
Expand All @@ -60,6 +69,7 @@ export default defineConfig({
"AI-powered ML Research Agent that plans tasks, researches papers, writes drafts, and executes code",
},
],
["meta", { name: "twitter:image", content: "https://openmlr.dev/og-image.png" }],
],

// Sitemap generation
Expand All @@ -76,6 +86,7 @@ export default defineConfig({
"configuration",
"modes",
"tools",
"compute",
"architecture",
"agent-harness",
"api",
Expand All @@ -93,6 +104,7 @@ export default defineConfig({
},

themeConfig: {
logo: "/logo-64.png",
nav: [
{ text: "Home", link: "/" },
{ text: "Setup", link: "/setup" },
Expand All @@ -113,6 +125,7 @@ export default defineConfig({
items: [
{ text: "Modes (Plan / Execute)", link: "/modes" },
{ text: "Agent Tools", link: "/tools" },
{ text: "Compute Environments", link: "/compute" },
],
},
{
Expand Down
29 changes: 29 additions & 0 deletions site/docs/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,35 @@ description: OpenMLR version history and release notes. Track new features, impr

# Changelog

## v0.3.0

Compute environments, UI improvements, and bug fixes.

### Compute Environments
- **Multi-backend compute** — Execute code on local Docker, SSH remotes, or Modal cloud
- **SSH key management** — Generate Ed25519/RSA keys, upload existing keys via Settings > Compute
- **Compute probing** — Detect OS, GPUs (with CUDA version), Python versions, and disk space
- **Compute selection** — Switch between configured compute nodes mid-conversation
- **Connection pooling** — SSH connections are reused across tool calls for performance
- **Docker-in-Docker detection** — Worker container executes commands directly when already in Docker

### UI Improvements
- **Collapsible Tasks & Resources** — Click section headers in the right panel to collapse/expand
- **Fixed right panel layout** — Right panel no longer causes page scroll issues when toggled
- **Improved scroll behavior** — Message list scrolls correctly without affecting page layout

### Bug Fixes
- Fixed `scrollIntoView` causing entire page to scroll when RightPanel is open
- Fixed `_get_draft` database call using wrong function name
- Fixed test suite failures related to async database mocking

### Internal
- Added `_running_in_container()` detection for Docker Compose worker environments
- Improved test coverage for compute and writing tools
- Updated Settings nav to reflect current menu structure

---

## v0.2.0

Major rewrite of the mode system, paper writing, processing architecture, and UI routing.
Expand Down
Loading
Loading