1178 lines
44 KiB
Python
1178 lines
44 KiB
Python
|
|
#!/usr/bin/env python3
|
||
|
|
"""Catalog inspection, inventory verification, and safe model discovery.
|
||
|
|
|
||
|
|
The bundled catalog is a versioned capability baseline. Discovery never guesses
|
||
|
|
an execution protocol for an unknown remote model. Codex account discovery can
|
||
|
|
optionally create a conservative user overlay because Codex itself provides the
|
||
|
|
route and wire path for built-in models.
|
||
|
|
"""
|
||
|
|
|
||
|
|
from __future__ import annotations
|
||
|
|
|
||
|
|
import contextlib
|
||
|
|
import datetime as dt
|
||
|
|
import hashlib
|
||
|
|
import json
|
||
|
|
import os
|
||
|
|
import shutil
|
||
|
|
import subprocess
|
||
|
|
import urllib.error
|
||
|
|
import urllib.parse
|
||
|
|
import urllib.request
|
||
|
|
from collections.abc import Mapping, Sequence
|
||
|
|
from pathlib import Path
|
||
|
|
from typing import Any
|
||
|
|
|
||
|
|
from mmo_catalog_data import user_catalog_root, validate_model_entry, validated_global_catalog
|
||
|
|
from mmo_inventory_snapshot import (
|
||
|
|
DISCOVERY_FIELDS,
|
||
|
|
FULL_FINGERPRINT_FIELDS,
|
||
|
|
OPENCODE_GO_SOURCE_IDS,
|
||
|
|
OPENCODE_ZEN_SOURCE_IDS,
|
||
|
|
build_inventory_snapshot,
|
||
|
|
build_opencode_go_snapshot,
|
||
|
|
build_opencode_zen_snapshot,
|
||
|
|
build_openrouter_snapshot,
|
||
|
|
codex_runtime_evidence,
|
||
|
|
compare_inventory_fingerprints,
|
||
|
|
load_inventory_snapshots,
|
||
|
|
route_catalog_key,
|
||
|
|
)
|
||
|
|
from mmo_profiles import resolve_profile
|
||
|
|
from mmo_util import (
|
||
|
|
atomic_write_json,
|
||
|
|
atomic_write_text,
|
||
|
|
config_root,
|
||
|
|
filtered_environment,
|
||
|
|
install_root,
|
||
|
|
parse_env_file,
|
||
|
|
read_json,
|
||
|
|
read_json_object,
|
||
|
|
strict_json_loads,
|
||
|
|
toml_dumps,
|
||
|
|
utc_now,
|
||
|
|
valid_absolute_uri,
|
||
|
|
)
|
||
|
|
from mmo_version import MMO_SCHEMA_VERSION, PACKAGE_VERSION
|
||
|
|
|
||
|
|
CODEX_REASONING_VALUES = {"none", "minimal", "low", "medium", "high", "xhigh", "max", "ultra"}
|
||
|
|
|
||
|
|
|
||
|
|
def _inventory_endpoint(inventory: str) -> str:
|
||
|
|
entry = inventory_baseline().get("inventories", {}).get(inventory, {})
|
||
|
|
endpoint = entry.get("endpoint")
|
||
|
|
if not isinstance(endpoint, str) or not endpoint:
|
||
|
|
raise RuntimeError(f"inventory {inventory!r} has no declarative discovery endpoint")
|
||
|
|
return endpoint
|
||
|
|
|
||
|
|
|
||
|
|
def catalog_data(profile: str | None = None) -> dict[str, Any]:
|
||
|
|
if profile:
|
||
|
|
resolved = resolve_profile(profile)
|
||
|
|
return {
|
||
|
|
"routes": resolved["routes"],
|
||
|
|
"models": resolved["models"],
|
||
|
|
"resources": resolved["resources"],
|
||
|
|
}
|
||
|
|
return validated_global_catalog()
|
||
|
|
|
||
|
|
|
||
|
|
def _matches_query(key: str, value: Mapping[str, Any], query: str | None) -> bool:
|
||
|
|
if not query:
|
||
|
|
return True
|
||
|
|
needle = query.casefold()
|
||
|
|
fields = [
|
||
|
|
key,
|
||
|
|
str(value.get("upstream_id", "")),
|
||
|
|
str(value.get("display_name", "")),
|
||
|
|
str(value.get("description", "")),
|
||
|
|
str(value.get("route", "")),
|
||
|
|
str(value.get("maker", "")),
|
||
|
|
str(value.get("inventory", "")),
|
||
|
|
]
|
||
|
|
return any(needle in field.casefold() for field in fields)
|
||
|
|
|
||
|
|
|
||
|
|
def list_models(
|
||
|
|
*,
|
||
|
|
profile: str | None = None,
|
||
|
|
route: str | None = None,
|
||
|
|
inventory: str | None = None,
|
||
|
|
query: str | None = None,
|
||
|
|
agent_compatible: bool | None = None,
|
||
|
|
availability: str | None = None,
|
||
|
|
) -> dict[str, dict[str, Any]]:
|
||
|
|
models = catalog_data(profile)["models"]
|
||
|
|
result: dict[str, dict[str, Any]] = {}
|
||
|
|
for key, value in sorted(models.items()):
|
||
|
|
if route and value.get("route") != route:
|
||
|
|
continue
|
||
|
|
if inventory and value.get("inventory") != inventory:
|
||
|
|
continue
|
||
|
|
if agent_compatible is not None and bool(value.get("agent_compatible")) != agent_compatible:
|
||
|
|
continue
|
||
|
|
if availability and value.get("availability") != availability:
|
||
|
|
continue
|
||
|
|
if not _matches_query(key, value, query):
|
||
|
|
continue
|
||
|
|
result[key] = dict(value)
|
||
|
|
return result
|
||
|
|
|
||
|
|
|
||
|
|
def find_model(key: str, *, profile: str | None = None) -> dict[str, Any]:
|
||
|
|
models = catalog_data(profile)["models"]
|
||
|
|
if key not in models:
|
||
|
|
raise FileNotFoundError(f"unknown route-qualified catalog model key: {key}")
|
||
|
|
return {"key": key, **models[key]}
|
||
|
|
|
||
|
|
|
||
|
|
def catalog_summary(profile: str | None = None) -> dict[str, Any]:
|
||
|
|
data = catalog_data(profile)
|
||
|
|
models = data["models"]
|
||
|
|
by_inventory: dict[str, int] = {}
|
||
|
|
by_kind: dict[str, int] = {}
|
||
|
|
by_driver: dict[str, int] = {}
|
||
|
|
by_maker: dict[str, int] = {}
|
||
|
|
by_operator: dict[str, int] = {}
|
||
|
|
for model in models.values():
|
||
|
|
inventory = str(model.get("inventory") or "unscoped")
|
||
|
|
by_inventory[inventory] = by_inventory.get(inventory, 0) + 1
|
||
|
|
kind = str(model.get("kind") or "chat")
|
||
|
|
by_kind[kind] = by_kind.get(kind, 0) + 1
|
||
|
|
route = data["routes"][model["route"]]
|
||
|
|
driver = str(route["driver"])
|
||
|
|
by_driver[driver] = by_driver.get(driver, 0) + 1
|
||
|
|
maker = str(model["maker"])
|
||
|
|
by_maker[maker] = by_maker.get(maker, 0) + 1
|
||
|
|
operator = str(route["api_operator"])
|
||
|
|
by_operator[operator] = by_operator.get(operator, 0) + 1
|
||
|
|
return {
|
||
|
|
"profile": profile,
|
||
|
|
"routes": len(data["routes"]),
|
||
|
|
"models": len(models),
|
||
|
|
"agent_compatible_models": sum(
|
||
|
|
bool(item.get("agent_compatible")) for item in models.values()
|
||
|
|
),
|
||
|
|
"resources": len(data["resources"]),
|
||
|
|
"models_by_inventory": dict(sorted(by_inventory.items())),
|
||
|
|
"models_by_kind": dict(sorted(by_kind.items())),
|
||
|
|
"models_by_driver": dict(sorted(by_driver.items())),
|
||
|
|
"models_by_maker": dict(sorted(by_maker.items())),
|
||
|
|
"models_by_api_operator": dict(sorted(by_operator.items())),
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
def inventory_baseline() -> dict[str, Any]:
|
||
|
|
return read_json_object(
|
||
|
|
install_root() / "config" / "upstream-inventory.json",
|
||
|
|
label="inventory baseline",
|
||
|
|
)
|
||
|
|
|
||
|
|
|
||
|
|
def local_inventory_report() -> dict[str, Any]:
|
||
|
|
baseline = inventory_baseline()
|
||
|
|
catalog = validated_global_catalog()
|
||
|
|
snapshot_errors: list[str] = []
|
||
|
|
snapshot_map: dict[str, dict[str, Any]] = {}
|
||
|
|
try:
|
||
|
|
snapshot_map = {
|
||
|
|
str(snapshot["inventory"]): snapshot
|
||
|
|
for snapshot in load_inventory_snapshots(
|
||
|
|
install_root() / "config" / "inventory-snapshots"
|
||
|
|
)
|
||
|
|
}
|
||
|
|
except (OSError, ValueError) as exc:
|
||
|
|
snapshot_errors.append(f"{type(exc).__name__}: {exc}")
|
||
|
|
report: dict[str, Any] = {
|
||
|
|
"as_of": baseline.get("as_of"),
|
||
|
|
"sources": baseline.get("sources", {}),
|
||
|
|
"inventories": {},
|
||
|
|
"snapshot_errors": snapshot_errors,
|
||
|
|
"passed": not snapshot_errors,
|
||
|
|
}
|
||
|
|
for inventory_id, expected in sorted(baseline.get("inventories", {}).items()):
|
||
|
|
expected_ids = set(expected.get("models", []))
|
||
|
|
catalog_items = {
|
||
|
|
key: model
|
||
|
|
for key, model in catalog["models"].items()
|
||
|
|
if model.get("inventory") == inventory_id
|
||
|
|
}
|
||
|
|
actual_ids = {str(model["upstream_id"]) for model in catalog_items.values()}
|
||
|
|
missing = sorted(expected_ids - actual_ids)
|
||
|
|
extra = sorted(actual_ids - expected_ids)
|
||
|
|
expected_keys = set(expected.get("catalog_keys", []))
|
||
|
|
actual_keys = set(catalog_items)
|
||
|
|
missing_keys = sorted(expected_keys - actual_keys)
|
||
|
|
extra_keys = sorted(actual_keys - expected_keys)
|
||
|
|
expected_count = expected.get("expected_count")
|
||
|
|
count_ok = expected_count is None or len(actual_ids) == int(expected_count)
|
||
|
|
snapshot = snapshot_map.get(inventory_id)
|
||
|
|
record_mismatches: list[str] = []
|
||
|
|
if snapshot:
|
||
|
|
for key in sorted(set(catalog_items) & set(snapshot["models"])):
|
||
|
|
actual_record = catalog_items[key]
|
||
|
|
snapshot_record = snapshot["models"][key]["catalog"]
|
||
|
|
normalized_snapshot_record = validate_model_entry(
|
||
|
|
key, snapshot_record, catalog["routes"]
|
||
|
|
)
|
||
|
|
if actual_record != normalized_snapshot_record:
|
||
|
|
record_mismatches.append(key)
|
||
|
|
expected_discovery = {key: expected[key] for key in DISCOVERY_FIELDS if key in expected}
|
||
|
|
snapshot_ok = bool(
|
||
|
|
snapshot
|
||
|
|
and snapshot.get("models_sha256") == expected.get("models_sha256")
|
||
|
|
and snapshot.get("as_of") == expected.get("as_of")
|
||
|
|
and snapshot.get("dynamic") == expected.get("dynamic")
|
||
|
|
and snapshot.get("adapter") == expected.get("adapter")
|
||
|
|
and snapshot.get("fingerprint_fields") == expected.get("fingerprint_fields")
|
||
|
|
and snapshot.get("captures", []) == expected.get("captures", [])
|
||
|
|
and snapshot.get("discovery") == expected_discovery
|
||
|
|
)
|
||
|
|
passed = (
|
||
|
|
not missing
|
||
|
|
and not extra
|
||
|
|
and not missing_keys
|
||
|
|
and not extra_keys
|
||
|
|
and not record_mismatches
|
||
|
|
and count_ok
|
||
|
|
and snapshot_ok
|
||
|
|
)
|
||
|
|
report["inventories"][inventory_id] = {
|
||
|
|
"passed": passed,
|
||
|
|
"dynamic": bool(expected.get("dynamic", False)),
|
||
|
|
"expected_count": expected_count,
|
||
|
|
"actual_count": len(actual_ids),
|
||
|
|
"missing": missing,
|
||
|
|
"extra": extra,
|
||
|
|
"missing_catalog_keys": missing_keys,
|
||
|
|
"extra_catalog_keys": extra_keys,
|
||
|
|
"catalog_record_mismatches": record_mismatches,
|
||
|
|
"snapshot": expected.get("snapshot"),
|
||
|
|
"models_sha256": expected.get("models_sha256"),
|
||
|
|
"snapshot_ok": snapshot_ok,
|
||
|
|
"catalog_keys": sorted(catalog_items),
|
||
|
|
}
|
||
|
|
report["passed"] = report["passed"] and passed
|
||
|
|
known_sources = set(baseline.get("sources", {}))
|
||
|
|
source_errors: list[str] = []
|
||
|
|
for model_key, model in sorted(catalog["models"].items()):
|
||
|
|
if model.get("inventory") not in baseline.get("inventories", {}):
|
||
|
|
continue
|
||
|
|
for field in ("source", "availability_source", "capability_source", "pricing_source"):
|
||
|
|
source = model.get(field)
|
||
|
|
if source is not None and source not in known_sources:
|
||
|
|
source_errors.append(f"{model_key}.{field}: unknown source {source!r}")
|
||
|
|
report["source_errors"] = source_errors
|
||
|
|
untracked_snapshots = sorted(set(snapshot_map) - set(baseline.get("inventories", {})))
|
||
|
|
report["untracked_snapshots"] = untracked_snapshots
|
||
|
|
report["passed"] = report["passed"] and not source_errors and not untracked_snapshots
|
||
|
|
return report
|
||
|
|
|
||
|
|
|
||
|
|
def _http_bytes(
|
||
|
|
url: str,
|
||
|
|
timeout: float,
|
||
|
|
*,
|
||
|
|
headers: Mapping[str, str] | None = None,
|
||
|
|
accept: str = "application/json",
|
||
|
|
) -> bytes:
|
||
|
|
if not isinstance(url, str) or not valid_absolute_uri(url):
|
||
|
|
raise ValueError("catalog discovery URL must be an absolute HTTP(S) URL")
|
||
|
|
try:
|
||
|
|
parsed = urllib.parse.urlsplit(url)
|
||
|
|
_ = parsed.port
|
||
|
|
except ValueError as exc:
|
||
|
|
raise ValueError("catalog discovery URL must be an absolute HTTP(S) URL") from exc
|
||
|
|
if (
|
||
|
|
parsed.scheme.lower() not in {"http", "https"}
|
||
|
|
or not parsed.hostname
|
||
|
|
or parsed.username is not None
|
||
|
|
or parsed.password is not None
|
||
|
|
or parsed.fragment
|
||
|
|
):
|
||
|
|
raise ValueError("catalog discovery URL must be an absolute HTTP(S) URL")
|
||
|
|
request_headers = {
|
||
|
|
"Accept": accept,
|
||
|
|
"User-Agent": f"codex-mmo/{PACKAGE_VERSION} catalog-discovery",
|
||
|
|
}
|
||
|
|
request_headers.update(dict(headers or {}))
|
||
|
|
request = urllib.request.Request(url, headers=request_headers)
|
||
|
|
opener = urllib.request.build_opener(_SameOriginRedirectHandler())
|
||
|
|
with opener.open(request, timeout=timeout) as response:
|
||
|
|
return response.read()
|
||
|
|
|
||
|
|
|
||
|
|
def _http_json(
|
||
|
|
url: str,
|
||
|
|
timeout: float,
|
||
|
|
*,
|
||
|
|
headers: Mapping[str, str] | None = None,
|
||
|
|
) -> Any:
|
||
|
|
return strict_json_loads(_http_bytes(url, timeout, headers=headers).decode("utf-8"))
|
||
|
|
|
||
|
|
|
||
|
|
class _SameOriginRedirectHandler(urllib.request.HTTPRedirectHandler):
|
||
|
|
"""Permit discovery redirects without forwarding credentials off-origin."""
|
||
|
|
|
||
|
|
@staticmethod
|
||
|
|
def _origin(url: str) -> tuple[str, str, int | None]:
|
||
|
|
parsed = urllib.parse.urlsplit(url)
|
||
|
|
port = parsed.port
|
||
|
|
if port is None:
|
||
|
|
port = 443 if parsed.scheme.lower() == "https" else 80
|
||
|
|
return parsed.scheme.lower(), (parsed.hostname or "").lower(), port
|
||
|
|
|
||
|
|
def redirect_request(
|
||
|
|
self,
|
||
|
|
req: urllib.request.Request,
|
||
|
|
fp: Any,
|
||
|
|
code: int,
|
||
|
|
msg: str,
|
||
|
|
headers: Any,
|
||
|
|
newurl: str,
|
||
|
|
) -> urllib.request.Request | None:
|
||
|
|
if self._origin(req.full_url) != self._origin(newurl):
|
||
|
|
with contextlib.suppress(Exception):
|
||
|
|
fp.close()
|
||
|
|
raise urllib.error.URLError(
|
||
|
|
f"cross-origin discovery redirect rejected ({code}): {newurl}"
|
||
|
|
)
|
||
|
|
return super().redirect_request(req, fp, code, msg, headers, newurl)
|
||
|
|
|
||
|
|
|
||
|
|
def _model_rows(document: Any) -> list[dict[str, Any]]:
|
||
|
|
if isinstance(document, list):
|
||
|
|
rows = document
|
||
|
|
elif isinstance(document, Mapping):
|
||
|
|
rows = document.get("data") or document.get("models") or []
|
||
|
|
else:
|
||
|
|
rows = []
|
||
|
|
return [dict(item) for item in rows if isinstance(item, Mapping)]
|
||
|
|
|
||
|
|
|
||
|
|
def _model_slug(item: Mapping[str, Any]) -> str | None:
|
||
|
|
for key in ("id", "slug", "model", "name"):
|
||
|
|
value = item.get(key)
|
||
|
|
if isinstance(value, str) and value:
|
||
|
|
return value
|
||
|
|
return None
|
||
|
|
|
||
|
|
|
||
|
|
def discover_opencode_go(*, url: str | None = None, timeout: float = 10.0) -> dict[str, Any]:
|
||
|
|
target = url or os.environ.get("MMO_OPENCODE_MODELS_URL") or _inventory_endpoint("opencode-go")
|
||
|
|
started = utc_now()
|
||
|
|
try:
|
||
|
|
document = _http_json(target, timeout)
|
||
|
|
rows = _model_rows(document)
|
||
|
|
ids = sorted({slug for item in rows if (slug := _model_slug(item))})
|
||
|
|
return {
|
||
|
|
"inventory": "opencode-go",
|
||
|
|
"url": target,
|
||
|
|
"observed_at": started,
|
||
|
|
"passed": bool(ids),
|
||
|
|
"models": ids,
|
||
|
|
"count": len(ids),
|
||
|
|
"raw_metadata": rows,
|
||
|
|
}
|
||
|
|
except (OSError, ValueError, urllib.error.URLError, json.JSONDecodeError) as exc:
|
||
|
|
return {
|
||
|
|
"inventory": "opencode-go",
|
||
|
|
"url": target,
|
||
|
|
"observed_at": started,
|
||
|
|
"passed": False,
|
||
|
|
"models": [],
|
||
|
|
"count": 0,
|
||
|
|
"error": f"{type(exc).__name__}: {exc}",
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
def discover_opencode_zen(*, url: str | None = None, timeout: float = 10.0) -> dict[str, Any]:
|
||
|
|
"""Discover the public OpenCode Zen availability listing."""
|
||
|
|
|
||
|
|
target = (
|
||
|
|
url or os.environ.get("MMO_OPENCODE_ZEN_MODELS_URL") or _inventory_endpoint("opencode-zen")
|
||
|
|
)
|
||
|
|
started = utc_now()
|
||
|
|
try:
|
||
|
|
document = _http_json(target, timeout)
|
||
|
|
rows = _model_rows(document)
|
||
|
|
ids = sorted({slug for item in rows if (slug := _model_slug(item))})
|
||
|
|
return {
|
||
|
|
"inventory": "opencode-zen",
|
||
|
|
"url": target,
|
||
|
|
"observed_at": started,
|
||
|
|
"passed": bool(ids),
|
||
|
|
"models": ids,
|
||
|
|
"count": len(ids),
|
||
|
|
"raw_metadata": rows,
|
||
|
|
}
|
||
|
|
except (OSError, ValueError, urllib.error.URLError, json.JSONDecodeError) as exc:
|
||
|
|
return {
|
||
|
|
"inventory": "opencode-zen",
|
||
|
|
"url": target,
|
||
|
|
"observed_at": started,
|
||
|
|
"passed": False,
|
||
|
|
"models": [],
|
||
|
|
"count": 0,
|
||
|
|
"error": f"{type(exc).__name__}: {exc}",
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
def _catalog_credentials() -> dict[str, str]:
|
||
|
|
values = parse_env_file(config_root() / "credentials.env")
|
||
|
|
values.update({key: value for key, value in os.environ.items() if value})
|
||
|
|
return values
|
||
|
|
|
||
|
|
|
||
|
|
def discover_openrouter(
|
||
|
|
*,
|
||
|
|
url: str | None = None,
|
||
|
|
api_key: str | None = None,
|
||
|
|
timeout: float = 10.0,
|
||
|
|
) -> dict[str, Any]:
|
||
|
|
"""Discover OpenRouter's public model inventory, authenticating when available."""
|
||
|
|
|
||
|
|
official_target = _inventory_endpoint("openrouter")
|
||
|
|
target = url or os.environ.get("MMO_OPENROUTER_MODELS_URL") or official_target
|
||
|
|
# A configured credential is sent implicitly only to the reviewed official
|
||
|
|
# endpoint. Callers that deliberately target another URL must explicitly
|
||
|
|
# provide its credential, preventing URL overrides from exfiltrating the
|
||
|
|
# user's OpenRouter key.
|
||
|
|
configured_token = (
|
||
|
|
_catalog_credentials().get("OPENROUTER_API_KEY")
|
||
|
|
if api_key is None and target == official_target
|
||
|
|
else None
|
||
|
|
)
|
||
|
|
token = (
|
||
|
|
api_key
|
||
|
|
if api_key is not None
|
||
|
|
else (configured_token if target == official_target else None)
|
||
|
|
)
|
||
|
|
headers = {"Authorization": f"Bearer {token}"} if token else None
|
||
|
|
started = utc_now()
|
||
|
|
try:
|
||
|
|
document = _http_json(target, timeout, headers=headers)
|
||
|
|
if not isinstance(document, Mapping):
|
||
|
|
raise ValueError("OpenRouter model listing must be an object")
|
||
|
|
rows = _model_rows(document)
|
||
|
|
if "total_count" not in document or "links" not in document:
|
||
|
|
raise ValueError("OpenRouter model listing lacks required pagination metadata")
|
||
|
|
total_count = document["total_count"]
|
||
|
|
if (
|
||
|
|
not isinstance(total_count, int)
|
||
|
|
or isinstance(total_count, bool)
|
||
|
|
or total_count != len(rows)
|
||
|
|
):
|
||
|
|
raise ValueError(
|
||
|
|
"OpenRouter model listing is paginated or incomplete: "
|
||
|
|
f"total_count={total_count!r}, rows={len(rows)}"
|
||
|
|
)
|
||
|
|
links = document["links"]
|
||
|
|
if not isinstance(links, Mapping) or "next" not in links:
|
||
|
|
raise ValueError("OpenRouter model listing has invalid pagination links")
|
||
|
|
if links["next"] not in (None, ""):
|
||
|
|
raise ValueError("OpenRouter model listing has an unconsumed next page")
|
||
|
|
ids = sorted({slug for item in rows if (slug := _model_slug(item))})
|
||
|
|
return {
|
||
|
|
"inventory": "openrouter",
|
||
|
|
"url": target,
|
||
|
|
"observed_at": started,
|
||
|
|
"passed": bool(ids),
|
||
|
|
"authenticated_request": bool(token),
|
||
|
|
"models": ids,
|
||
|
|
"count": len(ids),
|
||
|
|
"raw_metadata": rows,
|
||
|
|
}
|
||
|
|
except (OSError, ValueError, urllib.error.URLError, json.JSONDecodeError) as exc:
|
||
|
|
return {
|
||
|
|
"inventory": "openrouter",
|
||
|
|
"url": target,
|
||
|
|
"observed_at": started,
|
||
|
|
"passed": False,
|
||
|
|
"authenticated_request": bool(token),
|
||
|
|
"models": [],
|
||
|
|
"count": 0,
|
||
|
|
"error": f"{type(exc).__name__}: {exc}",
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
def discover_zai(
|
||
|
|
inventory: str,
|
||
|
|
*,
|
||
|
|
url: str | None = None,
|
||
|
|
api_key: str | None = None,
|
||
|
|
timeout: float = 10.0,
|
||
|
|
) -> dict[str, Any]:
|
||
|
|
"""Discover model IDs advertised by one Z.AI OpenAI-compatible endpoint.
|
||
|
|
|
||
|
|
Z.AI's complete catalog also includes media and hosted-agent services that
|
||
|
|
are not necessarily returned by ``/models``. Those remain represented by
|
||
|
|
the versioned documentation inventory. Live discovery is used to detect
|
||
|
|
newly advertised coding/chat IDs without guessing capabilities for unknown
|
||
|
|
services.
|
||
|
|
"""
|
||
|
|
|
||
|
|
if inventory not in {"zai-api", "zai-coding-plan"}:
|
||
|
|
raise ValueError("inventory must be zai-api or zai-coding-plan")
|
||
|
|
if inventory == "zai-api":
|
||
|
|
official_target = _inventory_endpoint("zai-api")
|
||
|
|
target = url or os.environ.get("MMO_ZAI_MODELS_URL") or official_target
|
||
|
|
credential_names = ["ZAI_API_KEY"]
|
||
|
|
else:
|
||
|
|
official_target = _inventory_endpoint("zai-coding-plan")
|
||
|
|
target = url or os.environ.get("MMO_ZAI_CODING_MODELS_URL") or official_target
|
||
|
|
credential_names = ["ZAI_CODING_API_KEY"]
|
||
|
|
values = _catalog_credentials()
|
||
|
|
configured_token = next(
|
||
|
|
(values.get(name) for name in credential_names if values.get(name)), None
|
||
|
|
)
|
||
|
|
# Never send a configured Z.AI credential to an operator-overridden URL.
|
||
|
|
# Custom endpoints require an explicit credential, matching the discovery
|
||
|
|
# boundary used for OpenRouter above. An explicit blank disables implicit
|
||
|
|
# authentication even for the official endpoint.
|
||
|
|
token = (
|
||
|
|
api_key
|
||
|
|
if api_key is not None
|
||
|
|
else (configured_token if target == official_target else None)
|
||
|
|
)
|
||
|
|
started = utc_now()
|
||
|
|
if not token:
|
||
|
|
return {
|
||
|
|
"inventory": inventory,
|
||
|
|
"url": target,
|
||
|
|
"observed_at": started,
|
||
|
|
"passed": False,
|
||
|
|
"skipped": True,
|
||
|
|
"authenticated_request": False,
|
||
|
|
"models": [],
|
||
|
|
"count": 0,
|
||
|
|
"error": "missing credential: " + "/".join(credential_names),
|
||
|
|
}
|
||
|
|
try:
|
||
|
|
document = _http_json(
|
||
|
|
target,
|
||
|
|
timeout,
|
||
|
|
headers={"Authorization": f"Bearer {token}"},
|
||
|
|
)
|
||
|
|
rows = _model_rows(document)
|
||
|
|
ids = sorted({slug for item in rows if (slug := _model_slug(item))})
|
||
|
|
return {
|
||
|
|
"inventory": inventory,
|
||
|
|
"url": target,
|
||
|
|
"observed_at": started,
|
||
|
|
"passed": bool(ids),
|
||
|
|
"skipped": False,
|
||
|
|
"authenticated_request": True,
|
||
|
|
"models": ids,
|
||
|
|
"count": len(ids),
|
||
|
|
"raw_metadata": rows,
|
||
|
|
}
|
||
|
|
except (OSError, ValueError, urllib.error.URLError, json.JSONDecodeError) as exc:
|
||
|
|
return {
|
||
|
|
"inventory": inventory,
|
||
|
|
"url": target,
|
||
|
|
"observed_at": started,
|
||
|
|
"passed": False,
|
||
|
|
"skipped": False,
|
||
|
|
"authenticated_request": True,
|
||
|
|
"models": [],
|
||
|
|
"count": 0,
|
||
|
|
"error": f"{type(exc).__name__}: {exc}",
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
def _load_json_output(result: subprocess.CompletedProcess[str]) -> Any | None:
|
||
|
|
text = result.stdout.strip()
|
||
|
|
if not text:
|
||
|
|
return None
|
||
|
|
with contextlib.suppress(json.JSONDecodeError, ValueError):
|
||
|
|
return strict_json_loads(text)
|
||
|
|
# Some Codex versions print a short informational line before the JSON.
|
||
|
|
for offset, char in enumerate(text):
|
||
|
|
if char not in "[{":
|
||
|
|
continue
|
||
|
|
with contextlib.suppress(json.JSONDecodeError, ValueError):
|
||
|
|
return strict_json_loads(text[offset:])
|
||
|
|
return None
|
||
|
|
|
||
|
|
|
||
|
|
def _run_codex_models(binary: str, home: Path, *, bundled: bool) -> dict[str, Any]:
|
||
|
|
command = [binary, "debug", "models"]
|
||
|
|
if bundled:
|
||
|
|
command.append("--bundled")
|
||
|
|
try:
|
||
|
|
result = subprocess.run(
|
||
|
|
command,
|
||
|
|
env=filtered_environment(extra={"CODEX_HOME": str(home)}),
|
||
|
|
text=True,
|
||
|
|
capture_output=True,
|
||
|
|
timeout=45,
|
||
|
|
check=False,
|
||
|
|
)
|
||
|
|
except (OSError, subprocess.SubprocessError) as exc:
|
||
|
|
return {
|
||
|
|
"command": command,
|
||
|
|
"passed": False,
|
||
|
|
"models": [],
|
||
|
|
"error": f"{type(exc).__name__}: {exc}",
|
||
|
|
}
|
||
|
|
document = _load_json_output(result)
|
||
|
|
rows = _model_rows(document)
|
||
|
|
return {
|
||
|
|
"command": command,
|
||
|
|
"passed": result.returncode == 0 and bool(rows),
|
||
|
|
"exit_code": result.returncode,
|
||
|
|
"models": rows,
|
||
|
|
"stderr": result.stderr[-4000:],
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
def _read_codex_cache(home: Path) -> dict[str, Any]:
|
||
|
|
path = home / "models_cache.json"
|
||
|
|
if not path.is_file():
|
||
|
|
return {"path": str(path), "passed": False, "models": [], "error": "not found"}
|
||
|
|
try:
|
||
|
|
document = read_json(path)
|
||
|
|
rows = _model_rows(document)
|
||
|
|
return {
|
||
|
|
"path": str(path),
|
||
|
|
"passed": bool(rows),
|
||
|
|
"models": rows,
|
||
|
|
"client_version": document.get("client_version")
|
||
|
|
if isinstance(document, Mapping)
|
||
|
|
else None,
|
||
|
|
"fetched_at": document.get("fetched_at") if isinstance(document, Mapping) else None,
|
||
|
|
}
|
||
|
|
except (OSError, ValueError, json.JSONDecodeError) as exc:
|
||
|
|
return {
|
||
|
|
"path": str(path),
|
||
|
|
"passed": False,
|
||
|
|
"models": [],
|
||
|
|
"error": f"{type(exc).__name__}: {exc}",
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
def discover_codex(
|
||
|
|
*,
|
||
|
|
binary: str = "codex",
|
||
|
|
home: Path | None = None,
|
||
|
|
include_bundled: bool = True,
|
||
|
|
) -> dict[str, Any]:
|
||
|
|
codex_home = (home or Path(os.environ.get("CODEX_HOME", "~/.codex"))).expanduser().resolve()
|
||
|
|
candidate = Path(binary).expanduser()
|
||
|
|
resolved_binary = shutil.which(binary) or (
|
||
|
|
str(candidate.resolve()) if candidate.is_file() else None
|
||
|
|
)
|
||
|
|
sources: dict[str, Any] = {"cache": _read_codex_cache(codex_home)}
|
||
|
|
if resolved_binary:
|
||
|
|
sources["authenticated"] = _run_codex_models(resolved_binary, codex_home, bundled=False)
|
||
|
|
if include_bundled:
|
||
|
|
sources["bundled"] = _run_codex_models(resolved_binary, codex_home, bundled=True)
|
||
|
|
else:
|
||
|
|
sources["authenticated"] = {
|
||
|
|
"passed": False,
|
||
|
|
"models": [],
|
||
|
|
"error": f"Codex binary not found: {binary}",
|
||
|
|
}
|
||
|
|
if include_bundled:
|
||
|
|
sources["bundled"] = {
|
||
|
|
"passed": False,
|
||
|
|
"models": [],
|
||
|
|
"error": f"Codex binary not found: {binary}",
|
||
|
|
}
|
||
|
|
|
||
|
|
merged: dict[str, dict[str, Any]] = {}
|
||
|
|
availability: dict[str, list[str]] = {}
|
||
|
|
for source_name in ("authenticated", "bundled", "cache"):
|
||
|
|
source = sources.get(source_name, {})
|
||
|
|
for row in source.get("models", []):
|
||
|
|
slug = _model_slug(row)
|
||
|
|
if not slug:
|
||
|
|
continue
|
||
|
|
merged.setdefault(slug, dict(row))
|
||
|
|
availability.setdefault(slug, []).append(source_name)
|
||
|
|
return {
|
||
|
|
"inventory": "openai-codex",
|
||
|
|
"observed_at": utc_now(),
|
||
|
|
"codex_home": str(codex_home),
|
||
|
|
"codex_binary": resolved_binary,
|
||
|
|
"passed": bool(merged),
|
||
|
|
"models": sorted(merged),
|
||
|
|
"count": len(merged),
|
||
|
|
"availability_sources": {key: sorted(value) for key, value in sorted(availability.items())},
|
||
|
|
"metadata": merged,
|
||
|
|
"sources": sources,
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
def _compare(observed: Sequence[str], expected: Sequence[str]) -> dict[str, Any]:
|
||
|
|
observed_set = set(observed)
|
||
|
|
expected_set = set(expected)
|
||
|
|
return {
|
||
|
|
"observed_count": len(observed_set),
|
||
|
|
"expected_count": len(expected_set),
|
||
|
|
"missing_from_observed": sorted(expected_set - observed_set),
|
||
|
|
"unknown_to_catalog": sorted(observed_set - expected_set),
|
||
|
|
"exact": observed_set == expected_set,
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
def _snapshot_map() -> dict[str, dict[str, Any]]:
|
||
|
|
return {
|
||
|
|
str(snapshot["inventory"]): snapshot
|
||
|
|
for snapshot in load_inventory_snapshots(install_root() / "config" / "inventory-snapshots")
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
def _source_bytes(
|
||
|
|
snapshot: Mapping[str, Any], source: str, timeout: float, *, override: str | None = None
|
||
|
|
) -> tuple[str, bytes]:
|
||
|
|
sources = snapshot.get("sources", {})
|
||
|
|
if not isinstance(sources, Mapping) or not isinstance(sources.get(source), str):
|
||
|
|
raise ValueError(f"inventory snapshot lacks source {source!r}")
|
||
|
|
url = override or str(sources[source])
|
||
|
|
accept = "text/plain" if source.endswith("docs-source") else "application/json"
|
||
|
|
return url, _http_bytes(url, timeout, accept=accept)
|
||
|
|
|
||
|
|
|
||
|
|
def _build_observed_public_snapshot(
|
||
|
|
inventory: str,
|
||
|
|
expected: Mapping[str, Any],
|
||
|
|
*,
|
||
|
|
timeout: float,
|
||
|
|
endpoint_override: str | None,
|
||
|
|
) -> dict[str, Any]:
|
||
|
|
"""Rebuild a dynamic public inventory with its declared source adapter."""
|
||
|
|
|
||
|
|
adapter = expected.get("adapter")
|
||
|
|
observed_at = dt.datetime.now(dt.UTC).strftime("%Y-%m-%dT%H:%M:%SZ")
|
||
|
|
if adapter == "openrouter_models_api":
|
||
|
|
source_url, raw = _source_bytes(
|
||
|
|
expected, "openrouter-models-api", timeout, override=endpoint_override
|
||
|
|
)
|
||
|
|
zdr_source_url, zdr_raw = _source_bytes(expected, "openrouter-zdr-endpoints", timeout)
|
||
|
|
return build_openrouter_snapshot(
|
||
|
|
strict_json_loads(raw),
|
||
|
|
strict_json_loads(zdr_raw),
|
||
|
|
as_of=str(expected["as_of"]),
|
||
|
|
retrieved_at=observed_at,
|
||
|
|
response_sha256=hashlib.sha256(raw).hexdigest(),
|
||
|
|
zdr_response_sha256=hashlib.sha256(zdr_raw).hexdigest(),
|
||
|
|
source_url=source_url,
|
||
|
|
zdr_source_url=zdr_source_url,
|
||
|
|
endpoint_selections=expected["discovery"]["endpoint_selections"],
|
||
|
|
)
|
||
|
|
if adapter not in {"opencode_go_join", "opencode_zen_join"}:
|
||
|
|
raise ValueError(
|
||
|
|
f"inventory {inventory!r} adapter {adapter!r} cannot produce a full live fingerprint"
|
||
|
|
)
|
||
|
|
if adapter == "opencode_go_join":
|
||
|
|
prefix = "opencode-go"
|
||
|
|
builder = build_opencode_go_snapshot
|
||
|
|
else:
|
||
|
|
prefix = "opencode-zen"
|
||
|
|
builder = build_opencode_zen_snapshot
|
||
|
|
listing_url, listing_raw = _source_bytes(
|
||
|
|
expected, f"{prefix}-models", timeout, override=endpoint_override
|
||
|
|
)
|
||
|
|
models_dev_url, models_dev_raw = _source_bytes(expected, f"models-dev-{prefix}", timeout)
|
||
|
|
docs_source_id = f"{prefix}-docs-source"
|
||
|
|
live_docs_url = (
|
||
|
|
OPENCODE_GO_SOURCE_IDS[docs_source_id]
|
||
|
|
if adapter == "opencode_go_join"
|
||
|
|
else OPENCODE_ZEN_SOURCE_IDS[docs_source_id]
|
||
|
|
)
|
||
|
|
# Snapshot captures remain immutable commit URLs. Live verification uses
|
||
|
|
# the provider's current branch so a documentation/protocol change cannot
|
||
|
|
# hide behind the reviewed capture.
|
||
|
|
docs_url, docs_raw = _source_bytes(
|
||
|
|
expected,
|
||
|
|
docs_source_id,
|
||
|
|
timeout,
|
||
|
|
override=live_docs_url,
|
||
|
|
)
|
||
|
|
return builder(
|
||
|
|
strict_json_loads(listing_raw),
|
||
|
|
strict_json_loads(models_dev_raw),
|
||
|
|
docs_raw.decode("utf-8"),
|
||
|
|
as_of=str(expected["as_of"]),
|
||
|
|
retrieved_at=observed_at,
|
||
|
|
listing_sha256=hashlib.sha256(listing_raw).hexdigest(),
|
||
|
|
models_dev_sha256=hashlib.sha256(models_dev_raw).hexdigest(),
|
||
|
|
docs_sha256=hashlib.sha256(docs_raw).hexdigest(),
|
||
|
|
listing_url=listing_url,
|
||
|
|
models_dev_url=models_dev_url,
|
||
|
|
docs_url=docs_url,
|
||
|
|
)
|
||
|
|
|
||
|
|
|
||
|
|
def _id_only_fingerprint_comparison(
|
||
|
|
observed: Sequence[str], expected_snapshot: Mapping[str, Any]
|
||
|
|
) -> dict[str, Any]:
|
||
|
|
expected_by_id = {
|
||
|
|
str(record["catalog"]["upstream_id"]): str(record["catalog"]["route"])
|
||
|
|
for record in expected_snapshot["models"].values()
|
||
|
|
}
|
||
|
|
ids = sorted(set(observed))
|
||
|
|
membership = _compare(ids, sorted(expected_by_id))
|
||
|
|
incomplete = [
|
||
|
|
{
|
||
|
|
"route": expected_by_id[model_id],
|
||
|
|
"upstream_id": model_id,
|
||
|
|
"fields": list(FULL_FINGERPRINT_FIELDS[1:]),
|
||
|
|
}
|
||
|
|
for model_id in sorted(set(ids) & set(expected_by_id))
|
||
|
|
]
|
||
|
|
return {
|
||
|
|
**membership,
|
||
|
|
"fingerprint_mismatches": [],
|
||
|
|
"incomplete_evidence": incomplete,
|
||
|
|
"verified_fields": ["upstream_id"] if not membership["unknown_to_catalog"] else [],
|
||
|
|
"exact": False,
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
def _verify_source_captures(snapshot: Mapping[str, Any], timeout: float) -> dict[str, Any]:
|
||
|
|
captures: list[dict[str, Any]] = []
|
||
|
|
for capture in snapshot.get("captures", []):
|
||
|
|
source = str(capture["source"])
|
||
|
|
item: dict[str, Any] = {
|
||
|
|
"source": source,
|
||
|
|
"expected_sha256": capture["response_sha256"],
|
||
|
|
}
|
||
|
|
try:
|
||
|
|
url, raw = _source_bytes(snapshot, source, timeout)
|
||
|
|
observed = hashlib.sha256(raw).hexdigest()
|
||
|
|
item.update(
|
||
|
|
{
|
||
|
|
"url": url,
|
||
|
|
"observed_sha256": observed,
|
||
|
|
"passed": observed == capture["response_sha256"],
|
||
|
|
}
|
||
|
|
)
|
||
|
|
except (OSError, ValueError, urllib.error.URLError) as exc:
|
||
|
|
item.update({"passed": False, "error": f"{type(exc).__name__}: {exc}"})
|
||
|
|
captures.append(item)
|
||
|
|
return {
|
||
|
|
"captures": captures,
|
||
|
|
"passed": bool(captures) and all(item["passed"] for item in captures),
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
def _codex_fingerprint_comparison(
|
||
|
|
discovery: Mapping[str, Any], expected: Mapping[str, Any]
|
||
|
|
) -> dict[str, Any]:
|
||
|
|
metadata = discovery.get("metadata", {})
|
||
|
|
if not isinstance(metadata, Mapping):
|
||
|
|
raise ValueError("Codex discovery metadata is missing")
|
||
|
|
records: dict[str, dict[str, Any]] = {}
|
||
|
|
for key, expected_record in expected["models"].items():
|
||
|
|
slug = str(expected_record["catalog"]["upstream_id"])
|
||
|
|
row = metadata.get(slug)
|
||
|
|
if not isinstance(row, Mapping):
|
||
|
|
continue
|
||
|
|
records[str(key)] = {
|
||
|
|
"catalog": dict(expected_record["catalog"]),
|
||
|
|
"evidence": {
|
||
|
|
"codex_runtime": codex_runtime_evidence(row),
|
||
|
|
"verified_fingerprint_fields": list(FULL_FINGERPRINT_FIELDS),
|
||
|
|
},
|
||
|
|
}
|
||
|
|
observed = build_inventory_snapshot(
|
||
|
|
inventory="openai-codex",
|
||
|
|
adapter="codex_installed_models_join",
|
||
|
|
fingerprint_fields=FULL_FINGERPRINT_FIELDS,
|
||
|
|
as_of=str(expected["as_of"]),
|
||
|
|
dynamic=False,
|
||
|
|
sources=expected["sources"],
|
||
|
|
discovery=expected["discovery"],
|
||
|
|
captures=[],
|
||
|
|
models=records,
|
||
|
|
)
|
||
|
|
return compare_inventory_fingerprints(expected, observed)
|
||
|
|
|
||
|
|
|
||
|
|
def verify_catalog(
|
||
|
|
*,
|
||
|
|
remote: bool = False,
|
||
|
|
include_codex: bool = False,
|
||
|
|
opencode_url: str | None = None,
|
||
|
|
opencode_zen_url: str | None = None,
|
||
|
|
openrouter_url: str | None = None,
|
||
|
|
zai_coding_url: str | None = None,
|
||
|
|
codex_binary: str = "codex",
|
||
|
|
codex_home: Path | None = None,
|
||
|
|
timeout: float = 10.0,
|
||
|
|
) -> dict[str, Any]:
|
||
|
|
baseline = inventory_baseline()
|
||
|
|
local = local_inventory_report()
|
||
|
|
result: dict[str, Any] = {
|
||
|
|
"checked_at": utc_now(),
|
||
|
|
"local": local,
|
||
|
|
"remote": {},
|
||
|
|
"passed": bool(local["passed"]),
|
||
|
|
}
|
||
|
|
inventories = baseline["inventories"]
|
||
|
|
snapshots = _snapshot_map()
|
||
|
|
if remote:
|
||
|
|
public_overrides = {
|
||
|
|
"opencode-go": opencode_url,
|
||
|
|
"opencode-zen": opencode_zen_url,
|
||
|
|
"openrouter": openrouter_url,
|
||
|
|
}
|
||
|
|
for inventory_id, endpoint_override in public_overrides.items():
|
||
|
|
expected_snapshot = snapshots[inventory_id]
|
||
|
|
report: dict[str, Any] = {
|
||
|
|
"inventory": inventory_id,
|
||
|
|
"adapter": expected_snapshot["adapter"],
|
||
|
|
"observed_at": utc_now(),
|
||
|
|
"fingerprint_fields": list(expected_snapshot["fingerprint_fields"]),
|
||
|
|
}
|
||
|
|
try:
|
||
|
|
observed_snapshot = _build_observed_public_snapshot(
|
||
|
|
inventory_id,
|
||
|
|
expected_snapshot,
|
||
|
|
timeout=timeout,
|
||
|
|
endpoint_override=endpoint_override,
|
||
|
|
)
|
||
|
|
comparison = compare_inventory_fingerprints(expected_snapshot, observed_snapshot)
|
||
|
|
report.update(
|
||
|
|
{
|
||
|
|
"url": observed_snapshot["discovery"].get("endpoint"),
|
||
|
|
"models": sorted(
|
||
|
|
str(item["catalog"]["upstream_id"])
|
||
|
|
for item in observed_snapshot["models"].values()
|
||
|
|
),
|
||
|
|
"count": len(observed_snapshot["models"]),
|
||
|
|
"comparison": comparison,
|
||
|
|
"full_fingerprint_verified": bool(comparison["exact"]),
|
||
|
|
"passed": bool(comparison["exact"]),
|
||
|
|
}
|
||
|
|
)
|
||
|
|
except (OSError, ValueError, urllib.error.URLError, json.JSONDecodeError) as exc:
|
||
|
|
report.update(
|
||
|
|
{
|
||
|
|
"passed": False,
|
||
|
|
"full_fingerprint_verified": False,
|
||
|
|
"error": f"{type(exc).__name__}: {exc}",
|
||
|
|
}
|
||
|
|
)
|
||
|
|
result["remote"][inventory_id] = report
|
||
|
|
result["passed"] = result["passed"] and bool(report["passed"])
|
||
|
|
# The bundled portfolio uses the Coding Plan, not the unrelated Z.AI
|
||
|
|
# general API product. Its authenticated model IDs establish live
|
||
|
|
# availability while exact official Markdown captures establish the
|
||
|
|
# reviewed capability fingerprint.
|
||
|
|
inventory_id = "zai-coding-plan"
|
||
|
|
zai = discover_zai(inventory_id, url=zai_coding_url, timeout=timeout)
|
||
|
|
expected_ids = sorted(
|
||
|
|
str(record["catalog"]["upstream_id"])
|
||
|
|
for record in snapshots[inventory_id]["models"].values()
|
||
|
|
)
|
||
|
|
membership = _compare(zai.get("models", []), expected_ids)
|
||
|
|
source_verification = _verify_source_captures(snapshots[inventory_id], timeout)
|
||
|
|
full_verified = bool(
|
||
|
|
zai.get("passed") and membership["exact"] and source_verification["passed"]
|
||
|
|
)
|
||
|
|
zai.update(
|
||
|
|
{
|
||
|
|
"comparison": membership,
|
||
|
|
"source_capture_verification": source_verification,
|
||
|
|
"fingerprint_fields": list(FULL_FINGERPRINT_FIELDS),
|
||
|
|
"verified_fields": list(FULL_FINGERPRINT_FIELDS) if full_verified else [],
|
||
|
|
"full_fingerprint_verified": full_verified,
|
||
|
|
}
|
||
|
|
)
|
||
|
|
result["remote"][inventory_id] = zai
|
||
|
|
result["passed"] = result["passed"] and full_verified
|
||
|
|
if include_codex:
|
||
|
|
codex = discover_codex(binary=codex_binary, home=codex_home)
|
||
|
|
codex_inventory = inventories["openai-codex"]
|
||
|
|
observed = [item for item in codex.get("models", []) if isinstance(item, str)]
|
||
|
|
ignored_prefixes = tuple(codex_inventory.get("ignored_model_prefixes", []))
|
||
|
|
deprecated = set(codex_inventory.get("known_deprecated_models", []))
|
||
|
|
codex["ignored_internal_models"] = sorted(
|
||
|
|
item for item in observed if ignored_prefixes and item.startswith(ignored_prefixes)
|
||
|
|
)
|
||
|
|
codex["known_deprecated_models_observed"] = sorted(set(observed) & deprecated)
|
||
|
|
comparable = [
|
||
|
|
item
|
||
|
|
for item in observed
|
||
|
|
if item not in deprecated
|
||
|
|
and not (ignored_prefixes and item.startswith(ignored_prefixes))
|
||
|
|
]
|
||
|
|
try:
|
||
|
|
codex["comparison"] = _codex_fingerprint_comparison(codex, snapshots["openai-codex"])
|
||
|
|
except ValueError as exc:
|
||
|
|
codex["comparison"] = _id_only_fingerprint_comparison(
|
||
|
|
comparable, snapshots["openai-codex"]
|
||
|
|
)
|
||
|
|
codex["fingerprint_error"] = f"{type(exc).__name__}: {exc}"
|
||
|
|
codex["full_fingerprint_verified"] = bool(codex["comparison"]["exact"])
|
||
|
|
codex["runtime_source_ok"] = any(
|
||
|
|
bool(codex.get("sources", {}).get(source, {}).get("passed"))
|
||
|
|
for source in ("authenticated", "bundled")
|
||
|
|
)
|
||
|
|
result["remote"]["openai-codex"] = codex
|
||
|
|
result["passed"] = (
|
||
|
|
result["passed"]
|
||
|
|
and bool(codex["passed"])
|
||
|
|
and bool(codex["runtime_source_ok"])
|
||
|
|
and bool(codex["full_fingerprint_verified"])
|
||
|
|
)
|
||
|
|
return result
|
||
|
|
|
||
|
|
|
||
|
|
def _reasoning_levels(row: Mapping[str, Any]) -> list[str]:
|
||
|
|
raw = row.get("supported_reasoning_levels") or row.get("supportedReasoningLevels") or []
|
||
|
|
if not isinstance(raw, list):
|
||
|
|
return []
|
||
|
|
values: list[str] = []
|
||
|
|
for item in raw:
|
||
|
|
effort = item.get("effort") if isinstance(item, Mapping) else item
|
||
|
|
if isinstance(effort, str) and effort in CODEX_REASONING_VALUES and effort not in values:
|
||
|
|
values.append(effort)
|
||
|
|
return values
|
||
|
|
|
||
|
|
|
||
|
|
def _metadata_boolean(value: Any, *, default: bool) -> bool:
|
||
|
|
if isinstance(value, bool):
|
||
|
|
return value
|
||
|
|
if isinstance(value, int) and value in {0, 1}:
|
||
|
|
return bool(value)
|
||
|
|
if isinstance(value, str) and value.casefold() in {"true", "false"}:
|
||
|
|
return value.casefold() == "true"
|
||
|
|
return default
|
||
|
|
|
||
|
|
|
||
|
|
def build_codex_discovery_overlay(discovery: Mapping[str, Any]) -> dict[str, Any]:
|
||
|
|
catalog = validated_global_catalog()
|
||
|
|
known_ids = {
|
||
|
|
str(model["upstream_id"])
|
||
|
|
for model in catalog["models"].values()
|
||
|
|
if model.get("inventory") == "openai-codex"
|
||
|
|
}
|
||
|
|
codex_inventory = inventory_baseline().get("inventories", {}).get("openai-codex", {})
|
||
|
|
ignored_prefixes = tuple(codex_inventory.get("ignored_model_prefixes", []))
|
||
|
|
deprecated = set(codex_inventory.get("known_deprecated_models", []))
|
||
|
|
models: dict[str, Any] = {}
|
||
|
|
seen_slugs: set[str] = set()
|
||
|
|
metadata = discovery.get("metadata", {})
|
||
|
|
if not isinstance(metadata, Mapping):
|
||
|
|
metadata = {}
|
||
|
|
for slug in discovery.get("models", []):
|
||
|
|
if not isinstance(slug, str) or not slug:
|
||
|
|
continue
|
||
|
|
if (
|
||
|
|
slug in seen_slugs
|
||
|
|
or slug in known_ids
|
||
|
|
or slug in deprecated
|
||
|
|
or (ignored_prefixes and slug.startswith(ignored_prefixes))
|
||
|
|
):
|
||
|
|
continue
|
||
|
|
seen_slugs.add(slug)
|
||
|
|
row = metadata.get(slug, {})
|
||
|
|
if not isinstance(row, Mapping):
|
||
|
|
row = {}
|
||
|
|
safe = route_catalog_key("codex_chatgpt_builtin", slug, set(models))
|
||
|
|
raw_context = row.get("context_window") or row.get("max_context_window")
|
||
|
|
try:
|
||
|
|
if isinstance(raw_context, bool) or not isinstance(raw_context, (int, str)):
|
||
|
|
raise ValueError
|
||
|
|
context = int(raw_context)
|
||
|
|
except (TypeError, ValueError, OverflowError):
|
||
|
|
continue
|
||
|
|
if not 1024 <= context <= 20_000_000:
|
||
|
|
continue
|
||
|
|
levels = _reasoning_levels(row)
|
||
|
|
if not levels:
|
||
|
|
continue
|
||
|
|
modalities = row.get("input_modalities") or row.get("inputModalities")
|
||
|
|
if not isinstance(modalities, list) or not all(
|
||
|
|
isinstance(item, str) for item in modalities
|
||
|
|
):
|
||
|
|
continue
|
||
|
|
if (
|
||
|
|
not modalities
|
||
|
|
or len(modalities) != len(set(modalities))
|
||
|
|
or set(modalities) - {"text", "image", "audio", "video", "file"}
|
||
|
|
):
|
||
|
|
continue
|
||
|
|
models[safe] = {
|
||
|
|
"maker": "openai",
|
||
|
|
"route": "codex_chatgpt_builtin",
|
||
|
|
"upstream_id": slug,
|
||
|
|
"display_name": str(row.get("display_name") or row.get("displayName") or slug),
|
||
|
|
"description": "Discovered from the local authenticated Codex model catalog",
|
||
|
|
"kind": "vision_chat" if "image" in modalities else "chat",
|
||
|
|
# Discovery alone never grants execution authority. An operator
|
||
|
|
# must review the generated fragment before opting a model into an
|
||
|
|
# agent binding.
|
||
|
|
"agent_compatible": False,
|
||
|
|
"context_window": context,
|
||
|
|
"reasoning_levels": levels,
|
||
|
|
"default_reasoning": "high" if "high" in levels else levels[-1],
|
||
|
|
"modalities": modalities,
|
||
|
|
"output_modalities": ["text"],
|
||
|
|
"tool_calling": False,
|
||
|
|
"parallel_tool_calls": False,
|
||
|
|
"supports_reasoning_summaries": _metadata_boolean(
|
||
|
|
row.get("supports_reasoning_summaries"), default=False
|
||
|
|
),
|
||
|
|
"structured_output": False,
|
||
|
|
"availability": "account-discovered",
|
||
|
|
"capability_confidence": "codex-runtime",
|
||
|
|
"source": "codex-debug-models",
|
||
|
|
"inventory": "openai-codex-discovered",
|
||
|
|
"resource_group": "chatgpt_subscription",
|
||
|
|
}
|
||
|
|
return {"schema_version": MMO_SCHEMA_VERSION, "models": models}
|
||
|
|
|
||
|
|
|
||
|
|
def refresh_discovery(
|
||
|
|
*,
|
||
|
|
remote: bool = True,
|
||
|
|
include_codex: bool = True,
|
||
|
|
install_codex_overlay: bool = False,
|
||
|
|
opencode_url: str | None = None,
|
||
|
|
opencode_zen_url: str | None = None,
|
||
|
|
openrouter_url: str | None = None,
|
||
|
|
zai_coding_url: str | None = None,
|
||
|
|
codex_binary: str = "codex",
|
||
|
|
codex_home: Path | None = None,
|
||
|
|
timeout: float = 10.0,
|
||
|
|
) -> dict[str, Any]:
|
||
|
|
report = verify_catalog(
|
||
|
|
remote=remote,
|
||
|
|
include_codex=include_codex,
|
||
|
|
opencode_url=opencode_url,
|
||
|
|
opencode_zen_url=opencode_zen_url,
|
||
|
|
openrouter_url=openrouter_url,
|
||
|
|
zai_coding_url=zai_coding_url,
|
||
|
|
codex_binary=codex_binary,
|
||
|
|
codex_home=codex_home,
|
||
|
|
timeout=timeout,
|
||
|
|
)
|
||
|
|
output = config_root() / "catalog-discovery.json"
|
||
|
|
atomic_write_json(output, report, 0o600)
|
||
|
|
overlay_path = None
|
||
|
|
overlay_models = 0
|
||
|
|
if install_codex_overlay and include_codex:
|
||
|
|
discovery = report["remote"].get("openai-codex", {})
|
||
|
|
if not discovery.get("passed") or not discovery.get("runtime_source_ok"):
|
||
|
|
raise RuntimeError("Codex discovery did not produce a live model catalog")
|
||
|
|
overlay = build_codex_discovery_overlay(discovery)
|
||
|
|
overlay_path = user_catalog_root() / "90-codex-discovered.toml"
|
||
|
|
overlay_models = len(overlay.get("models", {}))
|
||
|
|
header = (
|
||
|
|
"# Generated by codex-mmo catalog refresh --install-codex-overlay.\n"
|
||
|
|
"# Remove this file to return to the bundled Codex model baseline.\n\n"
|
||
|
|
)
|
||
|
|
atomic_write_text(overlay_path, header + toml_dumps(overlay), 0o600)
|
||
|
|
return {
|
||
|
|
"report_path": str(output),
|
||
|
|
"overlay_path": str(overlay_path) if overlay_path else None,
|
||
|
|
"overlay_models": overlay_models,
|
||
|
|
"report": report,
|
||
|
|
}
|