You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tooling.apache.org by gs...@apache.org on 2026/08/26 17:56:58 UTC

(tooling-llmao) branch main updated: Move vLLM set placement into config.yaml fleet.sets.

This is an automated email from the ASF dual-hosted git repository.

gstein pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tooling-llmao.git


The following commit(s) were added to refs/heads/main by this push:
     new 7a607c2  Move vLLM set placement into config.yaml fleet.sets.
7a607c2 is described below

commit 7a607c2927b0fd22615b728d57863e4f925af06d
Author: Greg Stein <gs...@gmail.com>
AuthorDate: Wed Aug 26 12:56:51 2026 -0500

    Move vLLM set placement into config.yaml fleet.sets.
    
    Catalog models no longer carry model_set or vllm.port. Each set is a
    list of processes (model, host, port) so the same weights can run
    more than once. GET /vllm/config joins that list to catalog vLLM args.
    
    Co-authored-by: Grok <gr...@x.ai>
---
 config.yaml.example       |  11 ++++-
 docs/vllm-fleet-design.md |  38 ++++++++-------
 hosting/README.md         |   4 +-
 hosting/vast/README.md    |   4 +-
 hosting/vast/env.py       |  12 ++---
 llmao/fleet.py            | 121 ++++++++++++++++++++++++++++++++++++++--------
 model_list.yaml.example   |   4 --
 tests/test_fleet.py       |  78 ++++++++++++++++++++++++++++--
 tests/test_vast_env.py    |  16 ++++--
 9 files changed, 226 insertions(+), 62 deletions(-)

diff --git a/config.yaml.example b/config.yaml.example
index 8ba6642..35a8a6c 100644
--- a/config.yaml.example
+++ b/config.yaml.example
@@ -41,6 +41,15 @@ site_admins: []
 models_path: model_list.yaml
 
 # Shared secret GPU boxes present as Authorization: Bearer when fetching
-# GET /vllm/config/<model_set>. Same value as box env FLEET_KEY.
+# GET /vllm/config/<set_id>. Same value as box env FLEET_KEY.
+# Placement (host/port per vLLM process) lives here, not on model_list.
 fleet:
   key: CHANGE_ME_FLEET_KEY
+  sets:
+    primary:
+      - model: gemma4-26b
+        host: 127.0.0.1
+        port: 8001
+      - model: qwen3-8b
+        host: 127.0.0.1
+        port: 8003
diff --git a/docs/vllm-fleet-design.md b/docs/vllm-fleet-design.md
index d0a242c..e5c7a9c 100644
--- a/docs/vllm-fleet-design.md
+++ b/docs/vllm-fleet-design.md
@@ -58,8 +58,8 @@ flowchart TB
   - The real API keys used between LiteLLM and each vLLM server
   - Logical **sets** (groupings of models that should run together on one GPU box)
 - Each GPU box receives only two pieces of bootstrap information: the **fleet key** and a **set identifier**.
-- At box-start, the provider installer fetches JSON for its `model_set` and
-  installs native process units (Vast: Supervisor). There is no on-disk
+- At box-start, the provider installer fetches JSON for its set id (`VLLM_SET`)
+  and installs native process units (Vast: Supervisor). There is no on-disk
   `servers.yaml` and no Python process manager.
 
 ---
@@ -75,20 +75,24 @@ flowchart TB
 
 ### 3.2 Sets (Groupings)
 
-A **set** is a named collection of models that are intended to run on the same GPU instance.
+A **set** is a named collection of **vLLM processes** that share a GPU instance
+(and the box env `VLLM_SET`). The same catalog model may appear in many sets
+and more than once in a set.
 
 Examples:
-- `box-a` → model-a + model-b
-- `box-b` → model-c + model-d + model-e
-- `coding` / `heavy` / `fast` (semantic names are also fine)
+- `primary` → gemma4-26b @ host:8001 + qwen3-8b @ host:8003
+- `overflow` → gemma4-26b again on another host
 
-asfquart owns the mapping from set → list of models (plus ports, vLLM arguments, etc.). Changing which models run where is a control-plane change only; GPU templates stay identical.
+asfquart owns placement in `config.yaml` → `fleet.sets` (model + host + port
+per process). `model_list.yaml` is how to serve (HF id, vLLM args, api_key),
+not where. Changing placement is a control-plane change only; GPU templates
+stay identical.
 
 ### 3.3 Set config JSON
 
