diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..9ae771c --- /dev/null +++ b/.env.example @@ -0,0 +1,56 @@ +# Copy to `.env` and fill in the values you need: +# +# cp .env.example .env +# +# `.env` is git-ignored. `launch_agent.py` and `launch_parallel_agents.py` call +# `load_dotenv()`, so everything below is picked up automatically by those two +# entrypoints (including Hydra `${oc.env:...}` interpolation in the configs). + + +# --- Model providers --------------------------------------------------------- + +# Key for the OpenAI-compatible endpoint used by config/agent/GPT-5.5-*.yaml +# (`api_key: ${oc.env:GPT55_API_KEY}`). +GPT55_API_KEY= + +# Any variable name works -- point an agent's `api_key` at the one you use: +# uv run launch_agent.py agent=GPT-5.5-computer-use \ +# 'agent.api_key=${oc.env:OPENAI_API_KEY}' +# OPENAI_API_KEY= + +# Bedrock, for config/agent/claude_4_sonnet.yaml (`client_type: aws`). The +# config leaves the credentials null, so the Anthropic SDK falls back to these. +# AWS_ACCESS_KEY_ID= +# AWS_SECRET_ACCESS_KEY= +# AWS_SESSION_TOKEN= +# AWS_REGION=us-west-2 + +# Self-hosted models need no key -- point the agent at the serving host instead: +# uv run launch_agent.py agent=Qwen3.6-27B-computer-use \ +# agent.hostname= agent.port=8000 + + +# --- Experiment tracking ----------------------------------------------------- + +# `wandb.entity` and `logs_dir` interpolate ${oc.env:USER}; your shell already +# sets USER, override it here only if the W&B account differs from the login. +# USER= + +# WANDB_API_KEY= +# WANDB_BASE_URL= # only for a self-hosted W&B server +# WANDB_MODE=offline # skip online logging entirely + + +# --- Shell-level variables --------------------------------------------------- +# +# NOT read from this file: `scripts/conduct.sh` and `scripts/conduct_slurm.sh` +# are shell scripts, and `src/open_apps/mcp/` is launched separately, so none of +# them go through `load_dotenv()`. Export them at the call site: +# +# AGENTS="dummy" COUNT=4 MAX_PARALLEL=2 ./scripts/conduct.sh +# VLLM_HOST= AGENTS=gemma-4-computer-use COUNT=1 sbatch scripts/conduct_slurm.sh +# uv run python -m open_apps.mcp --app todo --host 127.0.0.1 --port 8000 +# +# ...or source this file into the shell first, if you prefer keeping them here: +# +# set -a; source .env; set +a diff --git a/README.md b/README.md index db94044..db7f937 100644 --- a/README.md +++ b/README.md @@ -64,8 +64,8 @@ Launch an agent to perform a task of *adding a meeting with Dennis to the calend ``` -# export OPENAI_API_KEY="" -uv run launch_agent.py agent=GPT-5-1 task_name=add_meeting_with_dennis +# export GPT55_API_KEY="" +uv run launch_agent.py agent=GPT-5.5-computer-use task_name=add_meeting_with_dennis ``` To see the agent solving the task live, add the headless argument: @@ -79,6 +79,114 @@ You can specify the agent of your choice with the `agent=` argument. For example Learn more about launching with OpenAI, Claude, and VLLM models such as UI-Tars in our [docs](https://facebookresearch.github.io/OpenApps/). +## Environment variables + +Copy [`.env.example`](.env.example) and fill in what you need — `launch_agent.py` and +`launch_parallel_agents.py` call `load_dotenv()`, so a `.env` at the repo root is picked up +automatically, and `.env` is git-ignored so keys stay out of the configs: + +```bash +cp .env.example .env +``` + +| Variable | Read by | Purpose | +| --- | --- | --- | +| `USER` | `config/config*.yaml`, `config/mode/*` | W&B `entity` and the `logs_dir` path | +| `GPT55_API_KEY` | `config/agent/GPT-5.5-*.yaml` | key for the OpenAI-compatible endpoint | +| `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `AWS_SESSION_TOKEN` | `config/agent/claude_4_sonnet.yaml` (`client_type: aws`) | Bedrock credentials, when left null in the config | +| `WANDB_API_KEY`, `WANDB_BASE_URL`, `WANDB_MODE` | `wandb` | auth, self-hosted server, and `WANDB_MODE=offline` to skip online logging | + +Agent API keys are read through Hydra interpolation, so any variable name works — point the +agent's `api_key` at the one you use: + +```bash +uv run launch_agent.py agent=GPT-5.5-computer-use 'agent.api_key=${oc.env:OPENAI_API_KEY}' +``` + +The batch scripts and the MCP server take environment variables too, but they are read by +the shell (or by an MCP client's `env` block), **not** through `.env` — export them at the +call site, or `set -a; source .env; set +a` first: + +| Variable | Read by | Default | +| --- | --- | --- | +| `AGENTS` | `scripts/conduct.sh` | `dummy` — space-separated `config/agent/` stems, used round-robin | +| `COUNT` | `scripts/conduct.sh` | number of agents — total runs to launch | +| `MAX_PARALLEL` | `scripts/conduct.sh` | `4` — concurrent runs | +| `HEADLESS` | `scripts/conduct.sh` | `True` | +| `LOG_DIR`, `WANDB_GROUP` | `scripts/conduct.sh` | `log_outputs`, `batch-` | +| `VLLM_MODEL`, `VLLM_PORT` | `scripts/conduct_slurm.sh` | the `served_model_name` and port to look for | +| `VLLM_HOST` | `scripts/conduct_slurm.sh` | unset — pin a node to skip auto-discovery | +| `OPENAPPS_APP`, `OPENAPPS_MCP_HOST`, `OPENAPPS_MCP_PORT` | `src/open_apps/mcp/server.py` | `todo`, `127.0.0.1`, `8000` — also settable as `python -m open_apps.mcp --app/--host/--port` | + +## Running on SLURM + +`config/mode/slurm_cluster.yaml` ships with placeholder values (`logs_dir: /example/dir`, +`slurm_account: example_replace_me`, …) that `sbatch` will reject. Rather than editing it and +risking committing your site's paths and account names, `.gitignore` carries an `internal-*` +rule: **any file named `internal-*` stays untracked**. The convention is to keep a private +twin next to the public one: + +```bash +cp config/mode/slurm_cluster.yaml config/mode/internal-slurm_cluster.yaml +``` + +```yaml +# config/mode/internal-slurm_cluster.yaml (untracked) +# @package _global_ +project: open_apps + +logs_dir: /your/checkpoint/path/${oc.env:USER}/logs/${project}/${now:%Y-%m-%d_%H-%M-%S}-${agent.model_name}/${job_id} +databases_dir: ${logs_dir}/databases + +cluster: slurm + +slurm_sweep_launcher: + gpus_per_node: 0 + nodes: 1 + tasks_per_node: 1 + cpus_per_task: 2 + timeout_min: 400 + slurm_account: your_account + slurm_qos: your_qos + slurm_partition: your_partition + mem_gb: 10 + slurm_srun_args: ["-vv", "--cpu-bind", "none"] + slurm_comment: "parallel agent tasks" +``` + +Select it like any other Hydra mode: + +```bash +uv run launch_parallel_agents.py mode=internal-slurm_cluster agent=dummy \ + tasks=longer_horizon parallel_tasks.task_names=all use_wandb=True +``` + +The same pattern applies elsewhere — e.g. `docs/internal-notes.md` for cluster-specific +instructions alongside the public `docs/`. + +For a self-hosted model, serve it on a GPU node and point the agent at that host: + +```bash +# on the GPU node +vllm serve --host 0.0.0.0 --port 8000 + +# from anywhere on the cluster +uv run launch_agent.py agent=Qwen3.6-27B-computer-use agent.hostname= agent.port=8000 +``` + +`scripts/conduct_slurm.sh` automates that last step: it requests a CPU allocation, probes +your running jobs (`squeue --me`) for a node serving `$VLLM_MODEL` on `$VLLM_PORT`, and runs +the worker pool against it. Override the account/QOS/partition at submit time instead of +editing the `#SBATCH` placeholders: + +```bash +AGENTS=gemma-4-computer-use COUNT=1 \ + sbatch --account=... --qos=... --partition=... scripts/conduct_slurm.sh +``` + +See the [agents docs](https://facebookresearch.github.io/OpenApps/agents/) for the full +cluster walkthrough. + ## OpenApps in action