* docs: deep audit — fix stale config keys, missing commands, and registry drift Cross-checked ~80 high-impact docs pages (getting-started, reference, top-level user-guide, user-guide/features) against the live registries: hermes_cli/commands.py COMMAND_REGISTRY (slash commands) hermes_cli/auth.py PROVIDER_REGISTRY (providers) hermes_cli/config.py DEFAULT_CONFIG (config keys) toolsets.py TOOLSETS (toolsets) tools/registry.py get_all_tool_names() (tools) python -m hermes_cli.main <subcmd> --help (CLI args) reference/ - cli-commands.md: drop duplicate hermes fallback row + duplicate section, add stepfun/lmstudio to --provider enum, expand auth/mcp/curator subcommand lists to match --help output (status/logout/spotify, login, archive/prune/ list-archived). - slash-commands.md: add missing /sessions and /reload-skills entries + correct the cross-platform Notes line. - tools-reference.md: drop bogus '68 tools' headline, drop fictional 'browser-cdp toolset' (these tools live in 'browser' and are runtime-gated), add missing 'kanban' and 'video' toolset sections, fix MCP example to use the real mcp_<server>_<tool> prefix. - toolsets-reference.md: list browser_cdp/browser_dialog inside the 'browser' row, add missing 'kanban' and 'video' toolset rows, drop the stale '38 tools' count for hermes-cli. - profile-commands.md: add missing install/update/info subcommands, document fish completion. - environment-variables.md: dedupe GMI_API_KEY/GMI_BASE_URL rows (kept the one with the correct gmi-serving.com default). - faq.md: Anthropic/Google/OpenAI examples — direct providers exist (not just via OpenRouter), refresh the OpenAI model list. getting-started/ - installation.md: PortableGit (not MinGit) is what the Windows installer fetches; document the 32-bit MinGit fallback. - installation.md / termux.md: installer prefers .[termux-all] then falls back to .[termux]. - nix-setup.md: Python 3.12 (not 3.11), Node.js 22 (not 20); fix invalid 'nix flake update --flake' invocation. - updating.md: 'hermes backup restore --state pre-update' doesn't exist — point at the snapshot/quick-snapshot flow; correct config key 'updates.pre_update_backup' (was 'update.backup'). user-guide/ - configuration.md: api_max_retries default 3 (not 2); display.runtime_footer is the real key (not display.runtime_metadata_footer); checkpoints defaults enabled=false / max_snapshots=20 (not true / 50). - configuring-models.md: 'hermes model list' / 'hermes model set ...' don't exist — hermes model is interactive only. - tui.md: busy_indicator -> tui_status_indicator with values kaomoji|emoji|unicode|ascii (not kawaii|minimal|dots|wings|none). - security.md: SSH backend keys (TERMINAL_SSH_HOST/USER/KEY) live in .env, not config.yaml. - windows-wsl-quickstart.md: there is no 'hermes api' subcommand — the OpenAI-compatible API server runs inside hermes gateway. user-guide/features/ - computer-use.md: approvals.mode (not security.approval_level); fix broken ./browser-use.md link to ./browser.md. - fallback-providers.md: top-level fallback_providers (not model.fallback_providers); the picker is subcommand-based, not modal. - api-server.md: API_SERVER_* are env vars — write to per-profile .env, not 'hermes config set' which targets YAML. - web-search.md: drop web_crawl as a registered tool (it isn't); deep-crawl modes are exposed through web_extract. - kanban.md: failure_limit default is 2, not '~5'. - plugins.md: drop hard-coded '33 providers' count. - honcho.md: fix unclosed quote in echo HONCHO_API_KEY snippet; document that 'hermes honcho' subcommand is gated on memory.provider=honcho; reconcile subcommand list with actual --help output. - memory-providers.md: legacy 'hermes honcho setup' redirect documented. Verified via 'npm run build' — site builds cleanly; broken-link count went from 149 to 146 (no regressions, fixed a few in passing). * docs: round 2 audit fixes + regenerate skill catalogs Follow-up to the previous commit on this branch: Round 2 manual fixes: - quickstart.md: KIMI_CODING_API_KEY mentioned alongside KIMI_API_KEY; voice-mode and ACP install commands rewritten — bare 'pip install ...' doesn't work for curl-installed setups (no pip on PATH, not in repo dir); replaced with 'cd ~/.hermes/hermes-agent && uv pip install -e ".[voice]"'. ACP already ships in [all] so the curl install includes it. - cli.md / configuration.md: 'auxiliary.compression.model' shown as 'google/gemini-3-flash-preview' (the doc's own claimed default); actual default is empty (= use main model). Reworded as 'leave empty (default) or pin a cheap model'. - built-in-plugins.md: added the bundled 'kanban/dashboard' plugin row that was missing from the table. Regenerated skill catalogs: - ran website/scripts/generate-skill-docs.py to refresh all 163 per-skill pages and both reference catalogs (skills-catalog.md, optional-skills-catalog.md). This adds the entries that were genuinely missing — productivity/teams-meeting-pipeline (bundled), optional/finance/* (entire category — 7 skills: 3-statement-model, comps-analysis, dcf-model, excel-author, lbo-model, merger-model, pptx-author), creative/hyperframes, creative/kanban-video-orchestrator, devops/watchers, productivity/shop-app, research/searxng-search, apple/macos-computer-use — and rewrites every other per-skill page from the current SKILL.md. Most diffs are tiny (one line of refreshed metadata). Validation: - 'npm run build' succeeded. - Broken-link count moved 146 -> 155 — the +9 are zh-Hans translation shells that lag every newly-added skill page (pre-existing pattern). No regressions on any en/ page.
363 lines
9.3 KiB
Markdown
363 lines
9.3 KiB
Markdown
---
|
|
title: "Modal Serverless Gpu — Serverless GPU cloud platform for running ML workloads"
|
|
sidebar_label: "Modal Serverless Gpu"
|
|
description: "Serverless GPU cloud platform for running ML workloads"
|
|
---
|
|
|
|
{/* This page is auto-generated from the skill's SKILL.md by website/scripts/generate-skill-docs.py. Edit the source SKILL.md, not this page. */}
|
|
|
|
# Modal Serverless Gpu
|
|
|
|
Serverless GPU cloud platform for running ML workloads. Use when you need on-demand GPU access without infrastructure management, deploying ML models as APIs, or running batch jobs with automatic scaling.
|
|
|
|
## Skill metadata
|
|
|
|
| | |
|
|
|---|---|
|
|
| Source | Optional — install with `hermes skills install official/mlops/modal` |
|
|
| Path | `optional-skills/mlops/modal` |
|
|
| Version | `1.0.0` |
|
|
| Author | Orchestra Research |
|
|
| License | MIT |
|
|
| Dependencies | `modal>=0.64.0` |
|
|
| Platforms | linux, macos, windows |
|
|
| Tags | `Infrastructure`, `Serverless`, `GPU`, `Cloud`, `Deployment`, `Modal` |
|
|
|
|
## Reference: full SKILL.md
|
|
|
|
:::info
|
|
The following is the complete skill definition that Hermes loads when this skill is triggered. This is what the agent sees as instructions when the skill is active.
|
|
:::
|
|
|
|
# Modal Serverless GPU
|
|
|
|
Comprehensive guide to running ML workloads on Modal's serverless GPU cloud platform.
|
|
|
|
## When to use Modal
|
|
|
|
**Use Modal when:**
|
|
- Running GPU-intensive ML workloads without managing infrastructure
|
|
- Deploying ML models as auto-scaling APIs
|
|
- Running batch processing jobs (training, inference, data processing)
|
|
- Need pay-per-second GPU pricing without idle costs
|
|
- Prototyping ML applications quickly
|
|
- Running scheduled jobs (cron-like workloads)
|
|
|
|
**Key features:**
|
|
- **Serverless GPUs**: T4, L4, A10G, L40S, A100, H100, H200, B200 on-demand
|
|
- **Python-native**: Define infrastructure in Python code, no YAML
|
|
- **Auto-scaling**: Scale to zero, scale to 100+ GPUs instantly
|
|
- **Sub-second cold starts**: Rust-based infrastructure for fast container launches
|
|
- **Container caching**: Image layers cached for rapid iteration
|
|
- **Web endpoints**: Deploy functions as REST APIs with zero-downtime updates
|
|
|
|
**Use alternatives instead:**
|
|
- **RunPod**: For longer-running pods with persistent state
|
|
- **Lambda Labs**: For reserved GPU instances
|
|
- **SkyPilot**: For multi-cloud orchestration and cost optimization
|
|
- **Kubernetes**: For complex multi-service architectures
|
|
|
|
## Quick start
|
|
|
|
### Installation
|
|
|
|
```bash
|
|
pip install modal
|
|
modal setup # Opens browser for authentication
|
|
```
|
|
|
|
### Hello World with GPU
|
|
|
|
```python
|
|
import modal
|
|
|
|
app = modal.App("hello-gpu")
|
|
|
|
@app.function(gpu="T4")
|
|
def gpu_info():
|
|
import subprocess
|
|
return subprocess.run(["nvidia-smi"], capture_output=True, text=True).stdout
|
|
|
|
@app.local_entrypoint()
|
|
def main():
|
|
print(gpu_info.remote())
|
|
```
|
|
|
|
Run: `modal run hello_gpu.py`
|
|
|
|
### Basic inference endpoint
|
|
|
|
```python
|
|
import modal
|
|
|
|
app = modal.App("text-generation")
|
|
image = modal.Image.debian_slim().pip_install("transformers", "torch", "accelerate")
|
|
|
|
@app.cls(gpu="A10G", image=image)
|
|
class TextGenerator:
|
|
@modal.enter()
|
|
def load_model(self):
|
|
from transformers import pipeline
|
|
self.pipe = pipeline("text-generation", model="gpt2", device=0)
|
|
|
|
@modal.method()
|
|
def generate(self, prompt: str) -> str:
|
|
return self.pipe(prompt, max_length=100)[0]["generated_text"]
|
|
|
|
@app.local_entrypoint()
|
|
def main():
|
|
print(TextGenerator().generate.remote("Hello, world"))
|
|
```
|
|
|
|
## Core concepts
|
|
|
|
### Key components
|
|
|
|
| Component | Purpose |
|
|
|-----------|---------|
|
|
| `App` | Container for functions and resources |
|
|
| `Function` | Serverless function with compute specs |
|
|
| `Cls` | Class-based functions with lifecycle hooks |
|
|
| `Image` | Container image definition |
|
|
| `Volume` | Persistent storage for models/data |
|
|
| `Secret` | Secure credential storage |
|
|
|
|
### Execution modes
|
|
|
|
| Command | Description |
|
|
|---------|-------------|
|
|
| `modal run script.py` | Execute and exit |
|
|
| `modal serve script.py` | Development with live reload |
|
|
| `modal deploy script.py` | Persistent cloud deployment |
|
|
|
|
## GPU configuration
|
|
|
|
### Available GPUs
|
|
|
|
| GPU | VRAM | Best For |
|
|
|-----|------|----------|
|
|
| `T4` | 16GB | Budget inference, small models |
|
|
| `L4` | 24GB | Inference, Ada Lovelace arch |
|
|
| `A10G` | 24GB | Training/inference, 3.3x faster than T4 |
|
|
| `L40S` | 48GB | Recommended for inference (best cost/perf) |
|
|
| `A100-40GB` | 40GB | Large model training |
|
|
| `A100-80GB` | 80GB | Very large models |
|
|
| `H100` | 80GB | Fastest, FP8 + Transformer Engine |
|
|
| `H200` | 141GB | Auto-upgrade from H100, 4.8TB/s bandwidth |
|
|
| `B200` | Latest | Blackwell architecture |
|
|
|
|
### GPU specification patterns
|
|
|
|
```python
|
|
# Single GPU
|
|
@app.function(gpu="A100")
|
|
|
|
# Specific memory variant
|
|
@app.function(gpu="A100-80GB")
|
|
|
|
# Multiple GPUs (up to 8)
|
|
@app.function(gpu="H100:4")
|
|
|
|
# GPU with fallbacks
|
|
@app.function(gpu=["H100", "A100", "L40S"])
|
|
|
|
# Any available GPU
|
|
@app.function(gpu="any")
|
|
```
|
|
|
|
## Container images
|
|
|
|
```python
|
|
# Basic image with pip
|
|
image = modal.Image.debian_slim(python_version="3.11").pip_install(
|
|
"torch==2.1.0", "transformers==4.36.0", "accelerate"
|
|
)
|
|
|
|
# From CUDA base
|
|
image = modal.Image.from_registry(
|
|
"nvidia/cuda:12.1.0-cudnn8-devel-ubuntu22.04",
|
|
add_python="3.11"
|
|
).pip_install("torch", "transformers")
|
|
|
|
# With system packages
|
|
image = modal.Image.debian_slim().apt_install("git", "ffmpeg").pip_install("whisper")
|
|
```
|
|
|
|
## Persistent storage
|
|
|
|
```python
|
|
volume = modal.Volume.from_name("model-cache", create_if_missing=True)
|
|
|
|
@app.function(gpu="A10G", volumes={"/models": volume})
|
|
def load_model():
|
|
import os
|
|
model_path = "/models/llama-7b"
|
|
if not os.path.exists(model_path):
|
|
model = download_model()
|
|
model.save_pretrained(model_path)
|
|
volume.commit() # Persist changes
|
|
return load_from_path(model_path)
|
|
```
|
|
|
|
## Web endpoints
|
|
|
|
### FastAPI endpoint decorator
|
|
|
|
```python
|
|
@app.function()
|
|
@modal.fastapi_endpoint(method="POST")
|
|
def predict(text: str) -> dict:
|
|
return {"result": model.predict(text)}
|
|
```
|
|
|
|
### Full ASGI app
|
|
|
|
```python
|
|
from fastapi import FastAPI
|
|
web_app = FastAPI()
|
|
|
|
@web_app.post("/predict")
|
|
async def predict(text: str):
|
|
return {"result": await model.predict.remote.aio(text)}
|
|
|
|
@app.function()
|
|
@modal.asgi_app()
|
|
def fastapi_app():
|
|
return web_app
|
|
```
|
|
|
|
### Web endpoint types
|
|
|
|
| Decorator | Use Case |
|
|
|-----------|----------|
|
|
| `@modal.fastapi_endpoint()` | Simple function → API |
|
|
| `@modal.asgi_app()` | Full FastAPI/Starlette apps |
|
|
| `@modal.wsgi_app()` | Django/Flask apps |
|
|
| `@modal.web_server(port)` | Arbitrary HTTP servers |
|
|
|
|
## Dynamic batching
|
|
|
|
```python
|
|
@app.function()
|
|
@modal.batched(max_batch_size=32, wait_ms=100)
|
|
async def batch_predict(inputs: list[str]) -> list[dict]:
|
|
# Inputs automatically batched
|
|
return model.batch_predict(inputs)
|
|
```
|
|
|
|
## Secrets management
|
|
|
|
```bash
|
|
# Create secret
|
|
modal secret create huggingface HF_TOKEN=hf_xxx
|
|
```
|
|
|
|
```python
|
|
@app.function(secrets=[modal.Secret.from_name("huggingface")])
|
|
def download_model():
|
|
import os
|
|
token = os.environ["HF_TOKEN"]
|
|
```
|
|
|
|
## Scheduling
|
|
|
|
```python
|
|
@app.function(schedule=modal.Cron("0 0 * * *")) # Daily midnight
|
|
def daily_job():
|
|
pass
|
|
|
|
@app.function(schedule=modal.Period(hours=1))
|
|
def hourly_job():
|
|
pass
|
|
```
|
|
|
|
## Performance optimization
|
|
|
|
### Cold start mitigation
|
|
|
|
```python
|
|
@app.function(
|
|
container_idle_timeout=300, # Keep warm 5 min
|
|
allow_concurrent_inputs=10, # Handle concurrent requests
|
|
)
|
|
def inference():
|
|
pass
|
|
```
|
|
|
|
### Model loading best practices
|
|
|
|
```python
|
|
@app.cls(gpu="A100")
|
|
class Model:
|
|
@modal.enter() # Run once at container start
|
|
def load(self):
|
|
self.model = load_model() # Load during warm-up
|
|
|
|
@modal.method()
|
|
def predict(self, x):
|
|
return self.model(x)
|
|
```
|
|
|
|
## Parallel processing
|
|
|
|
```python
|
|
@app.function()
|
|
def process_item(item):
|
|
return expensive_computation(item)
|
|
|
|
@app.function()
|
|
def run_parallel():
|
|
items = list(range(1000))
|
|
# Fan out to parallel containers
|
|
results = list(process_item.map(items))
|
|
return results
|
|
```
|
|
|
|
## Common configuration
|
|
|
|
```python
|
|
@app.function(
|
|
gpu="A100",
|
|
memory=32768, # 32GB RAM
|
|
cpu=4, # 4 CPU cores
|
|
timeout=3600, # 1 hour max
|
|
container_idle_timeout=120,# Keep warm 2 min
|
|
retries=3, # Retry on failure
|
|
concurrency_limit=10, # Max concurrent containers
|
|
)
|
|
def my_function():
|
|
pass
|
|
```
|
|
|
|
## Debugging
|
|
|
|
```python
|
|
# Test locally
|
|
if __name__ == "__main__":
|
|
result = my_function.local()
|
|
|
|
# View logs
|
|
# modal app logs my-app
|
|
```
|
|
|
|
## Common issues
|
|
|
|
| Issue | Solution |
|
|
|-------|----------|
|
|
| Cold start latency | Increase `container_idle_timeout`, use `@modal.enter()` |
|
|
| GPU OOM | Use larger GPU (`A100-80GB`), enable gradient checkpointing |
|
|
| Image build fails | Pin dependency versions, check CUDA compatibility |
|
|
| Timeout errors | Increase `timeout`, add checkpointing |
|
|
|
|
## References
|
|
|
|
- **[Advanced Usage](https://github.com/NousResearch/hermes-agent/blob/main/optional-skills/mlops/modal/references/advanced-usage.md)** - Multi-GPU, distributed training, cost optimization
|
|
- **[Troubleshooting](https://github.com/NousResearch/hermes-agent/blob/main/optional-skills/mlops/modal/references/troubleshooting.md)** - Common issues and solutions
|
|
|
|
## Resources
|
|
|
|
- **Documentation**: https://modal.com/docs
|
|
- **Examples**: https://github.com/modal-labs/modal-examples
|
|
- **Pricing**: https://modal.com/pricing
|
|
- **Discord**: https://discord.gg/modal
|