-asfquart builds JSON from `model_list.yaml` (entries whose `model_info.model_set`
-matches the requested set). Vast `install_set.py` fetches it at box-start
-and writes Supervisor programs. Same payload; never a `servers.yaml`.
+asfquart builds JSON from `fleet.sets.<id>` joined to the catalog. Vast
+`install_set.py` fetches it at box-start and writes Supervisor programs.
+Same payload; never a `servers.yaml`.
 
 ---
 
@@ -102,8 +106,8 @@ Response: 200 application/json
 ```
 
 - asfquart validates the fleet key (not OAuth).
-- Filters `model_list.yaml` by `model_info.model_set`.
-- Emits JSON (models, ports, args, API keys).
+- Looks up `fleet.sets.<set_id>` and joins catalog vLLM args.
+- Emits JSON (models, host, ports, args, API keys).
 - Optional future hardening: bind the request to a known instance ID / label.
 
 ---
@@ -119,6 +123,7 @@ Response: 200 application/json
     {
       "name": "model-a",
       "model": "org/model-name",
+      "host": "127.0.0.1",
       "port": 8000,
       "api_key": "sk-...",
       "gpu_memory_utilization": 0.42,
@@ -129,12 +134,9 @@ Response: 200 application/json
 }
 ```
 
-Inventory fields (under each `model_list` entry’s `model_info`, so LiteLLM
-`Deployment` still accepts extras):
-
-- `model_set` — box env `VLLM_SET`
-- `vllm.model` / `vllm.port` / optional util, max len, `args`
-- `api_key` comes from `litellm_params` (same secret LiteLLM presents)
+Catalog (`model_list` `model_info.vllm`): HF `model`, optional util, max len, `args`.
+Placement (`config.yaml` `fleet.sets`): `model` (catalog name), `host`, `port`, optional `name`.
+`api_key` comes from `litellm_params` (same secret LiteLLM presents).
 
 Notes:
 - `args` may be a list of strings or a single string; the installer normalises either form.
diff --git a/hosting/README.md b/hosting/README.md
index 6ebda18..5fa142a 100644
--- a/hosting/README.md
+++ b/hosting/README.md
@@ -6,7 +6,7 @@ are not part of the asfquart process.
 **Design (why):** [`docs/vllm-fleet-design.md`](../docs/vllm-fleet-design.md).
 
 **Control plane (all providers):** asfquart serves `GET /vllm/config/{set_id}`
-as JSON from `model_list.yaml` for that `model_set`. Bearer fleet key.
+as JSON from `config.yaml` `fleet.sets` plus the model catalog. Bearer fleet key.
 
 Provider-specific: how that JSON becomes running vLLM. Vast writes
 Supervisor units at box-start. There is no long-lived Python launcher.
@@ -25,7 +25,7 @@ Supervisor units at box-start. There is no long-lived Python launcher.
 Required:
 
 - `FLEET_KEY` — shared secret; `Authorization: Bearer`
-- `VLLM_SET` — `model_set` id from `model_list.yaml`
+- `VLLM_SET` — set id under `fleet.sets` in `config.yaml`
 - `ASFQUART_URL` — origin only, e.g. `https://llm.apache.org:8443`
 
 Optional:
diff --git a/hosting/vast/README.md b/hosting/vast/README.md
index 4af3d99..ba46479 100644
--- a/hosting/vast/README.md
+++ b/hosting/vast/README.md
@@ -15,7 +15,7 @@ no Python process manager.
 
   ```bash
   FLEET_KEY=<shared-secret>
-  VLLM_SET=<model_set>
+  VLLM_SET=<set id from config.yaml fleet.sets>
   ASFQUART_URL=https://llm.apache.org:8443
   ```
 
@@ -48,7 +48,7 @@ python3 hosting/vast/env.py set INSTANCE_ID --vllm-set primary
 
 `show` prints account secrets, template docker env, and instance `extra_env` with real values (the CLI masks them).
 
-`set` reads `fleet.key` from repo `config.yaml`, checks `--vllm-set` exists in `model_list.yaml`, prints the models that box will fetch from llm.apache.org, writes `FLEET_KEY` + `VLLM_SET` as Docker create options (`-e …`, via `args` — instance `extra_env` does not enter the container), then asks `Ready to reboot? [Y/n]` so on-start re-runs `install_set.py`.
+`set` reads `fleet.key` from repo `config.yaml`, checks `--vllm-set` exists in `config.yaml` `fleet.sets`, prints the models that box will fetch from llm.apache.org, writes `FLEET_KEY` + `VLLM_SET` as Docker create options (`-e …`, via `args` — instance `extra_env` does not enter the container), then asks `Ready to reboot? [Y/n]` so on-start re-runs `install_set.py`.
 
 ## Smoke
 
