read a model's card where the card actually lives

Adding `defaults` to the Z-Image-Turbo entry showed they would have been
ignored anyway.

The manager keeps runtime kwargs FLAT and the models.json entry under
`_raw_cfg`, so only keys that `build_runtime_kwargs` promotes exist at the top
level — and `defaults` is not one of them. `model_defaults()` read the flat
dict, so on the image path a card's steps and guidance were silently dropped:
`/v1/models` published them (it reads the entry) while generation used the
generic 30 steps at CFG 7.5. A client that read the listing and trusted the
server to apply it was wrong twice over.

Which means `Z-Image-Turbo-IntoRealism` (10 steps) and `Krea-2-RealDream` (8)
had never had their cards applied, and `call_kwargs` sits in the same place —
MOSS-Image's `uncond_skip_layers` was never passed either.

`card_view()` merges the entry under the flat kwargs, with the flat ones
winning a collision because they are what the loader was given, and both
readers go through it. The video path had already hit this and merged by hand
at its call site; `images.py` never did, and one of the two being right is
what made it invisible. Same lesson as the `gpu_split` and
`sequential_text_encoder` promotions, three lines apart in that same function.

The new test walks the installed catalogue and asserts, for every card
carrying `defaults`, that what the listing publishes is what generation would
use.

Also: a TTS engine's venv no longer reports itself ready on the strength of
the engine package alone. `pip install coqui-tts` brought ninety dependencies
and no torch, the venv looked complete, `/v1/audio/speech/capabilities` said
`env_ready: true`, and the first synthesis failed on `import TTS`. `torch` is
in every family's list now.
Co-Authored-By: 's avatarClaude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014T3rZozi1Ehk96NG1WesgC
parent 37f1040d
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
# Canonical product version for CoderAI — single source of truth. Both the API # Canonical product version for CoderAI — single source of truth. Both the API
# metadata and the admin web UI read from here. # metadata and the admin web UI read from here.
__version__ = "0.5.25" __version__ = "0.5.26"
__tagline__ = "Complete orchestration, distribution and escalation of remotizable advanced inference." __tagline__ = "Complete orchestration, distribution and escalation of remotizable advanced inference."
# Configure the CUDA caching allocator BEFORE torch is imported anywhere. # Configure the CUDA caching allocator BEFORE torch is imported anywhere.
......
...@@ -487,6 +487,32 @@ def _apply_sampler(pipeline, sampler: str) -> str: ...@@ -487,6 +487,32 @@ def _apply_sampler(pipeline, sampler: str) -> str:
return cls_name return cls_name
def card_view(model_config) -> dict:
"""The model's card as one dict: its models.json entry under the runtime kwargs.
The manager keeps runtime kwargs FLAT and the original models.json entry
under ``_raw_cfg``, so a key that was never promoted to a top-level kwarg
is invisible to anything reading the config directly. ``defaults`` is one
of those, and reading it from the flat dict is how a card's steps and
guidance were silently ignored on the image path: ``/v1/models`` published
them (it reads the entry) while generation used the generic 30 at CFG 7.5.
The video path had already hit this and merged by hand at its call site;
doing it here fixes every caller, and is the same lesson as the
``gpu_split`` and ``sequential_text_encoder`` promotions in
``build_runtime_kwargs``.
The flat kwargs win on a collision: they are what the loader was actually
given.
"""
cfg = model_config or {}
raw = cfg.get('_raw_cfg') if isinstance(cfg.get('_raw_cfg'), dict) else None
if not raw:
return dict(cfg) if isinstance(cfg, dict) else {}
merged = dict(raw)
merged.update({k: v for k, v in cfg.items() if k != '_raw_cfg'})
return merged
def model_defaults(model_config) -> dict: def model_defaults(model_config) -> dict:
"""A model card's own recommended settings, from its ``defaults`` entry. """A model card's own recommended settings, from its ``defaults`` entry.
...@@ -510,7 +536,7 @@ def model_defaults(model_config) -> dict: ...@@ -510,7 +536,7 @@ def model_defaults(model_config) -> dict:
rather than guess them. rather than guess them.
""" """
try: try:
d = dict((model_config or {}).get("defaults") or {}) d = dict(card_view(model_config).get("defaults") or {})
except Exception: except Exception:
return {} return {}
return d return d
...@@ -534,7 +560,11 @@ def _model_call_kwargs(pipeline, model_config) -> dict: ...@@ -534,7 +560,11 @@ def _model_call_kwargs(pipeline, model_config) -> dict:
""" """
extra = {} extra = {}
try: try:
extra = dict((model_config or {}).get("call_kwargs") or {}) # Through the card view, for the same reason as model_defaults: a key
# that was never promoted to a top-level runtime kwarg lives only in
# `_raw_cfg`, and `call_kwargs` is one of those -- MOSS-Image's
# `uncond_skip_layers` would never have been applied.
extra = dict(card_view(model_config).get("call_kwargs") or {})
except Exception: except Exception:
return {} return {}
if not extra: if not extra:
......
...@@ -679,11 +679,17 @@ def encode_audio(samples: np.ndarray, sample_rate: int, fmt: str) -> Tuple[bytes ...@@ -679,11 +679,17 @@ def encode_audio(samples: np.ndarray, sample_rate: int, fmt: str) -> Tuple[bytes
# interpreter costs seconds, and a catalogue call that costs seconds is how a # interpreter costs seconds, and a catalogue call that costs seconds is how a
# listing route becomes a loader. # listing route becomes a loader.
#: family -> (module that proves the venv works, venv locator) #: family -> the packages whose presence makes the venv usable.
#:
#: `torch` is in every list on purpose. A `pip install coqui-tts` brought the
#: engine and its ninety dependencies and NO torch, so the venv looked
#: complete, this route reported `env_ready: true`, and the first synthesis
#: failed on `import TTS` — measured on zeiss, 2026-10-29. The engine package
#: alone is not evidence that the venv can run.
_VENV_FAMILIES = { _VENV_FAMILIES = {
"coqui": "TTS", "coqui": ("TTS", "torch"),
"parler": "parler_tts", "parler": ("parler_tts", "torch"),
"melo": "melo", "melo": ("melo", "torch"),
} }
...@@ -714,8 +720,8 @@ def environment_report(model_name: str) -> dict: ...@@ -714,8 +720,8 @@ def environment_report(model_name: str) -> dict:
from pathlib import Path from pathlib import Path
fam = _family(model_name) fam = _family(model_name)
module = _VENV_FAMILIES.get(fam) modules = _VENV_FAMILIES.get(fam)
if not module: if not modules:
return {"needs_venv": False, "env_ready": True, "env_detail": ""} return {"needs_venv": False, "env_ready": True, "env_detail": ""}
d = _family_venv_dir(fam) d = _family_venv_dir(fam)
if d is None: if d is None:
...@@ -731,10 +737,13 @@ def environment_report(model_name: str) -> dict: ...@@ -731,10 +737,13 @@ def environment_report(model_name: str) -> dict:
if not ok: if not ok:
return {"needs_venv": True, "env_ready": False, return {"needs_venv": True, "env_ready": False,
"env_detail": f"{d}: {why or 'it is not there yet'}"} "env_detail": f"{d}: {why or 'it is not there yet'}"}
# The package directory, not an import: a populated venv is the evidence, # The package directories, not an import: a populated venv is the
# and importing into it would cost seconds on a catalogue call. # evidence, and importing into it would cost seconds on a catalogue call.
found = any(Path(d).glob(f"lib/python*/site-packages/{module}")) missing = [m for m in modules
if not found: if not any(Path(d).glob(f"lib/python*/site-packages/{m}"))]
if missing:
return {"needs_venv": True, "env_ready": False, return {"needs_venv": True, "env_ready": False,
"env_detail": f"{d} exists but {module} is not installed in it"} "env_detail": (f"{d} exists but {', '.join(missing)} "
f"{'is' if len(missing) == 1 else 'are'} not "
f"installed in it")}
return {"needs_venv": True, "env_ready": True, "env_detail": str(d)} return {"needs_venv": True, "env_ready": True, "env_detail": str(d)}
...@@ -39,6 +39,24 @@ Precedence, highest first: ...@@ -39,6 +39,24 @@ Precedence, highest first:
4. **what the model's own name says**, when nothing above answered (images); 4. **what the model's own name says**, when nothing above answered (images);
5. the generic floor (30 steps / CFG 7.5 for images, 25 / 7.5 for video). 5. the generic floor (30 steps / CFG 7.5 for images, 25 / 7.5 for video).
### Where the card is read from
`defaults` lives in the models.json **entry**. The manager keeps runtime kwargs
flat and that entry under `_raw_cfg`, so only keys promoted by
`build_runtime_kwargs` exist at the top level — and `defaults` is not one of
them. Reading the flat config therefore finds nothing, which is exactly what
the image path did until 2026-10-29: `/v1/models` published a card's steps and
guidance while generation used the generic floor, so a client that read the
listing and trusted the server was wrong twice. `Z-Image-Turbo-IntoRealism`
(10 steps) and `Krea-2-RealDream` (8) had never had their cards applied, and
`call_kwargs` — MOSS-Image's `uncond_skip_layers` — sits in the same place.
`images.card_view()` merges the entry under the flat kwargs (the flat ones win
on a collision: they are what the loader was given), and both `model_defaults`
and `_model_call_kwargs` read through it. A test walks the installed catalogue
and asserts, for every card carrying `defaults`, that what `/v1/models`
publishes is what generation would use.
### 4: the name, when the card says nothing ### 4: the name, when the card says nothing
A card with no `defaults` and no `acceleration` block used to fall straight to A card with no `defaults` and no `acceleration` block used to fall straight to
......
# CoderAI - OpenAI-compatible API server
# Copyright (C) 2026 Stefy Lanza <stefy@nexlab.net>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
"""A card's `defaults` have to reach the generation, not just `/v1/models`.
Found while adding `defaults` to the Z-Image-Turbo card, 2026-10-29.
The manager keeps runtime kwargs FLAT and the original models.json entry under
`_raw_cfg`. Only keys that `build_runtime_kwargs` promotes exist at the top
level, and `defaults` is not one of them — so `model_defaults()`, reading the
flat dict, got nothing. `/v1/models` published the card's steps and guidance
(it reads the entry) while generation used the generic 30 steps at CFG 7.5.
A client that reads `defaults` from the listing and trusts the server to apply
them was therefore wrong twice over, and the two models whose cards carried
numbers — Z-Image-Turbo-IntoRealism at 10 steps, Krea-2-RealDream at 8 — never
had them applied.
The video path had already hit this and merged `_raw_cfg` by hand at its call
site. Same trap as the `gpu_split` and `sequential_text_encoder` promotions,
which carry the same comment in `build_runtime_kwargs`.
"""
import io
import os
import sys
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from codai.api.images import card_view, model_defaults
_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
def _src(rel):
return io.open(os.path.join(_ROOT, rel), encoding="utf-8").read()
def _runtime_cfg(entry):
"""What the manager stores: flat kwargs plus the entry under _raw_cfg."""
from codai.main import build_runtime_kwargs
return build_runtime_kwargs(entry, "image")
def test_the_card_defaults_survive_the_runtime_config():
entry = {"alias": "Z-Image-Turbo-bf16", "path": "Tongyi-MAI/Z-Image-Turbo",
"precision": "bf16",
"defaults": {"steps": 8, "guidance_scale": 1.0,
"sampler": "flow_euler"}}
cfg = _runtime_cfg(entry)
assert "defaults" not in cfg, (
"this test is pointless if build_runtime_kwargs starts promoting it; "
"update it to match")
got = model_defaults(cfg)
assert got["steps"] == 8 and got["guidance_scale"] == 1.0, (
f"a card with defaults still reads as {got} on the image path")
def test_call_kwargs_survive_it_too():
"""MOSS-Image's `uncond_skip_layers` lives in the same place."""
cfg = _runtime_cfg({"path": "x", "call_kwargs": {"uncond_skip_layers": [22]}})
assert card_view(cfg).get("call_kwargs") == {"uncond_skip_layers": [22]}
def test_a_flat_config_still_works():
"""Not every caller hands over a runtime config; some pass the entry."""
assert model_defaults({"defaults": {"steps": 4}})["steps"] == 4
assert model_defaults({}) == {}
assert model_defaults(None) == {}
def test_the_runtime_kwargs_win_on_a_collision():
"""They are what the loader was actually given."""
merged = card_view({"precision": "fp8", "_raw_cfg": {"precision": "bf16"}})
assert merged["precision"] == "fp8"
def test_both_media_paths_read_the_card_the_same_way():
"""The video path merged by hand at its call site; the image path did not
merge at all. One of them being right is what made this invisible."""
img = _src("codai/api/images.py")
assert "def card_view(" in img
dflt = img.split("def model_defaults", 1)[1].split("\ndef ", 1)[0]
assert "card_view(model_config)" in dflt, (
"model_defaults reads the flat config again")
vid = _src("codai/api/video.py")
assert "model_defaults(" in vid
def test_the_listing_and_the_runtime_agree_on_a_real_card():
"""The regression in one line: what /v1/models publishes is what the
generation uses. Checked against the installed catalogue when there is
one, so it also catches a card whose defaults are malformed."""
import json
path = os.path.expanduser("~/.coderai/models.json")
if not os.path.isfile(path):
return
with open(path, encoding="utf-8") as fh:
data = json.load(fh)
for entry in (data.get("image_models") or []):
card = entry.get("defaults")
if not isinstance(card, dict) or not card:
continue
got = model_defaults(_runtime_cfg(entry))
assert got == card, (
f"{entry.get('alias') or entry.get('path')}: the listing says "
f"{card} and generation would use {got}")
...@@ -171,7 +171,7 @@ def test_a_venv_without_the_package_is_not_ready(tmp_path, monkeypatch): ...@@ -171,7 +171,7 @@ def test_a_venv_without_the_package_is_not_ready(tmp_path, monkeypatch):
monkeypatch.setattr(T, "_family_venv_dir", lambda fam: venv) monkeypatch.setattr(T, "_family_venv_dir", lambda fam: venv)
got = T.environment_report("coqui/XTTS-v2") got = T.environment_report("coqui/XTTS-v2")
assert got["env_ready"] is False assert got["env_ready"] is False
assert "TTS is not installed" in got["env_detail"] assert "TTS" in got["env_detail"] and "not" in got["env_detail"]
def test_a_populated_venv_is_ready_without_importing_anything(tmp_path, monkeypatch): def test_a_populated_venv_is_ready_without_importing_anything(tmp_path, monkeypatch):
...@@ -179,7 +179,8 @@ def test_a_populated_venv_is_ready_without_importing_anything(tmp_path, monkeypa ...@@ -179,7 +179,8 @@ def test_a_populated_venv_is_ready_without_importing_anything(tmp_path, monkeypa
venv = tmp_path / "xtts_venv" venv = tmp_path / "xtts_venv"
venv_env.create_venv(venv) venv_env.create_venv(venv)
site = next(iter(venv.glob("lib/python*/site-packages"))) site = next(iter(venv.glob("lib/python*/site-packages")))
(site / "TTS").mkdir() for pkg in T._VENV_FAMILIES["coqui"]:
(site / pkg).mkdir()
monkeypatch.setattr(T, "_family_venv_dir", lambda fam: venv) monkeypatch.setattr(T, "_family_venv_dir", lambda fam: venv)
def _no_imports(*a, **k): def _no_imports(*a, **k):
...@@ -196,3 +197,27 @@ def test_the_capabilities_route_reports_it(): ...@@ -196,3 +197,27 @@ def test_the_capabilities_route_reports_it():
src = open(os.path.join(root, "codai/api/tts.py"), encoding="utf-8").read() src = open(os.path.join(root, "codai/api/tts.py"), encoding="utf-8").read()
route = src.split("async def speech_capabilities", 1)[1].split("@router", 1)[0] route = src.split("async def speech_capabilities", 1)[1].split("@router", 1)[0]
assert "environment_report" in route assert "environment_report" in route
def test_the_engine_package_alone_is_not_evidence(tmp_path, monkeypatch):
"""`pip install coqui-tts` brought the engine and its ninety dependencies
and NO torch, so the venv looked complete, this route said ready, and the
first synthesis failed on `import TTS` (zeiss, 2026-10-29)."""
from codai.api import venv_env
venv = tmp_path / "xtts_venv"
venv_env.create_venv(venv)
site = next(iter(venv.glob("lib/python*/site-packages")))
(site / "TTS").mkdir()
monkeypatch.setattr(T, "_family_venv_dir", lambda fam: venv)
got = T.environment_report("coqui/XTTS-v2")
assert got["env_ready"] is False
assert "torch" in got["env_detail"], got["env_detail"]
(site / "torch").mkdir()
assert T.environment_report("coqui/XTTS-v2")["env_ready"] is True
def test_every_venv_family_requires_torch():
for fam, modules in T._VENV_FAMILIES.items():
assert "torch" in modules, f"{fam} can report ready without torch"
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment