Skip to content
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 AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,15 @@

## Local Dev Servers

- Managed stack: from the repo root, run `uv run --frozen python scripts/dev_server.py up`.
- Cleanup stale local server processes with `uv run --frozen python scripts/dev_server.py cleanup`.
- Stop the managed stack with `uv run --frozen python scripts/dev_server.py down`.
- Restart it with `uv run --frozen python scripts/dev_server.py restart`.
- The helper picks free frontend/backend ports, cleans stale dev servers from the same worktree before start, writes logs and state under ignored `scratch/dev-server/`, and prints the URLs it selected.
- Frontend: from `frontend/`, run `npm ci` if dependencies are missing, then `npm run dev`.
- Backend: from `backend/`, run `uv run uvicorn main:app --host ::1 --port 7860`.
- Frontend URL: http://localhost:5173/
- Default managed frontend URL: http://127.0.0.1:5173/
- Manual frontend URL: http://localhost:5173/
- Backend health check: `curl -g http://[::1]:7860/api`
- Frontend proxy health check: `curl http://localhost:5173/api`

Expand All @@ -13,6 +19,7 @@ Notes:
- Vite proxies `/api` and `/auth` to `http://localhost:7860`.
- If `127.0.0.1:7860` is already owned by another local process, binding the backend to `::1` lets the Vite proxy resolve `localhost` cleanly.
- Prefer `npm ci` over `npm install` for setup, since `npm install` may rewrite `frontend/package-lock.json` metadata depending on npm version.
- After a PR is merged and the local branch/worktree is being retired, run `uv run --frozen python scripts/dev_server.py down`, then `uv run --frozen python scripts/dev_server.py cleanup` before removing the worktree.
- Non-local LLM calls use `https://router.huggingface.co/v1` with the active Hugging Face user's token. Web sessions default to Kimi K2.6 for free users and Claude Opus 4.8 for Pro users; the CLI default is Claude Opus 4.8. For local development, set `HF_TOKEN` and optionally `ML_INTERN_DEFAULT_MODEL_ID`.

## Development Checks
Expand Down
24 changes: 24 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,30 @@ GITHUB_TOKEN=<github-personal-access-token>

All API-based model calls go through Hugging Face [Inference Providers](https://huggingface.co/docs/inference-providers/en/index), so your `HF_TOKEN` must be allowed to make Inference Provider calls. If no `HF_TOKEN` is set, the CLI will prompt you to paste one on first launch unless you start on a local model. To get a `GITHUB_TOKEN` follow the tutorial [here](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens#creating-a-fine-grained-personal-access-token). See the [local models section below](#local-models) for instructions on using agents that run on your hardware.

### Local Web Dev

Use the managed local stack when working across concurrent branches or
worktrees:

```bash
uv run --frozen python scripts/dev_server.py up
```

The helper starts the backend and frontend together, picks free ports when the
defaults are busy, points the Vite proxy at the selected backend, and writes
state/logs to ignored `scratch/dev-server/`. Before each start it also cleans
up stale backend/frontend dev server processes from the same worktree.

```bash
uv run --frozen python scripts/dev_server.py status
uv run --frozen python scripts/dev_server.py cleanup
uv run --frozen python scripts/dev_server.py restart
uv run --frozen python scripts/dev_server.py down
```

If frontend dependencies are missing, run `cd frontend && npm ci` first, or
pass `--install` to let the helper run it.

### Usage

#### Interactive mode (start a chat session):
Expand Down
24 changes: 18 additions & 6 deletions backend/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,23 @@
logger = logging.getLogger(__name__)


def _split_env_list(value: str | None) -> list[str]:
if not value:
return []
return [item.strip() for item in value.split(",") if item.strip()]


def _cors_origins() -> list[str]:
default_origins = [
"http://localhost:5173", # Vite dev server
"http://localhost:3000",
"http://127.0.0.1:5173",
"http://127.0.0.1:3000",
]
configured_origins = _split_env_list(os.environ.get("ML_INTERN_CORS_ORIGINS"))
return [*default_origins, *configured_origins]


@asynccontextmanager
async def lifespan(app: FastAPI):
"""Application lifespan handler."""
Expand Down Expand Up @@ -83,12 +100,7 @@ async def lifespan(app: FastAPI):
# CORS middleware for development
app.add_middleware(
CORSMiddleware,
allow_origins=[
"http://localhost:5173", # Vite dev server
"http://localhost:3000",
"http://127.0.0.1:5173",
"http://127.0.0.1:3000",
],
allow_origins=_cors_origins(),
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
Expand Down
55 changes: 33 additions & 22 deletions frontend/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,29 +2,40 @@ import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import path from 'path'

export default defineConfig({
plugins: [react()],
resolve: {
alias: {
'@': path.resolve(__dirname, './src'),
},
},
server: {
port: 5173,
proxy: {
'/api': {
target: 'http://localhost:7860',
changeOrigin: true,
ws: true, // Proxy WebSocket connections (/api/ws/...)
const parsePort = (value: string | undefined, fallback: number) => {
const parsed = Number.parseInt(value ?? '', 10)
return Number.isFinite(parsed) ? parsed : fallback
}

export default defineConfig(() => {
const backendProxyTarget =
process.env.VITE_BACKEND_PROXY_TARGET ?? 'http://localhost:7860'
const devServerPort = parsePort(process.env.VITE_DEV_SERVER_PORT, 5173)

return {
plugins: [react()],
resolve: {
alias: {
'@': path.resolve(__dirname, './src'),
},
'/auth': {
target: 'http://localhost:7860',
changeOrigin: true,
},
server: {
port: devServerPort,
proxy: {
'/api': {
target: backendProxyTarget,
changeOrigin: true,
ws: true, // Proxy WebSocket connections (/api/ws/...)
},
'/auth': {
target: backendProxyTarget,
changeOrigin: true,
},
},
},
},
build: {
outDir: 'dist',
sourcemap: false,
},
build: {
outDir: 'dist',
sourcemap: false,
},
}
})
Loading
Loading