Download assets/generate_assets.py from Dermitio/Planner-Cache: direct link, hf CLI and curl.
- Browser
- Download file 19.7 kB
-
https://huggingface.co/Dermitio/Planner-Cache/resolve/main/assets/generate_assets.py
- Command line
-
hf download hf://Dermitio/Planner-Cache/assets/generate_assets.py
-
curl -L -o generate_assets.py https://huggingface.co/Dermitio/Planner-Cache/resolve/main/assets/generate_assets.py
19.7 kB
| """Generate publication assets from recorded Planner Cache artifacts.""" | |
| from __future__ import annotations | |
| import csv | |
| import hashlib | |
| import json | |
| import shutil | |
| import subprocess | |
| import tempfile | |
| from pathlib import Path | |
| import cairosvg | |
| ASSETS = Path(__file__).resolve().parent | |
| PACK_ARTIFACTS = ASSETS.parent / "artifacts" | |
| ROOT = ASSETS.parent if PACK_ARTIFACTS.is_dir() else ASSETS.parents[1] | |
| ARTIFACTS = ROOT / "artifacts" | |
| def load(name: str): | |
| return json.loads((ARTIFACTS / name).read_text()) | |
| def sha256(path: Path) -> str: | |
| digest = hashlib.sha256() | |
| with path.open("rb") as handle: | |
| for block in iter(lambda: handle.read(1024 * 1024), b""): | |
| digest.update(block) | |
| return digest.hexdigest() | |
| def write_csv(name: str, fields: list[str], rows: list[dict[str, object]]) -> None: | |
| with (ASSETS / name).open("w", newline="") as handle: | |
| writer = csv.DictWriter(handle, fieldnames=fields) | |
| writer.writeheader() | |
| writer.writerows(rows) | |
| def architecture_svg() -> str: | |
| return """<svg xmlns="http://www.w3.org/2000/svg" width="1400" height="820" viewBox="0 0 1400 820"> | |
| <defs> | |
| <marker id="arrow" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="8" markerHeight="8" orient="auto-start-reverse"><path d="M 0 0 L 10 5 L 0 10 z" fill="#385170"/></marker> | |
| </defs> | |
| <rect width="1400" height="820" fill="#f8fafc"/> | |
| <text x="700" y="54" text-anchor="middle" font-family="Arial,sans-serif" font-size="34" font-weight="700" fill="#12233f">Planner Cache publication architecture</text> | |
| <text x="700" y="86" text-anchor="middle" font-family="Arial,sans-serif" font-size="18" fill="#50627a">Portable bounded semantic state for frozen language models</text> | |
| <rect x="45" y="125" width="1310" height="220" rx="22" fill="#edf4fb" stroke="#7da2c8" stroke-width="2"/> | |
| <text x="75" y="160" font-family="Arial,sans-serif" font-size="20" font-weight="700" fill="#244d76">Model and runtime owned</text> | |
| <rect x="90" y="195" width="260" height="105" rx="16" fill="#ffffff" stroke="#2c7a7b" stroke-width="3"/> | |
| <text x="220" y="235" text-anchor="middle" font-family="Arial,sans-serif" font-size="24" font-weight="700" fill="#1f5960">Recent KV</text> | |
| <text x="220" y="266" text-anchor="middle" font-family="Arial,sans-serif" font-size="16" fill="#50627a">Exact recent wording</text> | |
| <rect x="570" y="195" width="260" height="105" rx="16" fill="#ffffff" stroke="#315c9b" stroke-width="3"/> | |
| <text x="700" y="235" text-anchor="middle" font-family="Arial,sans-serif" font-size="24" font-weight="700" fill="#244d76">Frozen model</text> | |
| <text x="700" y="266" text-anchor="middle" font-family="Arial,sans-serif" font-size="16" fill="#50627a">Pythia and Gemma proof paths</text> | |
| <rect x="1050" y="195" width="260" height="105" rx="16" fill="#ffffff" stroke="#6856a5" stroke-width="3"/> | |
| <text x="1180" y="229" text-anchor="middle" font-family="Arial,sans-serif" font-size="22" font-weight="700" fill="#55428e">History and tools</text> | |
| <text x="1180" y="259" text-anchor="middle" font-family="Arial,sans-serif" font-size="15" fill="#50627a">Archive and retrieval systems</text> | |
| <text x="1180" y="282" text-anchor="middle" font-family="Arial,sans-serif" font-size="15" fill="#50627a">Outside Planner Cache core</text> | |
| <rect x="45" y="390" width="1310" height="350" rx="22" fill="#fff7ed" stroke="#cf8a49" stroke-width="2"/> | |
| <text x="75" y="425" font-family="Arial,sans-serif" font-size="20" font-weight="700" fill="#91501e">Planner Cache owned</text> | |
| <rect x="85" y="442" width="245" height="32" rx="12" fill="#fffdf8" stroke="#b36a2e" stroke-width="2" stroke-dasharray="7 5"/> | |
| <text x="207" y="464" text-anchor="middle" font-family="Arial,sans-serif" font-size="15" font-weight="700" fill="#8f4d1e">Hidden post-turn review</text> | |
| <rect x="85" y="485" width="245" height="125" rx="16" fill="#ffffff" stroke="#b36a2e" stroke-width="3"/> | |
| <text x="207" y="527" text-anchor="middle" font-family="Arial,sans-serif" font-size="25" font-weight="700" fill="#8f4d1e">P-cache</text> | |
| <text x="207" y="558" text-anchor="middle" font-family="Arial,sans-serif" font-size="15" fill="#50627a">Bounded mutable state</text> | |
| <text x="207" y="582" text-anchor="middle" font-family="Arial,sans-serif" font-size="15" fill="#50627a">Canonical P protocol</text> | |
| <rect x="405" y="485" width="245" height="125" rx="16" fill="#ffffff" stroke="#3e7c59" stroke-width="3"/> | |
| <text x="527" y="527" text-anchor="middle" font-family="Arial,sans-serif" font-size="25" font-weight="700" fill="#2d6848">Universal router</text> | |
| <text x="527" y="558" text-anchor="middle" font-family="Arial,sans-serif" font-size="15" fill="#50627a">Canonical selection</text> | |
| <text x="527" y="582" text-anchor="middle" font-family="Arial,sans-serif" font-size="15" fill="#50627a">Model independent</text> | |
| <rect x="725" y="485" width="245" height="125" rx="16" fill="#ffffff" stroke="#315c9b" stroke-width="3"/> | |
| <text x="847" y="527" text-anchor="middle" font-family="Arial,sans-serif" font-size="25" font-weight="700" fill="#244d76">.ttl / .ltl</text> | |
| <text x="847" y="558" text-anchor="middle" font-family="Arial,sans-serif" font-size="15" fill="#50627a">Compatibility boundary</text> | |
| <text x="847" y="582" text-anchor="middle" font-family="Arial,sans-serif" font-size="15" fill="#50627a">Semantic / lexical</text> | |
| <rect x="1045" y="485" width="245" height="125" rx="16" fill="#ffffff" stroke="#6856a5" stroke-width="3"/> | |
| <text x="1167" y="527" text-anchor="middle" font-family="Arial,sans-serif" font-size="25" font-weight="700" fill="#55428e">P-package</text> | |
| <text x="1167" y="558" text-anchor="middle" font-family="Arial,sans-serif" font-size="15" fill="#50627a">Disk resident personality</text> | |
| <text x="1167" y="582" text-anchor="middle" font-family="Arial,sans-serif" font-size="15" fill="#50627a">Portable .ppkg</text> | |
| <path d="M 330 548 L 395 548" stroke="#385170" stroke-width="4" fill="none" marker-end="url(#arrow)"/> | |
| <path d="M 207 474 L 207 482" stroke="#b36a2e" stroke-width="3" fill="none" marker-end="url(#arrow)"/> | |
| <path d="M 700 300 C 650 375 350 385 225 439" stroke="#b36a2e" stroke-width="2" stroke-dasharray="8 6" fill="none" marker-end="url(#arrow)"/> | |
| <path d="M 650 548 L 715 548" stroke="#385170" stroke-width="4" fill="none" marker-end="url(#arrow)"/> | |
| <path d="M 1045 620 C 930 695 585 695 527 620" stroke="#6856a5" stroke-width="3" stroke-dasharray="10 7" fill="none" marker-end="url(#arrow)"/> | |
| <path d="M 847 485 C 820 405 755 335 710 302" stroke="#315c9b" stroke-width="4" fill="none" marker-end="url(#arrow)"/> | |
| <path d="M 350 247 L 558 247" stroke="#2c7a7b" stroke-width="3" fill="none" marker-end="url(#arrow)"/> | |
| <text x="700" y="782" text-anchor="middle" font-family="Arial,sans-serif" font-size="16" fill="#50627a">Canonical files never store base weights, token IDs, conversation text, or model-native hidden vectors</text> | |
| </svg> | |
| """ | |
| def line_plot_svg(title: str, subtitle: str, series: list[tuple[str, str, list[tuple[float, float]]]], x_label: str, y_label: str) -> str: | |
| width = 1100 | |
| height = 650 | |
| left = 105 | |
| right = 55 | |
| top = 115 | |
| bottom = 90 | |
| plot_width = width - left - right | |
| plot_height = height - top - bottom | |
| all_x = [x for _, _, points in series for x, _ in points] | |
| all_y = [y for _, _, points in series for _, y in points] | |
| x_min = min(all_x) | |
| x_max = max(all_x) | |
| y_min = min(0.0, min(all_y)) | |
| y_max = max(all_y) * 1.08 | |
| def px(value: float) -> float: | |
| return left + (value - x_min) / (x_max - x_min) * plot_width | |
| def py(value: float) -> float: | |
| return top + plot_height - (value - y_min) / (y_max - y_min) * plot_height | |
| parts = [f'<svg xmlns="http://www.w3.org/2000/svg" width="{width}" height="{height}" viewBox="0 0 {width} {height}">'] | |
| parts.append('<rect width="1100" height="650" fill="#ffffff"/>') | |
| parts.append(f'<text x="550" y="42" text-anchor="middle" font-family="Arial,sans-serif" font-size="28" font-weight="700" fill="#12233f">{title}</text>') | |
| parts.append(f'<text x="550" y="72" text-anchor="middle" font-family="Arial,sans-serif" font-size="16" fill="#50627a">{subtitle}</text>') | |
| for tick in range(6): | |
| value = y_min + (y_max - y_min) * tick / 5 | |
| y = py(value) | |
| parts.append(f'<line x1="{left}" y1="{y:.2f}" x2="{width-right}" y2="{y:.2f}" stroke="#dbe4ee" stroke-width="1"/>') | |
| parts.append(f'<text x="{left-12}" y="{y+5:.2f}" text-anchor="end" font-family="Arial,sans-serif" font-size="13" fill="#50627a">{value:.1f}</text>') | |
| x_ticks = sorted(set(all_x)) | |
| for value in x_ticks: | |
| x = px(value) | |
| parts.append(f'<line x1="{x:.2f}" y1="{top}" x2="{x:.2f}" y2="{top+plot_height}" stroke="#eef2f7" stroke-width="1"/>') | |
| parts.append(f'<text x="{x:.2f}" y="{top+plot_height+27}" text-anchor="middle" font-family="Arial,sans-serif" font-size="13" fill="#50627a">{int(value)}</text>') | |
| parts.append(f'<line x1="{left}" y1="{top+plot_height}" x2="{width-right}" y2="{top+plot_height}" stroke="#385170" stroke-width="2"/>') | |
| parts.append(f'<line x1="{left}" y1="{top}" x2="{left}" y2="{top+plot_height}" stroke="#385170" stroke-width="2"/>') | |
| for index, (label, color, points) in enumerate(series): | |
| coordinates = " ".join(f"{px(x):.2f},{py(y):.2f}" for x, y in points) | |
| parts.append(f'<polyline points="{coordinates}" fill="none" stroke="{color}" stroke-width="4"/>') | |
| for x, y in points: | |
| parts.append(f'<circle cx="{px(x):.2f}" cy="{py(y):.2f}" r="5" fill="{color}"/>') | |
| legend_x = left + index * 280 | |
| parts.append(f'<line x1="{legend_x}" y1="{height-30}" x2="{legend_x+32}" y2="{height-30}" stroke="{color}" stroke-width="4"/>') | |
| parts.append(f'<text x="{legend_x+42}" y="{height-25}" font-family="Arial,sans-serif" font-size="14" fill="#28384e">{label}</text>') | |
| parts.append(f'<text x="{left+plot_width/2}" y="{height-55}" text-anchor="middle" font-family="Arial,sans-serif" font-size="15" fill="#28384e">{x_label}</text>') | |
| parts.append(f'<text x="28" y="{top+plot_height/2}" text-anchor="middle" transform="rotate(-90 28 {top+plot_height/2})" font-family="Arial,sans-serif" font-size="15" fill="#28384e">{y_label}</text>') | |
| parts.append('</svg>') | |
| return "\n".join(parts) + "\n" | |
| def main() -> None: | |
| audit = load("active-system-audit.json") | |
| cuda = load("active-system-cuda-attribution.json") | |
| split = load("phase-b-split-translator.json") | |
| personality = load("phase-b-personality-package.json") | |
| gemma = load("gemma4-e4b-q8-causal.json") | |
| vram = load("vram-comparison.json") | |
| router_rows = [] | |
| for count, values in sorted(audit["router_scaling"]["measurements"].items(), key=lambda item: int(item[0])): | |
| router_rows.append({ | |
| "slots": int(count), | |
| "top1_accuracy": values["top1_accuracy"], | |
| "top4_recall": values["top4_recall"], | |
| "mrr": values["mrr"], | |
| "routing_latency_ms": values["latency_seconds"] * 1000, | |
| }) | |
| write_csv("router_scaling.csv", list(router_rows[0]), router_rows) | |
| ppkg_rows = [] | |
| for count, values in sorted(audit["ppkg_scaling"].items(), key=lambda item: int(item[0])): | |
| ppkg_rows.append({ | |
| "entries": int(count), | |
| "disk_bytes": values["disk_bytes"], | |
| "checksum_ms": values["checksum_seconds"] * 1000, | |
| "db_open_ms": values["db_open_seconds"] * 1000, | |
| "routing_header_ms": values["routing_header_wall_seconds"] * 1000, | |
| "row_hydration_ms": values["row_hydration_wall_seconds"] * 1000, | |
| "canonical_conversion_ms": values["canonical_conversion_wall_seconds"] * 1000, | |
| "candidate_headers": values["candidate_headers"], | |
| "entries_loaded": values["entries_loaded"], | |
| "logical_bytes_read": values["logical_bytes_read"], | |
| "inactive_vram_bytes": values["inactive_vram_bytes"], | |
| }) | |
| write_csv("ppkg_scaling.csv", list(ppkg_rows[0]), ppkg_rows) | |
| causal_rows = [] | |
| for condition, values in cuda["causal"].items(): | |
| causal_rows.append({ | |
| "condition": condition, | |
| "selected_state": values["selected_state"] or "", | |
| "router_score": "" if values["router_score"] is None else values["router_score"], | |
| "router_accepted": values["router_accepted"], | |
| "gate": values["gate"], | |
| "alice_logit": values["alice_logit"], | |
| "bob_logit": values["bob_logit"], | |
| "generated": values["generated"].replace("\n", "\\n"), | |
| "kl_from_base": values["kl_from_base"], | |
| "latency_ms": cuda["latency_seconds"][condition] * 1000, | |
| }) | |
| write_csv("causal_conditions.csv", list(causal_rows[0]), causal_rows) | |
| gemma_rows = [] | |
| for condition, values in gemma["conditions"].items(): | |
| gemma_rows.append({ | |
| "condition": condition, | |
| "router_accepted": values["router_accepted"], | |
| "gate": values["gate"], | |
| "strength": values["strength"], | |
| "alice_logit": values["alice_logit"], | |
| "bob_logit": values["bob_logit"], | |
| "alice_probability": values["alice_probability"], | |
| "bob_probability": values["bob_probability"], | |
| "generated": values["generated"].replace("\n", "\\n"), | |
| "kl_from_base": values["kl_from_base"], | |
| "max_abs_logit_difference_from_base": values["max_abs_logit_difference_from_base"], | |
| "latency_ms": values["latency_ms"], | |
| }) | |
| write_csv("gemma_causal_conditions.csv", list(gemma_rows[0]), gemma_rows) | |
| vram_rows = [] | |
| for values in vram["results"]: | |
| vram_rows.append({ | |
| "workload_tokens": values["prompt_tokens"], | |
| "condition": values["condition"], | |
| "generated_tokens": values["generated_tokens"], | |
| "p_cache_slots": values["p_cache_slots"], | |
| "p_cache_canonical_bytes": values["p_cache_canonical_bytes"], | |
| "retained_kv_cache_bytes": values["retained_kv_cache_bytes"], | |
| "baseline_allocated_bytes": values["baseline_allocated_bytes"], | |
| "peak_allocated_bytes": values["peak_allocated_bytes"], | |
| "peak_reserved_bytes": values["peak_reserved_bytes"], | |
| "incremental_peak_allocated_bytes": values["incremental_peak_allocated_bytes"], | |
| "incremental_peak_reserved_bytes": values["incremental_peak_reserved_bytes"], | |
| "runtime_seconds": values["runtime_seconds"], | |
| "failure": "" if values["failure"] is None else values["failure"]["type"], | |
| }) | |
| write_csv("vram_comparison.csv", list(vram_rows[0]), vram_rows) | |
| architecture = architecture_svg() | |
| (ASSETS / "architecture.svg").write_text(architecture) | |
| qpdf = shutil.which("qpdf") | |
| if qpdf is None: | |
| raise RuntimeError("qpdf is required to normalize publication PDF metadata") | |
| with tempfile.TemporaryDirectory(prefix="planner-cache-publishing-") as temporary: | |
| raw_pdf = Path(temporary) / "architecture.raw.pdf" | |
| cairosvg.svg2pdf(bytestring=architecture.encode(), write_to=str(raw_pdf)) | |
| subprocess.run( | |
| [qpdf, "--remove-info", "--remove-metadata", "--deterministic-id", str(raw_pdf), str(ASSETS / "architecture.pdf")], | |
| check=True, | |
| ) | |
| (ASSETS / "architecture.mmd").write_text("""flowchart LR | |
| subgraph Runtime[Model and runtime owned] | |
| KV[Recent KV] | |
| LM[Frozen model] | |
| EXT[History and tool systems] | |
| end | |
| subgraph Planner[Planner Cache owned] | |
| P[P-cache] | |
| R[Universal router] | |
| C{Compatibility boundary} | |
| N[Native P] | |
| TTL[.ttl semantic/internal] | |
| LTL[.ltl lexical/output] | |
| PKG[P-package .ppkg] | |
| REVIEW[Hidden post-turn review] | |
| end | |
| KV --> LM | |
| LM -. side-channel after visible reply .-> REVIEW --> P | |
| P --> R --> C | |
| C --> N --> LM | |
| C --> TTL --> LM | |
| C --> LTL --> LM | |
| PKG -. selected canonical state .-> R | |
| EXT -. external evidence .-> LM | |
| """) | |
| router_points = [(float(row["slots"]), float(row["top1_accuracy"]) * 100) for row in router_rows] | |
| legacy = audit["router_scaling"]["immutable_pre_fix_baseline"] | |
| legacy_points = [(float(key), float(value) * 100) for key, value in sorted(legacy.items(), key=lambda item: int(item[0]))] | |
| (ASSETS / "router_scaling.svg").write_text(line_plot_svg( | |
| "Canonical router scaling", | |
| "Post-audit identity-safe storage compared with the immutable pre-fix baseline", | |
| [("Post-audit top-1", "#2c7a7b", router_points), ("Pre-fix top-1", "#b36a2e", legacy_points)], | |
| "Configured P slots", | |
| "Top-1 accuracy percent", | |
| )) | |
| ppkg_points = [(float(row["entries"]), float(row["routing_header_ms"])) for row in ppkg_rows] | |
| (ASSETS / "ppkg_lookup.svg").write_text(line_plot_svg( | |
| "P-package indexed lookup", | |
| "Bounded header routing while package contents grow on disk", | |
| [("Routing header latency", "#6856a5", ppkg_points)], | |
| "Package entries", | |
| "Latency ms", | |
| )) | |
| condition_labels = ( | |
| ("p_cache_only", "P-cache only", "#b36a2e"), | |
| ("kv_only", "Retained KV only", "#2c7a7b"), | |
| ("p_cache_plus_kv", "P-cache plus KV", "#6856a5"), | |
| ) | |
| vram_series = [] | |
| for condition, label, color in condition_labels: | |
| points = [ | |
| ( | |
| float(row["workload_tokens"]), | |
| float(row["incremental_peak_allocated_bytes"]) / (1024 * 1024), | |
| ) | |
| for row in vram_rows if row["condition"] == condition | |
| ] | |
| vram_series.append((label, color, points)) | |
| (ASSETS / "vram_comparison.svg").write_text(line_plot_svg( | |
| "Matched P-cache and retained-KV VRAM", | |
| "Frozen Pythia-1.4B, batch 1, float16 base, 8 greedy generated tokens", | |
| vram_series, | |
| "Prompt tokens and configured P slots", | |
| "Incremental peak allocated MiB", | |
| )) | |
| manifest = { | |
| "generated_from": { | |
| name: sha256(ARTIFACTS / name) | |
| for name in ( | |
| "active-system-audit.json", | |
| "active-system-cuda-attribution.json", | |
| "phase-b-factorized-representation.json", | |
| "phase-b-personality-package.json", | |
| "phase-b-split-translator.json", | |
| "ppkg-100k-profile.json", | |
| "gemma4-e4b-q8-causal.json", | |
| "gemma-native-prompt-equivalence.json", | |
| "post-turn-memory-review-acceptance.json", | |
| "debug-actions-profile.json", | |
| "vram-comparison.json", | |
| ) | |
| }, | |
| "public_binary_artifacts": { | |
| name: sha256(ARTIFACTS / name) | |
| for name in ( | |
| "canonical-p-v1.router", | |
| "pythia-1.4b-final-layer.ttl", | |
| "personality-proof.ppkg", | |
| "gemma4-e4b-q8-llama.ltl", | |
| ) | |
| }, | |
| "selected_configuration": { | |
| "base_model": cuda["base_model"], | |
| "ttl_parameters": ( | |
| audit["ttl_profile"]["cpu"]["parameter_count"] | |
| if "ttl_profile" in audit | |
| else audit["translate_profile"]["cpu"]["parameter_count"] | |
| ), | |
| "canonical_protocol": "pcm-canonical-p-v1", | |
| "router_format": "pcm-canonical-router-v1", | |
| "ttl_format": "planner-cache-ttl-v1", | |
| "ppkg_format": personality["format"], | |
| "ppkg_protocol": personality["protocol"], | |
| "selected_attachment": split["selected_variant"], | |
| "gemma_model": gemma["model"]["name"], | |
| "gemma_ltl_format": "planner-cache-ltl-v1", | |
| "gemma_ltl_parameters": 0, | |
| }, | |
| } | |
| (ASSETS / "EVIDENCE_MANIFEST.json").write_text(json.dumps(manifest, indent=2, sort_keys=True) + "\n") | |
| if __name__ == "__main__": | |
| main() | |