diff --git a/hosting/vast/env.py b/hosting/vast/env.py
index deb1369..ef14322 100755
--- a/hosting/vast/env.py
+++ b/hosting/vast/env.py
@@ -45,7 +45,7 @@ if str(REPO) not in sys.path:
     sys.path.insert(0, str(REPO))
 
 from llmao.fleet import UnknownSet, config_for_set  # noqa: E402
-from llmao.models import load_model_list, models_path_from_cfg  # noqa: E402
+from llmao.models import load_model_list  # noqa: E402
 
 _LOGGER = logging.getLogger(__name__)
 
@@ -267,11 +267,11 @@ def print_set_plan(set_id: str, cfg: edict) -> None:
     try:
         payload = config_for_set(set_id, entries=load_model_list(cfg=cfg), cfg=cfg)
     except UnknownSet:
-        path = models_path_from_cfg(cfg)
-        raise SystemExit(f"unknown model_set {set_id!r} (not in {path})") from None
-    print(f"set {set_id} will install (from model_list.yaml / GET /vllm/config/{set_id}):")
+        raise SystemExit(f"unknown model_set {set_id!r} (not in config.yaml fleet.sets)") from None
+    print(f"set {set_id} will install (from fleet.sets / GET /vllm/config/{set_id}):")
     for srv in payload["servers"]:
-        print(f"  {srv['name']}\t{srv['model']}\t:{srv['port']}")
+        host = srv.get("host") or ""
+        print(f"  {srv['name']}\t{srv['model']}\t{host}:{srv['port']}")
 
 
 def confirm_reboot() -> bool:
@@ -340,7 +340,7 @@ def build_parser() -> argparse.ArgumentParser:
 
     setp = sub.add_parser("set", help="set FLEET_KEY (from config.yaml) and VLLM_SET")
     setp.add_argument("instance_id", type=int)
