File size: 8,208 Bytes
8e4f215 3961b2f 8e4f215 3961b2f 8e4f215 8d672b3 8e4f215 8d672b3 8e4f215 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 | """Build the capability gallery: one screenshot per thing the harness can do.
python showcase.py # every capability
python showcase.py --list # list the ids without rendering
python showcase.py --only decisions # render a subset by id substring
The shared state - the catalogue, the renderers, the display - lives in
:mod:`showcase_core`, and the capabilities themselves in :mod:`showcase_catalog`,
so importing this file never makes a second copy of either.
"""
from __future__ import annotations
import json
import sys
import showcase_catalog # noqa: F401 (importing it registers every shot)
from showcase_core import (
CATALOG, GALLERY, ROOT, ensure_display, render_desktop, render_terminal, save,
)
def main(argv: list[str] | None = None) -> int:
args = list(argv if argv is not None else sys.argv[1:])
only = args[args.index("--only") + 1] if "--only" in args else None
wanted = (args[args.index("--ids") + 1].split(",") if "--ids" in args else None)
if "--card" in args:
print("card section written:", write_card(_read_index()).name)
return 0
display = ensure_display()
picked = [s for s in CATALOG
if (s.id in wanted if wanted else (not only or only.lower() in s.id.lower()))]
print(f"display: {display} | {len(picked)}/{len(CATALOG)} capabilities")
if "--list" in args:
for s in picked:
print(f" {s.id:36} {s.kind:8} {s.category}: {s.title}")
return 0
GALLERY.mkdir(exist_ok=True)
if "--reindex" in args or only:
# an index built from the catalogue, not from what this run happened to draw
_write_index()
if "--reindex" in args:
print(f"reindexed {len(_read_index())} pictures in {GALLERY}")
return 0
tmp = ROOT / ".showcase-tmp"
tmp.mkdir(exist_ok=True)
made = []
for s in picked:
try:
image = render_desktop(s, tmp) if s.kind == "desktop" else render_terminal(s, tmp)
path = save(image, s.id)
made.append(s)
print(f" ok {s.id:34} {path.name:34} {image.size[0]}x{image.size[1]}")
except Exception as exc:
print(f" FAIL {s.id:34} {type(exc).__name__}: {exc}")
_write_index()
print(f"wrote {len(made)} images to {GALLERY}")
return 0
def _entry(s):
return {"id": s.id, "category": s.category, "title": s.title, "shows": s.shows,
"note": s.note, "kind": s.kind}
def _write_index():
"""Index every capability whose picture exists, in catalogue order."""
entries = [_entry(s) for s in CATALOG if (GALLERY / f"{s.id}.png").exists()]
(GALLERY / "index.json").write_text(json.dumps(entries, indent=2))
return entries
def _read_index():
return json.loads((GALLERY / "index.json").read_text())
def api_surface() -> tuple[list[str], list[str]]:
"""(callables, constants) - the public surface a picture has to cover.
Introspected rather than hard-coded, so the gallery's promise is checked
against the package instead of against a list someone forgot to update.
"""
import importlib
import inspect
names, constants = [], []
modules = ["actions", "act", "observe", "perception", "brain", "models", "loop",
"safety", "desktop", "demo", "trace", "accessibility", "__main__",
# 2.0: the operator layer. A picture that ignores it would let the gallery's
# promise quietly mean "the pre-2.0 API", which is exactly the kind of claim
# this repository is supposed to be too honest to make.
"diff", "gates", "runner", "skills", "watchdog", "audit"]
for module_name in modules:
module = importlib.import_module(f"tiny_computer_agent.{module_name}")
for name, obj in vars(module).items():
if name.startswith("_"):
continue
if inspect.isfunction(obj) or inspect.isclass(obj):
if getattr(obj, "__module__", "") != module.__name__:
continue # imported, not defined here
names.append(name)
if inspect.isclass(obj):
for member, value in vars(obj).items():
if member.startswith("_"):
continue
if inspect.isfunction(value) or isinstance(value, property):
names.append(f"{name}.{member}")
elif name.isupper() and isinstance(obj, (str, int, float, tuple, frozenset)):
constants.append(name)
return names, constants
def write_card(entries: list[dict]) -> Path:
"""Write the gallery page, and the section the model card embeds.
Every entry carries the callables it demonstrates, so the card can promise -
and a reader can check - that no part of the public API is left without a
picture.
"""
areas: dict[str, list[dict]] = {}
for entry in entries:
areas.setdefault(entry["category"], []).append(entry)
pictured = sorted({name for entry in entries for name in entry["shows"]})
callables, constants = api_surface()
missing = sorted(set(callables) - set(pictured))
if missing:
print(f" WARNING: {len(missing)} public callables are not pictured: {missing}")
lines = [
"## Capability gallery",
"",
f"**{len(entries)} capabilities, one screenshot each - and "
f"{len(set(callables) & set(pictured))} of the package's {len(callables)} public "
f"callables (functions, methods, classes and protocols) appear in at least one of "
f"them**, together with {len(set(constants) & set(pictured))} of its public "
f"constants.",
"",
(f"_Not yet pictured: {len(missing)} callables added in 2.0 - the gate, skill, "
f"runner, diff, watchdog and audit layers. They are covered by tests; the "
f"roadmap tracks the pictures._" if missing else
"_Every public callable in the package is pictured._"),
"",
"Nothing here is a mock-up. Each picture is produced by "
"[`showcase.py`](showcase.py): a command or a call is run for real, and its actual "
"output - or the real desktop it acted on - is what you see. The desktop shots are "
"real screen captures on a real X server (Xvfb, 1280x800), including a real mouse "
"click verified by the target application's own reaction.",
"",
"> **An honest note on the platform.** These were captured on Linux, because the "
"sandbox that builds this card has no Windows: no KVM, no QEMU and no Windows image. "
"The harness is OS-agnostic (pyautogui uses `SendInput` on Windows, and `GrabScreen` "
"falls back to Pillow's `ImageGrab`), so these are the same code paths - but they are "
"not Windows pixels. The Windows twins are generated by the same script in "
"[`.github/workflows/live-desktop.yml`](.github/workflows/live-desktop.yml).",
"",
"Regenerate it with `python showcase.py` (`--list` to see the ids, `--only <id>` for a "
"subset, `--card` to rewrite this section). It runs the calls for real, so a broken "
"capability fails loudly instead of shipping a stale picture.",
"",
"| area | capabilities |",
"|---|---:|",
]
lines += [f"| {area} | {len(items)} |" for area, items in areas.items()]
lines += ["", "---", ""]
for area, items in areas.items():
lines += [f"### {area}", ""]
for entry in items:
lines += [f"**{entry['title']}**", ""]
if entry["note"]:
lines += [f"*{entry['note']}*", ""]
lines += [f'<img src="gallery/{entry["id"]}.png" width="860">', ""]
lines += ["`shows:` " + ", ".join(f"`{name}`" for name in entry["shows"]), ""]
text = "\n".join(lines)
(GALLERY / "_card_section.md").write_text(text)
(GALLERY / "README.md").write_text(
"---\ntitle: Capability gallery\n---\n\n" + text + "\n")
return GALLERY / "_card_section.md"
if __name__ == "__main__":
raise SystemExit(main())
|