Your personal AI assistant. Just chat with it to search for information, process files, write code, or draft reports. It automatically breaks down complex tasks and executes them step by step, showing you the results in real time.
- Deep Agent — multi-step reasoning with tool use, subagents, file I/O, and persistent memory
- Streaming chat — real-time SSE streaming with Markdown rendering and syntax highlighting
- Sandbox execution — isolated code execution via OpenSandbox, with automatic pip setup
- Tool call visualization — expandable tool call cards show args and results inline, in chronological order
- Skill system — dynamic skill installation from URLs, auto-restored on sandbox rebuild
- JWT authentication — user registration/login with thread-level isolation
- Persistent storage — Redis for thread metadata, MongoDB for conversation history and long-term memory
- Docker-first — multi-stage build bundles frontend and backend into a single image
┌──────────────────────┐ SSE + REST ┌──────────────────────────┐
│ React frontend (Vite) │ ◄─────────────────► │ FastAPI server │
│ Port 5173 (dev only) │ │ Port 8000 │
└──────────────────────┘ └────────┬─────────────────┘
│
┌─────────────────────────┼─────────────────────┐
│ │ │
┌────▼────┐ ┌──────▼──────┐ ┌─────▼──────┐
│ Redis │ │ MongoDB │ │ OpenSandbox │
│ metadata │ │ checkpoints │ │ code exec │
│ sandbox │ │ store/memory │ │ sandbox │
└─────────┘ └─────────────┘ └────────────┘
| Layer | Technology | Purpose |
|---|---|---|
| Frontend | React 19 + TypeScript + Tailwind CSS 4 | Chat UI with Markdown, tool call cards, streaming |
| API | FastAPI + Uvicorn | REST endpoints, SSE streaming, JWT auth |
| Agent | LangGraph + DeepAgents | Multi-step reasoning, tool orchestration, subagents |
| LLM | DeepSeek (OpenAI-compatible) | Model provider (configurable) |
| Checkpoint | MongoDB | Conversation history, agent state persistence |
| Memory | MongoDB Store | Long-term user preferences at /memories/ |
| Metadata | Redis | Thread list, sandbox mappings, skill records |
| Sandbox | OpenSandbox | Isolated shell commands, file I/O, code execution |
- Python 3.11+ with uv or pip
- Redis (any version)
- MongoDB 7+
- OpenSandbox server
- Node.js 22+ (frontend development only)
cp .env.example .env
# Edit .env with your API keys and connection stringsBackend:
uv sync
uv run uvicorn src.main:app --host 0.0.0.0 --port 8000 --reloadFrontend (in a separate terminal):
cd client
npm install
npm run dev # → http://localhost:5173The Vite dev server proxies /api requests to localhost:8000, so you get hot module replacement for the frontend while the backend handles API calls.
All endpoints are prefixed with /api. Protected endpoints require Authorization: Bearer <token>.
| Method | Path | Auth | Description |
|---|---|---|---|
POST |
/auth/register |
No | Register a new user |
POST |
/auth/login |
No | Login, returns JWT |
GET |
/auth/me |
Yes | Get current user info |
| Method | Path | Auth | Description |
|---|---|---|---|
POST |
/threads |
Yes | Create a new thread |
GET |
/threads |
Yes | List user's threads |
GET |
/threads/{id} |
Yes | Get thread metadata |
DELETE |
/threads/{id} |
Yes | Delete thread and its sandbox |
| Method | Path | Auth | Description |
|---|---|---|---|
POST |
/threads/{id}/runs |
Yes | Non-streaming run |
POST |
/threads/{id}/runs/stream |
Yes | SSE streaming run |
GET |
/threads/{id}/state |
Yes | Read thread state / messages |
The streaming endpoint emits the following events:
type |
Payload | Description |
|---|---|---|
message |
content, tool_calls, tool_call_chunks |
AI text delta and/or tool call information |
tool_result |
tool_call_id, name, content |
Tool execution result |
done |
thread_id |
Stream completed successfully |
error |
detail |
Error message |
The agent comes with a set of built-in tools accessible from the sandbox:
| Tool | Description |
|---|---|
search |
Web search via Tavily |
read_file |
Read files from sandbox (supports PDF, Markdown, DOCX, XLSX, PPTX, TXT) |
save_to_markdown |
Save text as a Markdown file in the sandbox |
save_to_pdf |
Save text as a PDF in the sandbox |
generate_download_url |
Generate a signed download URL for a sandbox file |
install_skill |
Download and install a skill from a URL |
utc_now |
Get current UTC timestamp |
Additional tools can be integrated via MCP (Model Context Protocol).
├── src/
│ ├── main.py # FastAPI app entry point
│ ├── model_provider.py # LLM configuration
│ ├── api/
│ │ └── chat.py # Thread, run, and state endpoints
│ ├── auth/
│ │ ├── router.py # Registration and login endpoints
│ │ ├── security.py # JWT encode / decode
│ │ ├── models.py # User data models
│ │ └── dependencies.py # get_current_user dependency
│ ├── deep_agent/
│ │ ├── graph.py # Agent factory, caching, persistence
│ │ ├── opensandbox_backend.py # OpenSandbox backend (create, reconnect, cleanup)
│ │ └── sub_agents.py # Subagent definitions
│ ├── tools/
│ │ ├── file_tool.py # File read/write/convert tools
│ │ ├── install_skill.py # Dynamic skill installation
│ │ ├── search_tool.py # Web search tool
│ │ ├── mcp_tool.py # MCP integration
│ │ └── sandbox_utils.py # Sandbox helpers and skill records
│ └── utils/
│ └── path.py # Project root path helper
├── client/ # React frontend (built separately)
│ └── src/
│ ├── components/chat/ # Chat UI components
│ ├── contexts/ # StreamContext, ThreadContext
│ ├── hooks/ # useStream, useAutoScroll, useThreads
│ ├── api/ # API client + SSE stream parser
│ └── lib/ # TypeScript types and helpers
├── AGENTS.md # Agent system prompt and conventions
├── Dockerfile # Multi-stage build (frontend + backend)
├── docker-compose.yml # Full stack (app + Redis + MongoDB)
└── .github/workflows/build-image.yml # CI: build on GitHub Release
The Dockerfile uses a multi-stage build:
- Stage 1 (
node:22-alpine) — builds the React frontend intoclient/dist/ - Stage 2 (
python:3.11-slim) — installs Python dependencies, copies the frontend build, and serves everything with Uvicorn on port 8000
In production, the frontend is served directly by FastAPI — no separate web server needed.
Pull the pre-built image or build locally, then start the full stack:
# Clone the repository
git clone https://github.com/arixse/MiloAgentServer.git
cd MiloAgentServer
# Configure environment
cp .env.example .env
# Edit .env with your API keys
# Start all services (app + Redis + MongoDB)
docker compose up -dThe app is available at http://localhost:8000. The compose file includes:
| Service | Image | Port |
|---|---|---|
app |
milo-agent:latest (built locally) |
8000 |
redis |
redis:7-alpine |
6379 |
mongodb |
mongo:7 |
27017 |
All services are connected via an internal milo-net bridge network. Redis and MongoDB data are persisted in named volumes.
To update to a new version:
git pull
docker compose up -d --buildCreating a GitHub Release triggers an automatic build and push to GHCR. To deploy with the pre-built image:
# Pull the image
docker pull ghcr.io/arixse/milo-agent-server:latest
# Start Redis and MongoDB separately
docker run -d --name milo-redis -p 6379:6379 redis:7-alpine
docker run -d --name milo-mongo -p 27017:27017 mongo:7
# Start the app
docker run -d \
--name milo-agent \
-p 8000:8000 \
--env-file .env \
-e REDIS_HOST=host.docker.internal \
-e MONGO_URI=mongodb://host.docker.internal:27017 \
ghcr.io/arixse/milo-agent-server:latestNote
On Linux, replace host.docker.internal with the host machine's IP or use --network host.
Prerequisites: Python 3.11+, Node.js 22+, Redis, MongoDB.
# Build the frontend
cd client
npm install && npm run build
cd ..
# Install Python dependencies
uv sync --no-dev
# Start the server
uv run uvicorn src.main:app --host 0.0.0.0 --port 8000Place the app behind a reverse proxy (nginx, Caddy) for TLS termination:
# Example nginx configuration
server {
listen 443 ssl;
server_name your-domain.com;
location / {
proxy_pass http://127.0.0.1:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_buffering off; # Required for SSE streaming
}
}Important
SSE streaming requires disabling proxy buffering (proxy_buffering off in nginx, X-Accel-Buffering: no header otherwise).
| Variable | Required | Default |
|---|---|---|
MODEL_API_KEY |
Yes | — |
MODEL_NAME |
No | |
MODEL_BASE_URL |
No | |
OPENSANDBOX_SERVER_URL |
Yes | |
OPENSANDBOX_API_KEY |
Yes | — |
REDIS_HOST |
No | localhost |
REDIS_PORT |
No | 6379 |
MONGO_URI |
No | mongodb://localhost:27017 |
MONGO_DB_NAME |
No | MiloAgent |
USE_MONGO_PERSISTENCE |
No | false |
JWT_SECRET_KEY |
No | auto-generated |
JWT_ALGORITHM |
No | HS256 |
ACCESS_TOKEN_EXPIRE_MINUTES |
No | 1440 |
SANDBOX_TIMEOUT_HOURS |
No | 24 |
TAVILY_API_KEY |
No | — |
- Each thread gets its own isolated OpenSandbox instance (
opensandbox/code-interpreter:v1.0.2) - Sandboxes are automatically renewed in the background (default 24h timeout)
- On server restart, sandboxes are reconnected via Redis mappings
- Thread deletion or server shutdown cleans up the associated sandbox
- pip is automatically installed during sandbox initialization via a three-tier fallback (built-in pip →
ensurepip→apt-get)
Skills are zip archives installed into the sandbox's /skills/ directory:
- Agent calls
install_skillwith a skill name and download URL - The zip is downloaded, uploaded to the sandbox, and extracted
- Installation records are persisted in Redis per user
- If a sandbox is destroyed and recreated, all skills are automatically restored
Note
Skill state does not persist across sandbox rebuilds — only the installation records are kept. Skills that modify system state (e.g., apt-get install) will need to be re-run.