-    setp.add_argument("--vllm-set", required=True, help="model_set id from model_list.yaml")
+    setp.add_argument("--vllm-set", required=True, help="set id from config.yaml fleet.sets")
     setp.add_argument(
         "--config",
         type=Path,
diff --git a/llmao/fleet.py b/llmao/fleet.py
index a2ddca7..f686d4d 100644
--- a/llmao/fleet.py
+++ b/llmao/fleet.py
@@ -1,4 +1,4 @@
-"""Build a vLLM set config JSON from model_list.yaml.
+"""Build a vLLM set config JSON from config.yaml fleet.sets + model_list.
 
 GPU boxes fetch this JSON at provision/install time (no servers.yaml).
 """
@@ -10,7 +10,7 @@ from llmao.models import load_model_list
 
 
 class UnknownSet(KeyError):
-    """No model_list entry has this model_set."""
+    """No fleet.sets entry for this set id."""
 
 
 def _vllm_block(entry: dict[str, Any]) -> dict[str, Any]:
@@ -23,22 +23,64 @@ def _vllm_block(entry: dict[str, Any]) -> dict[str, Any]:
     return block
 
 
-def _server_from_entry(entry: dict[str, Any]) -> dict[str, Any]:
-    name = entry.get("model_name")
-    if not name:
-        raise ValueError("model_list entry missing model_name")
+def _mapping(obj: Any) -> dict[str, Any] | None:
+    if obj is None:
+        return None
+    if isinstance(obj, dict):
+        return obj
+    if hasattr(obj, "items"):
+        return dict(obj.items())
+    return None
+
+
+def _sets_from_cfg(cfg: Any) -> dict[str, Any]:
+    if cfg is None:
+        raise ValueError("config_for_set requires cfg (fleet.sets)")
+    fleet = getattr(cfg, "fleet", None)
+    if fleet is None and isinstance(cfg, dict):
+        fleet = cfg.get("fleet")
+    raw = None
+    if fleet is not None:
+        raw = fleet.get("sets") if hasattr(fleet, "get") else getattr(fleet, "sets", None)
+    sets = _mapping(raw)
+    if not sets:
+        return {}
+    return {str(k): v for k, v in sets.items()}
+
+
+def _catalog_by_name(entries: list[dict[str, Any]]) -> dict[str, dict[str, Any]]:
+    out: dict[str, dict[str, Any]] = {}
+    for entry in entries:
+        name = entry.get("model_name")
+        if not name:
+            raise ValueError("model_list entry missing model_name")
+        key = str(name)
+        if key in out:
+            raise ValueError(f"duplicate model_name in model_list: {key}")
+        out[key] = entry
+    return out
+
+
+def _server_from_catalog(
+    entry: dict[str, Any],
+    *,
+    name: str,
+    host: str,
+    port: int,
+) -> dict[str, Any]:
+    catalog = entry.get("model_name")
     params = entry.get("litellm_params") or {}
     api_key = params.get("api_key")
     if not api_key:
-        raise ValueError(f"{name}: litellm_params.api_key is required")
+        raise ValueError(f"{catalog}: litellm_params.api_key is required")
     vllm = _vllm_block(entry)
     model = vllm.get("model")
-    port = vllm.get("port")
-    if not model or port is None:
-        raise ValueError(f"{name}: model_info.vllm needs model and port")
+    if not model:
+        raise ValueError(f"{catalog}: model_info.vllm needs model")
     server: dict[str, Any] = {
         "name": str(name),
         "model": str(model),
+        "host": str(host),
         "port": int(port),
         "api_key": str(api_key),
     }
@@ -53,19 +95,60 @@ def _server_from_entry(entry: dict[str, Any]) -> dict[str, Any]:
     return server
 
 
+def _set_item(item: Any, index: int) -> dict[str, Any]:
+    row = _mapping(item)
+    if row is None:
+        raise ValueError(f"fleet.sets item {index} must be a mapping with model, host, port")
+    model = str(row.get("model") or "").strip()
+    host = str(row.get("host") or "").strip()
+    port = row.get("port")
+    if not model or not host or port is None or str(port).strip() == "":
+        raise ValueError(f"fleet.sets item {index} needs model, host, and port")
+    name = str(row.get("name") or model).strip()
+    if not name:
+        raise ValueError(f"fleet.sets item {index} has empty name")
+    return {"model": model, "host": host, "port": int(port), "name": name}
+
+
 def config_for_set(set_id: str, *, entries: list[dict[str, Any]] | None = None, cfg: Any = None) -> dict[str, Any]:
-    """JSON object for one model_set. Raises UnknownSet if empty."""
+    """JSON object for one set. Raises UnknownSet if the id is missing."""
     if not set_id:
         raise UnknownSet(set_id)
-    rows = entries if entries is not None else load_model_list(cfg=cfg)
-    servers = []
-    for entry in rows:
-        info = entry.get("model_info") or {}
-        if str(info.get("model_set") or "") != set_id:
-            continue
-        servers.append(_server_from_entry(entry))
-    if not servers:
+    sets = _sets_from_cfg(cfg)
+    if set_id not in sets:
+        raise UnknownSet(set_id)
+    items = sets[set_id]
+    if items is None:
         raise UnknownSet(set_id)
+    if hasattr(items, "values") and not isinstance(items, (list, tuple, str)):
+        raise ValueError(f"fleet.sets.{set_id} must be a list of servers")
+    rows = list(items)
+    if not rows:
+        raise UnknownSet(set_id)
+
+    catalog = _catalog_by_name(entries if entries is not None else load_model_list(cfg=cfg))
+    servers = []
+    names: set[str] = set()
+    places: set[tuple[str, int]] = set()
+    for i, raw in enumerate(rows):
+        spec = _set_item(raw, i)
+        entry = catalog.get(spec["model"])
+        if entry is None:
+            raise ValueError(f"fleet.sets.{set_id}[{i}]: unknown model {spec['model']!r}")
+        if spec["name"] in names:
+            raise ValueError(f"fleet.sets.{set_id}: duplicate name {spec['name']!r}")
+        place = (spec["host"], spec["port"])
+        if place in places:
+            raise ValueError(
+                f"fleet.sets.{set_id}: duplicate {spec['host']}:{spec['port']}"
+            )
+        names.add(spec["name"])
+        places.add(place)
+        servers.append(
+            _server_from_catalog(
+                entry, name=spec["name"], host=spec["host"], port=spec["port"]
+            )
+        )
     return {
         "set_id": set_id,
         "hf_home": "/workspace/hf-cache",
diff --git a/model_list.yaml.example b/model_list.yaml.example
index ae52b76..8de1f44 100644
--- a/model_list.yaml.example
+++ b/model_list.yaml.example
@@ -14,10 +14,8 @@ model_list:
       api_base: http://127.0.0.1:8001
       api_key: CHANGE_ME_SELFHOST_API_KEY
     model_info:
-      model_set: primary
       vllm:
         model: google/gemma-4-26B-A4B-it
-        port: 8001
         gpu_memory_utilization: 0.55
         args: []
       display_name: "Gemma 4 26B-A4B (self-hosted, multimodal)"
@@ -42,10 +40,8 @@ model_list:
       api_base: http://127.0.0.1:8003
       api_key: CHANGE_ME_SELFHOST_API_KEY
     model_info:
-      model_set: primary
       vllm:
         model: Qwen/Qwen3-8B-FP8
-        port: 8003
         gpu_memory_utilization: 0.40
         args:
           - --reasoning-parser
diff --git a/tests/test_fleet.py b/tests/test_fleet.py
index f6b56e3..5f037d5 100644
--- a/tests/test_fleet.py
+++ b/tests/test_fleet.py
@@ -1,22 +1,34 @@
-"""config_for_set: JSON for a model_set from model_list entries."""
+"""config_for_set: JSON for a set from config.yaml fleet.sets + catalog."""
 from pathlib import Path
 
 import pytest
+import yaml
+from easydict import EasyDict as edict
 
 from llmao.fleet import UnknownSet, config_for_set
 from llmao.models import load_model_list
 
 EXAMPLE = Path(__file__).resolve().parent.parent / "model_list.yaml.example"
+EXAMPLE_CFG = Path(__file__).resolve().parent.parent / "config.yaml.example"
+
+
+def _cfg(sets, entries_path=EXAMPLE):
+    return edict({
+        "fleet": {"sets": sets},
+        "models_path": str(entries_path),
+    })
 
 
 def test_example_primary_set():
-    payload = config_for_set("primary", entries=load_model_list(EXAMPLE))
+    cfg = edict(yaml.safe_load(EXAMPLE_CFG.read_text(encoding="utf-8")))
+    payload = config_for_set("primary", entries=load_model_list(EXAMPLE), cfg=cfg)
     assert payload["set_id"] == "primary"
     names = [s["name"] for s in payload["servers"]]
     assert "gemma4-26b" in names
     assert "qwen3-8b" in names
     gemma = next(s for s in payload["servers"] if s["name"] == "gemma4-26b")
     assert gemma["model"] == "google/gemma-4-26B-A4B-it"
+    assert gemma["host"] == "127.0.0.1"
     assert gemma["port"] == 8001
     assert gemma["api_key"]
     qwen = next(s for s in payload["servers"] if s["name"] == "qwen3-8b")
@@ -24,15 +36,71 @@ def test_example_primary_set():
 
 
 def test_unknown_set():
+    cfg = _cfg({"primary": [{"model": "gemma4-26b", "host": "127.0.0.1", "port": 8001}]})
     with pytest.raises(UnknownSet):
-        config_for_set("no-such-set", entries=load_model_list(EXAMPLE))
+        config_for_set("no-such-set", entries=load_model_list(EXAMPLE), cfg=cfg)
 
 
 def test_missing_vllm_block():
     entries = [{
         "model_name": "x",
         "litellm_params": {"api_key": "sk-x"},
-        "model_info": {"model_set": "s"},
+        "model_info": {},
     }]
+    cfg = _cfg({"s": [{"model": "x", "host": "10.0.0.1", "port": 9}]})
     with pytest.raises(ValueError, match="model_info.vllm"):
-        config_for_set("s", entries=entries)
+        config_for_set("s", entries=entries, cfg=cfg)
+
+
+def test_unknown_catalog_model():
+    cfg = _cfg({"s": [{"model": "nope", "host": "10.0.0.1", "port": 9}]})
+    with pytest.raises(ValueError, match="unknown model"):
+        config_for_set("s", entries=load_model_list(EXAMPLE), cfg=cfg)
+
+
+def test_missing_host():
+    cfg = _cfg({"s": [{"model": "gemma4-26b", "port": 8001}]})
+    with pytest.raises(ValueError, match="model, host, and port"):
+        config_for_set("s", entries=load_model_list(EXAMPLE), cfg=cfg)
+
+
+def test_two_copies_different_ports():
+    cfg = _cfg({
+        "dual": [
+            {"model": "gemma4-26b", "host": "10.0.0.1", "port": 8001},
+            {"model": "gemma4-26b", "host": "10.0.0.1", "port": 8011, "name": "gemma4-26b-b"},
+        ]
+    })
+    payload = config_for_set("dual", entries=load_model_list(EXAMPLE), cfg=cfg)
+    assert [s["name"] for s in payload["servers"]] == ["gemma4-26b", "gemma4-26b-b"]
+    assert [s["port"] for s in payload["servers"]] == [8001, 8011]
+    assert payload["servers"][0]["model"] == payload["servers"][1]["model"]
+
+
+def test_duplicate_name():
+    cfg = _cfg({
+        "s": [
+            {"model": "gemma4-26b", "host": "10.0.0.1", "port": 1},
+            {"model": "gemma4-26b", "host": "10.0.0.1", "port": 2},
+        ]
+    })
+    with pytest.raises(ValueError, match="duplicate name"):
+        config_for_set("s", entries=load_model_list(EXAMPLE), cfg=cfg)
+
+
+def test_duplicate_host_port():
+    cfg = _cfg({
+        "s": [
+            {"model": "gemma4-26b", "host": "10.0.0.1", "port": 8001, "name": "a"},
+            {"model": "qwen3-8b", "host": "10.0.0.1", "port": 8001, "name": "b"},
+        ]
+    })
+    with pytest.raises(ValueError, match="duplicate"):
+        config_for_set("s", entries=load_model_list(EXAMPLE), cfg=cfg)
+
+
+def test_catalog_has_no_port():
+    for entry in load_model_list(EXAMPLE):
+        vllm = (entry.get("model_info") or {}).get("vllm") or {}
+        assert "port" not in vllm
+        assert "model_set" not in (entry.get("model_info") or {})
diff --git a/tests/test_vast_env.py b/tests/test_vast_env.py
index fe7015d..ec6d16d 100644
--- a/tests/test_vast_env.py
+++ b/tests/test_vast_env.py
@@ -21,6 +21,12 @@ sys.path.insert(0, str(VAST))
 import env as vast_env  # noqa: E402
 
 EXAMPLE_MODELS = Path(__file__).resolve().parent.parent / "model_list.yaml.example"
+PRIMARY_SETS = {
+    "primary": [
+        {"model": "gemma4-26b", "host": "127.0.0.1", "port": 8001},
+        {"model": "qwen3-8b", "host": "127.0.0.1", "port": 8003},
+    ]
+}
 
 
 def test_parse_docker_env_string():
@@ -38,7 +44,7 @@ def test_parse_docker_env_dict():
 
 def test_merge_keeps_port_keys(tmp_path, monkeypatch, capsys):
     cfg = {
-        "fleet": {"key": "sk-fleet"},
+        "fleet": {"key": "sk-fleet", "sets": PRIMARY_SETS},
         "models_path": str(EXAMPLE_MODELS),
     }
     cfg_path = tmp_path / "config.yaml"
@@ -71,7 +77,7 @@ def test_merge_keeps_port_keys(tmp_path, monkeypatch, capsys):
 
 def test_unknown_set_fails(tmp_path):
     cfg = {
-        "fleet": {"key": "sk-fleet"},
+        "fleet": {"key": "sk-fleet", "sets": PRIMARY_SETS},
         "models_path": str(EXAMPLE_MODELS),
     }
     cfg_path = tmp_path / "config.yaml"
@@ -100,7 +106,7 @@ def test_vllm_set_required():
 
 def test_reboot_yes(tmp_path, monkeypatch):
     cfg = {
-        "fleet": {"key": "sk-fleet"},
+        "fleet": {"key": "sk-fleet", "sets": PRIMARY_SETS},
         "models_path": str(EXAMPLE_MODELS),
     }
     cfg_path = tmp_path / "config.yaml"
@@ -169,7 +175,7 @@ def test_default_command_is_list():
 
 def test_set_requires_image(tmp_path):
     cfg = {
-        "fleet": {"key": "sk-fleet"},
+        "fleet": {"key": "sk-fleet", "sets": PRIMARY_SETS},
         "models_path": str(EXAMPLE_MODELS),
     }
     cfg_path = tmp_path / "config.yaml"
@@ -183,7 +189,7 @@ def test_set_requires_image(tmp_path):
 
 def test_set_http_400_prints_body(tmp_path, monkeypatch):
     cfg = {
-        "fleet": {"key": "sk-fleet"},
+        "fleet": {"key": "sk-fleet", "sets": PRIMARY_SETS},
         "models_path": str(EXAMPLE_MODELS),
     }
     cfg_path = tmp_path / "config.yaml"


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@tooling.apache.org
For additional commands, e-mail: commits-help@tooling.apache.org