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
56 changes: 56 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -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=<node> 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=<node> 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
112 changes: 110 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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/<name>` 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-<timestamp>` |
| `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 <model> --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=<node> 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


Expand Down
Loading