Download infer_nested_model.py from Alienanthony/Nested_Mamba_3Level: direct link, hf CLI and curl.
- Browser
- Download file 26.3 kB
-
https://huggingface.co/Alienanthony/Nested_Mamba_3Level/resolve/main/infer_nested_model.py
- Command line
-
hf download hf://Alienanthony/Nested_Mamba_3Level/infer_nested_model.py
-
curl -L -o infer_nested_model.py https://huggingface.co/Alienanthony/Nested_Mamba_3Level/resolve/main/infer_nested_model.py
26.3 kB
| #!/usr/bin/env python3 | |
| """Cached byte generation for two- and three-level nested Mamba checkpoints.""" | |
| from __future__ import annotations | |
| import argparse | |
| import base64 | |
| import gc | |
| import io | |
| import json | |
| import time | |
| from pathlib import Path | |
| from typing import Dict, List, Optional | |
| import torch | |
| from nested_inference_tools import generate_bytes, load_nested_checkpoint | |
| def parse_args() -> argparse.Namespace: | |
| parser = argparse.ArgumentParser(description=__doc__) | |
| parser.add_argument( | |
| "--checkpoint", | |
| default=".", | |
| help="Hugging Face model directory (default: current directory) or legacy last.pt.", | |
| ) | |
| prompt = parser.add_mutually_exclusive_group() | |
| prompt.add_argument("--prompt", default="", help="UTF-8 prompt text.") | |
| prompt.add_argument("--prompt-file", help="Read prompt bytes from this file.") | |
| parser.add_argument("--max-new-bytes", type=int, default=256) | |
| parser.add_argument("--temperature", type=float, default=0.8) | |
| parser.add_argument("--top-p", type=float, default=0.9) | |
| parser.add_argument("--top-k", type=int, default=0) | |
| parser.add_argument("--repeat-penalty", type=float, default=1.05) | |
| parser.add_argument("--repeat-window", type=int, default=256) | |
| parser.add_argument("--greedy", action="store_true") | |
| parser.add_argument("--seed", type=int, default=1234) | |
| parser.add_argument( | |
| "--precision", | |
| choices=["fp16", "bf16", "fp32"], | |
| default="bf16", | |
| help=( | |
| "Model weight/activation precision. BF16 is the safe default for " | |
| "Mamba-2's long cached rollouts; use FP16 only for checkpoints and " | |
| "GPUs verified to remain finite." | |
| ), | |
| ) | |
| parser.add_argument("--device", default="cuda:0", help="Single-device inference target.") | |
| parser.add_argument("--fine-device", default=None) | |
| parser.add_argument("--nested-devices", default=None) | |
| parser.add_argument("--tertiary-device", default=None) | |
| parser.add_argument("--use-saved-placement", action="store_true") | |
| parser.add_argument("--output", help="Optional raw-byte output file.") | |
| parser.add_argument("--stats-json", help="Optional JSON statistics output.") | |
| parser.add_argument( | |
| "--image-output-dir", | |
| help=( | |
| "Extract complete generated P6 PPM images, convert them to PNG, and " | |
| "write them to this directory. Images are also embedded in --html-output." | |
| ), | |
| ) | |
| parser.add_argument("--html-output", "--output-html", dest="html_output", help="Write a self-contained prompt/response report with toggleable L2/L3 influence coloring.") | |
| return parser.parse_args() | |
| def _ppm_token(data: bytes, position: int) -> tuple[bytes, int]: | |
| """Read one whitespace/comment-delimited PPM header token.""" | |
| size = len(data) | |
| while position < size: | |
| if data[position] in b" \t\r\n": | |
| position += 1 | |
| continue | |
| if data[position] == ord("#"): | |
| newline = data.find(b"\n", position) | |
| if newline < 0: | |
| raise ValueError("unterminated PPM header comment") | |
| position = newline + 1 | |
| continue | |
| break | |
| start = position | |
| while position < size and data[position] not in b" \t\r\n#": | |
| position += 1 | |
| if position == start: | |
| raise ValueError("missing PPM header token") | |
| return data[start:position], position | |
| def extract_ppm_images(data: bytes) -> tuple[List[Dict[str, object]], List[str]]: | |
| """Extract complete P6 RGB images from an arbitrary generated byte stream.""" | |
| try: | |
| from PIL import Image | |
| except ImportError as error: | |
| return [], [f"Pillow is required to render generated images: {error}"] | |
| images: List[Dict[str, object]] = [] | |
| warnings: List[str] = [] | |
| search_from = 0 | |
| while len(images) < 32: | |
| start = data.find(b"P6", search_from) | |
| if start < 0: | |
| break | |
| search_from = start + 2 | |
| if start > 0 and data[start - 1] not in b" \t\r\n>": | |
| continue | |
| if start + 2 >= len(data) or data[start + 2] not in b" \t\r\n": | |
| continue | |
| try: | |
| width_token, position = _ppm_token(data, start + 2) | |
| height_token, position = _ppm_token(data, position) | |
| maximum_token, position = _ppm_token(data, position) | |
| width, height, maximum = int(width_token), int(height_token), int(maximum_token) | |
| if width < 1 or height < 1 or width * height > 16_777_216: | |
| raise ValueError(f"unsafe dimensions {width}x{height}") | |
| if maximum != 255: | |
| raise ValueError(f"unsupported maximum channel value {maximum}; expected 255") | |
| if position >= len(data) or data[position] not in b" \t\r\n": | |
| raise ValueError("missing whitespace before PPM pixel payload") | |
| # The delimiter is one whitespace unit. Treat CRLF as one unit so | |
| # the first pixel is never shifted on Windows-produced headers. | |
| pixel_start = position + 1 | |
| if data[position] == ord("\r") and pixel_start < len(data) and data[pixel_start] == ord("\n"): | |
| pixel_start += 1 | |
| pixel_bytes = width * height * 3 | |
| pixel_end = pixel_start + pixel_bytes | |
| if pixel_end > len(data): | |
| warnings.append( | |
| f"Incomplete PPM at byte {start}: {width}x{height} needs " | |
| f"{pixel_bytes:,} RGB bytes, but only {max(0, len(data)-pixel_start):,} remain." | |
| ) | |
| continue | |
| image = Image.frombytes("RGB", (width, height), data[pixel_start:pixel_end]) | |
| encoded = io.BytesIO() | |
| image.save(encoded, format="PNG", optimize=True) | |
| images.append( | |
| { | |
| "width": width, | |
| "height": height, | |
| "start": start, | |
| "end": pixel_end, | |
| "png": encoded.getvalue(), | |
| } | |
| ) | |
| search_from = pixel_end | |
| except (TypeError, ValueError) as error: | |
| warnings.append(f"Invalid PPM candidate at byte {start}: {error}.") | |
| return images, warnings | |
| def image_report_payload(images: List[Dict[str, object]], warnings: List[str]) -> Dict[str, object]: | |
| return { | |
| "images": [ | |
| { | |
| "width": int(item["width"]), | |
| "height": int(item["height"]), | |
| "start": int(item["start"]), | |
| "end": int(item["end"]), | |
| "data_uri": "data:image/png;base64," + base64.b64encode(item["png"]).decode("ascii"), | |
| } | |
| for item in images | |
| ], | |
| "warnings": list(warnings), | |
| } | |
| def write_extracted_images( | |
| directory: Path, images: List[Dict[str, object]], *, prefix: str | |
| ) -> List[str]: | |
| directory.mkdir(parents=True, exist_ok=True) | |
| paths: List[str] = [] | |
| for index, item in enumerate(images, 1): | |
| path = directory / f"{prefix}_{index:03d}_{item['width']}x{item['height']}.png" | |
| path.write_bytes(item["png"]) | |
| paths.append(str(path)) | |
| return paths | |
| def attributed_utf8_segments( | |
| data: bytes, attributions: List[Dict[str, object]] | |
| ) -> List[Dict[str, object]]: | |
| """Group byte attribution into valid UTF-8 characters without losing data.""" | |
| segments: List[Dict[str, object]] = [] | |
| index = 0 | |
| while index < len(data): | |
| first = data[index] | |
| width = ( | |
| 1 | |
| if first < 0x80 | |
| else 2 | |
| if 0xC2 <= first <= 0xDF | |
| else 3 | |
| if 0xE0 <= first <= 0xEF | |
| else 4 | |
| if 0xF0 <= first <= 0xF4 | |
| else 1 | |
| ) | |
| chunk = data[index : index + width] | |
| try: | |
| text = chunk.decode("utf-8", "strict") | |
| except UnicodeDecodeError: | |
| width = 1 | |
| chunk = data[index : index + 1] | |
| text = chunk.decode("utf-8", "replace") | |
| values = attributions[index : index + width] | |
| count = max(1, len(values)) | |
| segments.append( | |
| { | |
| "text": text, | |
| "bytes": list(chunk), | |
| "level2_active": any(bool(item.get("level2_active")) for item in values), | |
| "level3_active": any(bool(item.get("level3_active")) for item in values), | |
| "level2_delta_logp": sum( | |
| float(item.get("level2_delta_logp", 0.0)) for item in values | |
| ) | |
| / count, | |
| "level3_delta_logp": sum( | |
| float(item.get("level3_delta_logp", 0.0)) for item in values | |
| ) | |
| / count, | |
| "hierarchy_delta_logp": sum( | |
| float(item.get("hierarchy_delta_logp", 0.0)) for item in values | |
| ) | |
| / count, | |
| } | |
| ) | |
| index += width | |
| return segments | |
| def make_generation_html( | |
| prompt: bytes, | |
| generated: bytes, | |
| attributions: List[Dict[str, object]], | |
| stats: Dict[str, object], | |
| comparisons: Optional[List[Dict[str, object]]] = None, | |
| image_report: Optional[Dict[str, object]] = None, | |
| ) -> str: | |
| segments = attributed_utf8_segments(generated, attributions) | |
| payload = json.dumps( | |
| { | |
| "prompt": prompt.decode("utf-8", "replace"), | |
| "segments": segments, | |
| "stats": stats, | |
| "comparisons": comparisons or [], | |
| "image_report": image_report or {"images": [], "warnings": []}, | |
| }, | |
| separators=(",", ":"), | |
| ).replace("</", "<\\/") | |
| return f"""<!doctype html> | |
| <html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"> | |
| <title>Nested Mamba generation influence</title> | |
| <style> | |
| :root{{--bg:#090d17;--panel:#111a2b;--line:#273652;--text:#e7edf9;--muted:#91a0b8;--prompt:#d6deec;--response:#63a4ff;--fine:#51d6ca;--l2:#ffbe55;--l3:#a78bfa;--negative:#fb7185}} | |
| *{{box-sizing:border-box}}body{{margin:0;background:linear-gradient(145deg,#080c15,#11192b);color:var(--text);font:14px/1.5 system-ui,sans-serif}}main{{max-width:1200px;margin:auto;padding:28px}} | |
| h1{{margin:0 0 6px;font-size:27px}}p{{color:var(--muted)}}.toolbar,.panel{{background:#111a2bf2;border:1px solid var(--line);border-radius:12px}} | |
| .toolbar{{display:flex;align-items:center;gap:18px;flex-wrap:wrap;padding:13px 16px;margin:18px 0}}label{{display:flex;align-items:center;gap:8px;font-weight:650}}input{{accent-color:var(--l3)}} | |
| .legend{{display:flex;gap:14px;flex-wrap:wrap;color:var(--muted);font-size:12px}}.dot{{width:10px;height:10px;border-radius:50%;display:inline-block;margin-right:5px}} | |
| .panel{{padding:20px}}.text{{white-space:pre-wrap;overflow-wrap:anywhere;font:16px/1.65 ui-monospace,SFMono-Regular,Menlo,Consolas,monospace}}.responses{{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:12px;margin-top:16px}}.response-card{{min-width:0;background:#0c1321;border:1px solid var(--line);border-radius:10px;padding:14px}}.response-card h2{{font:700 14px system-ui,sans-serif;margin:0 0 4px}}.response-meta{{color:var(--muted);font:11px system-ui,sans-serif;margin-bottom:11px}} | |
| .prompt{{color:var(--prompt)}}.generated{{color:var(--response);transition:color .15s,opacity .15s}}.divider{{display:block;color:var(--muted);font:12px system-ui,sans-serif;margin:18px 0 7px;border-top:1px solid var(--line);padding-top:10px}} | |
| .cards{{display:grid;grid-template-columns:repeat(auto-fit,minmax(170px,1fr));gap:10px;margin-top:14px}}.card{{background:#0c1321;border:1px solid var(--line);border-radius:9px;padding:11px}}.name{{color:var(--muted);font-size:11px;text-transform:uppercase}}.value{{font-size:18px;font-weight:700}} | |
| .note{{border-left:3px solid var(--l2);padding:9px 12px;background:#ffbe550d}} | |
| .image-panel{{margin-top:16px}}.image-galleries{{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:12px}}.image-gallery{{background:#0c1321;border:1px solid var(--line);border-radius:10px;padding:12px;min-width:0}}.image-gallery h2{{font-size:14px;margin:0 0 9px}}.image-item{{margin:0 0 12px}}.image-item img{{display:block;max-width:100%;height:auto;image-rendering:auto;border:1px solid #354766;background:#05070b}}.image-item figcaption,.image-status{{color:var(--muted);font-size:11px;margin-top:5px;white-space:pre-wrap}} | |
| @media(max-width:900px){{.responses{{grid-template-columns:1fr}}}} | |
| </style></head><body><main> | |
| <h1>Nested Mamba generation influence</h1><p id="subtitle"></p> | |
| <div class="toolbar"><label><input id="influence" type="checkbox" checked> Color generated text by hierarchy influence</label><div class="legend"><span><i class="dot" style="background:var(--fine)"></i>fine only</span><span><i class="dot" style="background:var(--l2)"></i>L2 dominant</span><span><i class="dot" style="background:var(--l3)"></i>L3 dominant</span><span><i class="dot" style="background:var(--negative)"></i>dominant branch reduced selected-byte probability</span></div></div> | |
| <p class="note">Color is a decoder counterfactual, not merely an activation marker. Positive delta means the cached dynamic level increased the selected byte's log probability relative to replacing that level with its beginning-of-document state. Hover text for exact values.</p> | |
| <section class="panel"><div class="text"><span class="divider">PROMPT SHARED BY ALL ROLLOUTS</span><span class="prompt" id="prompt"></span></div><div class="responses"><article class="response-card"><h2>Level 1 only</h2><div class="response-meta" id="level1Meta"></div><div class="text generated" id="level1Response"></div></article><article class="response-card"><h2>Levels 1 + 2</h2><div class="response-meta" id="level12Meta"></div><div class="text generated" id="level12Response"></div></article><article class="response-card"><h2>Levels 1 + 2 + 3</h2><div class="response-meta" id="fullMeta"></div><div class="text" id="response"></div></article></div><div class="cards" id="cards"></div></section> | |
| <section class="panel image-panel"><h1>Generated image rendering</h1><p>Complete P6 RGB byte sequences found in each prompt-plus-rollout stream are validated and embedded here as PNG.</p><div class="image-galleries"><div class="image-gallery"><h2>Level 1 only</h2><div id="level1Images"></div></div><div class="image-gallery"><h2>Levels 1 + 2</h2><div id="level12Images"></div></div><div class="image-gallery"><h2>Levels 1 + 2 + 3</h2><div id="fullImages"></div></div></div></section> | |
| <script> | |
| const R={payload},prompt=document.getElementById("prompt"),response=document.getElementById("response"),toggle=document.getElementById("influence"); | |
| prompt.textContent=R.prompt;document.getElementById("subtitle").textContent=`${{R.stats.checkpoint}} • step ${{Number(R.stats.checkpoint_step).toLocaleString()}}`; | |
| const byMode=Object.fromEntries(R.comparisons.map(x=>[x.mode,x]));for(const [mode,id,color] of [["level1","level1Response","#51d6ca"],["level12","level12Response","#ffbe55"]]){{const item=byMode[mode]||{{text:"",generated_bytes:0,seconds:0}};document.getElementById(id).textContent=item.text;document.getElementById(id).dataset.color=color;document.getElementById(mode+"Meta").textContent=`${{Number(item.generated_bytes).toLocaleString()}} bytes • ${{Number(item.seconds).toFixed(2)}} s`;}}document.getElementById("fullMeta").textContent=`${{Number(R.stats.generated_bytes).toLocaleString()}} bytes • ${{Number(R.stats.generation_seconds_including_prefill).toFixed(2)}} s`; | |
| const magnitudes=R.segments.flatMap(x=>[Math.abs(x.level2_delta_logp),Math.abs(x.level3_delta_logp)]).filter(Number.isFinite).sort((a,b)=>a-b),scale=magnitudes[Math.floor(magnitudes.length*.9)]||.01; | |
| function classification(x){{const candidates=[];if(x.level2_active)candidates.push([Math.abs(x.level2_delta_logp),x.level2_delta_logp,"#ffbe55","L2"]);if(x.level3_active)candidates.push([Math.abs(x.level3_delta_logp),x.level3_delta_logp,"#a78bfa","L3"]);if(!candidates.length)return ["#51d6ca",.72,"fine only"];candidates.sort((a,b)=>b[0]-a[0]);const best=candidates[0],color=best[1]<0?"#fb7185":best[2],opacity=.48+.52*Math.min(1,best[0]/scale);return [color,opacity,best[1]<0?best[3]+" negative":best[3]+" dominant"]}} | |
| R.segments.forEach(x=>{{const span=document.createElement("span"),style=classification(x);span.className="generated";span.textContent=x.text;span.dataset.color=style[0];span.dataset.opacity=style[1];span.title=`${{style[2]}} • bytes ${{x.bytes.join(",")}} • L2 Δlogp ${{x.level2_delta_logp.toFixed(5)}} • L3 Δlogp ${{x.level3_delta_logp.toFixed(5)}} • combined Δlogp ${{x.hierarchy_delta_logp.toFixed(5)}}`;response.appendChild(span)}}); | |
| function recolor(){{response.querySelectorAll(".generated").forEach(span=>{{span.style.color=toggle.checked?span.dataset.color:"#63a4ff";span.style.opacity=toggle.checked?span.dataset.opacity:"1"}});for(const id of ["level1Response","level12Response"]){{const node=document.getElementById(id);node.style.color=toggle.checked?node.dataset.color:"#63a4ff"}}}}toggle.addEventListener("change",recolor);recolor(); | |
| const attrs=R.segments,mean=key=>attrs.length?attrs.reduce((s,x)=>s+Number(x[key]||0),0)/attrs.length:0,cards=[["Prompt bytes",R.stats.prompt_bytes],["Generated bytes",R.stats.generated_bytes],["Generation tok/s",Number(R.stats.generated_bytes_per_second_including_prefill).toFixed(1)],["Mean L2 Δlogp",mean("level2_delta_logp").toFixed(5)],["Mean L3 Δlogp",mean("level3_delta_logp").toFixed(5)],["Mean combined Δlogp",mean("hierarchy_delta_logp").toFixed(5)]]; | |
| document.getElementById("cards").innerHTML=cards.map(x=>`<div class="card"><div class="name">${{x[0]}}</div><div class="value">${{x[1]}}</div></div>`).join(""); | |
| function renderImages(target,report){{const root=document.getElementById(target),images=report?.images||[],warnings=report?.warnings||[];if(!images.length&&!warnings.length){{root.innerHTML='<div class="image-status">No complete P6 image detected.</div>';return}}images.forEach((item,index)=>{{const figure=document.createElement("figure");figure.className="image-item";const image=document.createElement("img");image.src=item.data_uri;image.alt=`Generated image ${{index+1}}, ${{item.width}} by ${{item.height}} pixels`;const caption=document.createElement("figcaption");caption.textContent=`Image ${{index+1}} • ${{item.width}}×${{item.height}} • byte range ${{item.start.toLocaleString()}}–${{(item.end-1).toLocaleString()}}`;figure.append(image,caption);root.appendChild(figure)}});if(warnings.length){{const status=document.createElement("div");status.className="image-status";status.textContent=warnings.join("\\n");root.appendChild(status)}}}} | |
| renderImages("fullImages",R.image_report);renderImages("level1Images",byMode.level1?.image_report);renderImages("level12Images",byMode.level12?.image_report); | |
| </script></main></body></html>""" | |
| def main() -> None: | |
| args = parse_args() | |
| if args.max_new_bytes < 0 or args.temperature <= 0 or not 0 < args.top_p <= 1: | |
| raise ValueError("max-new-bytes must be non-negative, temperature positive, and top-p in (0, 1]") | |
| if args.prompt_file: | |
| prompt = Path(args.prompt_file).expanduser().read_bytes() | |
| else: | |
| prompt = args.prompt.encode("utf-8") | |
| load_started = time.perf_counter() | |
| loaded = load_nested_checkpoint( | |
| args.checkpoint, | |
| precision=args.precision, | |
| device=args.device, | |
| fine_device=args.fine_device, | |
| nested_devices=args.nested_devices, | |
| tertiary_device=args.tertiary_device, | |
| use_saved_placement=args.use_saved_placement, | |
| ) | |
| torch.cuda.synchronize() | |
| load_seconds = time.perf_counter() - load_started | |
| generation_started = time.perf_counter() | |
| generated, stream = generate_bytes( | |
| loaded, | |
| prompt, | |
| max_new_bytes=args.max_new_bytes, | |
| temperature=args.temperature, | |
| top_p=args.top_p, | |
| top_k=args.top_k, | |
| repeat_penalty=args.repeat_penalty, | |
| repeat_window=args.repeat_window, | |
| greedy=args.greedy, | |
| seed=args.seed, | |
| collect_hierarchy_attribution=bool(args.html_output), | |
| ) | |
| torch.cuda.synchronize() | |
| generation_seconds = time.perf_counter() - generation_started | |
| combined = prompt + generated | |
| full_images, full_image_warnings = extract_ppm_images(combined) | |
| full_image_report = image_report_payload(full_images, full_image_warnings) | |
| print(combined.decode("utf-8", "replace")) | |
| attributions = list(stream.get("generated_attribution", [])) | |
| stats = { | |
| "checkpoint": str(loaded.checkpoint_path), | |
| "checkpoint_step": loaded.checkpoint_step, | |
| "trained_tokens": loaded.trained_tokens, | |
| "precision": loaded.precision, | |
| "model_parallel": loaded.model_parallel, | |
| "prompt_bytes": len(prompt), | |
| "generated_bytes": len(generated), | |
| "load_seconds": load_seconds, | |
| "generation_seconds_including_prefill": generation_seconds, | |
| "generated_bytes_per_second_including_prefill": ( | |
| len(generated) / generation_seconds if generation_seconds else 0.0 | |
| ), | |
| "fine_patches": int(stream["completed_patches"]), | |
| "level2_pools": int(stream["completed_nested_patches"]), | |
| "level3_pools": int(stream.get("completed_tertiary_patches", 0)), | |
| "hierarchy_attribution": bool(args.html_output), | |
| "rendered_images": len(full_images), | |
| "image_render_warnings": full_image_warnings, | |
| } | |
| if attributions: | |
| stats.update( | |
| { | |
| "attributed_bytes": len(attributions), | |
| "level2_active_generated_bytes": sum( | |
| bool(item.get("level2_active")) for item in attributions | |
| ), | |
| "level3_active_generated_bytes": sum( | |
| bool(item.get("level3_active")) for item in attributions | |
| ), | |
| "mean_level2_delta_logp": sum( | |
| float(item.get("level2_delta_logp", 0.0)) | |
| for item in attributions | |
| ) | |
| / len(attributions), | |
| "mean_level3_delta_logp": sum( | |
| float(item.get("level3_delta_logp", 0.0)) | |
| for item in attributions | |
| ) | |
| / len(attributions), | |
| "mean_hierarchy_delta_logp": sum( | |
| float(item.get("hierarchy_delta_logp", 0.0)) | |
| for item in attributions | |
| ) | |
| / len(attributions), | |
| } | |
| ) | |
| comparisons: List[Dict[str, object]] = [] | |
| del stream | |
| gc.collect() | |
| if args.html_output: | |
| for mode, label in (("level1", "Level 1 only"), ("level12", "Levels 1 + 2")): | |
| comparison_started = time.perf_counter() | |
| comparison_bytes, comparison_stream = generate_bytes( | |
| loaded, | |
| prompt, | |
| max_new_bytes=args.max_new_bytes, | |
| temperature=args.temperature, | |
| top_p=args.top_p, | |
| top_k=args.top_k, | |
| repeat_penalty=args.repeat_penalty, | |
| repeat_window=args.repeat_window, | |
| greedy=args.greedy, | |
| seed=args.seed, | |
| hierarchy_mode=mode, | |
| ) | |
| torch.cuda.synchronize() | |
| comparison_seconds = time.perf_counter() - comparison_started | |
| comparisons.append( | |
| { | |
| "mode": mode, | |
| "label": label, | |
| "text": comparison_bytes.decode("utf-8", "replace"), | |
| "generated_bytes": len(comparison_bytes), | |
| "seconds": comparison_seconds, | |
| "fine_patches": int(comparison_stream["completed_patches"]), | |
| "level2_pools": int( | |
| comparison_stream["completed_nested_patches"] | |
| ), | |
| "level3_pools": int( | |
| comparison_stream.get("completed_tertiary_patches", 0) | |
| ), | |
| "image_report": image_report_payload( | |
| *extract_ppm_images(prompt + comparison_bytes) | |
| ), | |
| } | |
| ) | |
| del comparison_stream | |
| gc.collect() | |
| # Keep base64 PNG payloads in the HTML only; terminal/stats JSON should | |
| # stay compact even when a rollout contains a large rendered image. | |
| stats["comparison_rollouts"] = [ | |
| {key: value for key, value in item.items() if key != "image_report"} | |
| for item in comparisons | |
| ] | |
| if args.output: | |
| Path(args.output).expanduser().write_bytes(combined) | |
| if args.image_output_dir: | |
| image_paths = write_extracted_images( | |
| Path(args.image_output_dir).expanduser(), full_images, prefix="full" | |
| ) | |
| stats["image_output_files"] = image_paths | |
| for item in comparisons: | |
| report = item.get("image_report") or {} | |
| comparison_images = [] | |
| for encoded in report.get("images", []): | |
| raw = base64.b64decode(str(encoded["data_uri"]).split(",", 1)[1]) | |
| comparison_images.append({**encoded, "png": raw}) | |
| stats.setdefault("comparison_image_output_files", {})[item["mode"]] = ( | |
| write_extracted_images( | |
| Path(args.image_output_dir).expanduser(), | |
| comparison_images, | |
| prefix=str(item["mode"]), | |
| ) | |
| ) | |
| print(json.dumps(stats, indent=2)) | |
| if args.stats_json: | |
| Path(args.stats_json).expanduser().write_text( | |
| json.dumps(stats, indent=2), encoding="utf-8" | |
| ) | |
| if args.html_output: | |
| html_path = Path(args.html_output).expanduser() | |
| html_path.parent.mkdir(parents=True, exist_ok=True) | |
| html_path.write_text( | |
| make_generation_html( | |
| prompt, | |
| generated, | |
| attributions, | |
| stats, | |
| comparisons=comparisons, | |
| image_report=full_image_report, | |
| ), | |
| encoding="utf-8", | |
| ) | |
| print(f"wrote hierarchy influence report: {html_path}") | |
| if __name__ == "__main__": | |
| main() | |