§22 S6: per-collection enabled_models — the §22.12 model-universe chain
A collection's .collection.yaml may carry an enabled_models list that NARROWS its project's universe, which narrows the deployment ENABLED_MODELS. Resolution (extending §6.6/§6.7): funder ∩ per-entry models ∩ collection ∩ project, with the operator providers as the ceiling. - migration 031: additive collections.config_json (parallels projects.config_json). - registry.parse_collection_manifest: read enabled_models into CollectionEntry.config (absent = inherit, present incl. [] = narrow; [] opts the collection out of AI); reject a non-list. _upsert_named_collection persists config_json. The default collection (from projects.yaml) leaves it NULL — it inherits the project. - models_resolver: _scope_narrowed_universe narrows the operator universe by the entry's project then collection enabled_models; the funder universe is bounded by it too. A collection cannot widen its project (narrowing from the ceiling). - collections.get_collection surfaces enabled_models; GET /api/projects/:id/collections/:cid returns it. - test_s6_collection_models_vertical.py: 9 cases (parse, the narrowing chain incl. opt-out + cannot-widen, API surfacing). Backend: 525 passed. Per SPEC §22.12. Releases as part of v0.45.0. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
+16
-4
@@ -64,6 +64,7 @@ class CollectionEntry:
|
||||
visibility: str | None
|
||||
initial_state: str
|
||||
name: str | None
|
||||
config: dict = field(default_factory=dict) # §22.12 enabled_models
|
||||
|
||||
|
||||
@dataclass
|
||||
@@ -149,7 +150,16 @@ def parse_collection_manifest(text: str) -> CollectionEntry:
|
||||
raise RegistryError(f"collection has invalid initial_state {initial_state!r}")
|
||||
name = raw.get("name")
|
||||
name = str(name).strip() if name else None
|
||||
return CollectionEntry(ctype, vis, initial_state, name)
|
||||
# §22.12: an optional per-collection enabled_models list that narrows the
|
||||
# project's universe. Absent → no narrowing (inherit). Present (incl. empty)
|
||||
# → narrowing applies; [] opts the collection out of AI.
|
||||
cfg: dict = {}
|
||||
if raw.get("enabled_models") is not None:
|
||||
em = raw["enabled_models"]
|
||||
if not isinstance(em, list):
|
||||
raise RegistryError("collection enabled_models must be a list")
|
||||
cfg["enabled_models"] = [str(m) for m in em]
|
||||
return CollectionEntry(ctype, vis, initial_state, name, cfg)
|
||||
|
||||
|
||||
def _default_collection_id(project_id: str, default_id: str) -> str:
|
||||
@@ -268,17 +278,19 @@ def _upsert_named_collection(
|
||||
conn.execute(
|
||||
"""
|
||||
INSERT INTO collections
|
||||
(id, project_id, type, subfolder, initial_state, visibility, name, registry_sha, updated_at)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, datetime('now'))
|
||||
(id, project_id, type, subfolder, initial_state, visibility, name, config_json, registry_sha, updated_at)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, datetime('now'))
|
||||
ON CONFLICT(id) DO UPDATE SET
|
||||
project_id = excluded.project_id,
|
||||
initial_state = excluded.initial_state,
|
||||
visibility = excluded.visibility,
|
||||
name = excluded.name,
|
||||
config_json = excluded.config_json,
|
||||
registry_sha = excluded.registry_sha,
|
||||
updated_at = datetime('now')
|
||||
""",
|
||||
(subdir, proj.id, ce.type, subdir, ce.initial_state, visibility, ce.name, sha),
|
||||
(subdir, proj.id, ce.type, subdir, ce.initial_state, visibility, ce.name,
|
||||
json.dumps(ce.config), sha),
|
||||
)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user