mirror of
https://github.com/rohitg00/ai-engineering-from-scratch.git
synced 2026-10-02 01:54:39 +08:00
* feat(projects): workflow-hooks stage 1 * feat(projects): workflow-hooks stage 2 * feat(projects): workflow-hooks stage 3 * feat(projects): workflow-hooks stage 4 * fix(projects): preserve sponsor navigation in project footer * feat(projects): agent-budget-planner stage 1 * feat(projects): agent-budget-planner stage 2 * feat(projects): agent-budget-planner stage 3 * feat(projects): agent-budget-planner stage 4 * feat(projects): agent-trace-debugger stage 1 * feat(projects): agent-trace-debugger stage 2 * feat(projects): agent-trace-debugger stage 3 * feat(projects): agent-trace-debugger stage 4 * feat(projects): browser-agent stage 1 * feat(projects): browser-agent stage 2 * feat(projects): browser-agent stage 3 * feat(projects): browser-agent stage 4 * feat(projects): calendar-focus-planner stage 1 * feat(projects): calendar-focus-planner stage 2 * feat(projects): calendar-focus-planner stage 3 * feat(projects): calendar-focus-planner stage 4 * feat(projects): changelog-writer-from-git stage 1 * feat(projects): changelog-writer-from-git stage 2 * feat(projects): changelog-writer-from-git stage 3 * feat(projects): changelog-writer-from-git stage 4 * feat(projects): cloud-agent-with-aws-strands stage 1 * feat(projects): cloud-agent-with-aws-strands stage 2 * feat(projects): cloud-agent-with-aws-strands stage 3 * feat(projects): cloud-agent-with-aws-strands stage 4 * feat(projects): csv-sql-question-workbench stage 1 * feat(projects): csv-sql-question-workbench stage 2 * feat(projects): csv-sql-question-workbench stage 3 * feat(projects): csv-sql-question-workbench stage 4 * feat(projects): dataset-split-auditor stage 1 * feat(projects): dataset-split-auditor stage 2 * feat(projects): dataset-split-auditor stage 3 * feat(projects): dataset-split-auditor stage 4 * feat(projects): desktop-control stage 1 * feat(projects): desktop-control stage 2 * feat(projects): desktop-control stage 3 * feat(projects): desktop-control stage 4 * feat(projects): distributed-eval-farm stage 1 * feat(projects): distributed-eval-farm stage 2 * feat(projects): distributed-eval-farm stage 3 * feat(projects): distributed-eval-farm stage 4 * feat(projects): doc-qa-with-citations stage 1 * feat(projects): doc-qa-with-citations stage 2 * feat(projects): doc-qa-with-citations stage 3 * feat(projects): doc-qa-with-citations stage 4 * feat(projects): document-extraction-desk stage 1 * feat(projects): document-extraction-desk stage 2 * feat(projects): document-extraction-desk stage 3 * feat(projects): document-extraction-desk stage 4 * feat(projects): durable-agent-jobs stage 1 * feat(projects): durable-agent-jobs stage 2 * feat(projects): durable-agent-jobs stage 3 * feat(projects): durable-agent-jobs stage 4 * feat(projects): feedback-theme-board stage 1 * feat(projects): feedback-theme-board stage 2 * feat(projects): feedback-theme-board stage 3 * feat(projects): feedback-theme-board stage 4 * feat(projects): harness-bench stage 1 * feat(projects): harness-bench stage 2 * feat(projects): harness-bench stage 3 * feat(projects): harness-bench stage 4 * feat(projects): inbox-triage-desk stage 1 * feat(projects): inbox-triage-desk stage 2 * feat(projects): inbox-triage-desk stage 3 * feat(projects): inbox-triage-desk stage 4 * feat(projects): json-schema-output-guard stage 1 * feat(projects): json-schema-output-guard stage 2 * feat(projects): json-schema-output-guard stage 3 * feat(projects): json-schema-output-guard stage 4 * feat(projects): llm-gateway-with-fallbacks stage 1 * feat(projects): llm-gateway-with-fallbacks stage 2 * feat(projects): llm-gateway-with-fallbacks stage 3 * feat(projects): llm-gateway-with-fallbacks stage 4 * feat(projects): local-model-eval-harness stage 1 * feat(projects): local-model-eval-harness stage 2 * feat(projects): local-model-eval-harness stage 3 * feat(projects): local-model-eval-harness stage 4 * feat(projects): mcp-at-scale stage 1 * feat(projects): mcp-at-scale stage 2 * feat(projects): mcp-at-scale stage 3 * feat(projects): mcp-at-scale stage 4 * feat(projects): mcp-at-scale stage 5 * feat(projects): meeting-notes-to-actions stage 1 * feat(projects): meeting-notes-to-actions stage 2 * feat(projects): meeting-notes-to-actions stage 3 * feat(projects): meeting-notes-to-actions stage 4 * feat(projects): memory-server stage 1 * feat(projects): memory-server stage 2 * feat(projects): memory-server stage 3 * feat(projects): memory-server stage 4 * feat(projects): multi-agent-code-review-panel stage 1 * feat(projects): multi-agent-code-review-panel stage 2 * feat(projects): multi-agent-code-review-panel stage 3 * feat(projects): multi-agent-code-review-panel stage 4 * feat(projects): postmortem-writer stage 1 * feat(projects): postmortem-writer stage 2 * feat(projects): postmortem-writer stage 3 * feat(projects): postmortem-writer stage 4 * feat(projects): pr-review-reporter stage 1 * feat(projects): pr-review-reporter stage 2 * feat(projects): pr-review-reporter stage 3 * feat(projects): pr-review-reporter stage 4 * feat(projects): prompt-regression-tester stage 1 * feat(projects): prompt-regression-tester stage 2 * feat(projects): prompt-regression-tester stage 3 * feat(projects): prompt-regression-tester stage 4 * feat(projects): rag-freshness-pipeline stage 1 * feat(projects): rag-freshness-pipeline stage 2 * feat(projects): rag-freshness-pipeline stage 3 * feat(projects): rag-freshness-pipeline stage 4 * feat(projects): report-judge stage 1 * feat(projects): report-judge stage 2 * feat(projects): report-judge stage 3 * feat(projects): report-judge stage 4 * feat(projects): research-report-agent stage 1 * feat(projects): research-report-agent stage 2 * feat(projects): research-report-agent stage 3 * feat(projects): research-report-agent stage 4 * feat(projects): research-report-agent stage 5 * feat(projects): research-report-agent stage 6 * feat(projects): research-report-agent stage 7 * feat(projects): retrieval-evaluation-lab stage 1 * feat(projects): retrieval-evaluation-lab stage 2 * feat(projects): retrieval-evaluation-lab stage 3 * feat(projects): retrieval-evaluation-lab stage 4 * feat(projects): rust-agent-shell stage 1 * feat(projects): rust-agent-shell stage 2 * feat(projects): rust-agent-shell stage 3 * feat(projects): rust-agent-shell stage 4 * feat(projects): sandbox-ladder stage 1 * feat(projects): sandbox-ladder stage 2 * feat(projects): sandbox-ladder stage 3 * feat(projects): sandbox-ladder stage 4 * feat(projects): self-improving-skill-loop stage 1 * feat(projects): self-improving-skill-loop stage 2 * feat(projects): self-improving-skill-loop stage 3 * feat(projects): self-improving-skill-loop stage 4 * feat(projects): semantic-notes-search stage 1 * feat(projects): semantic-notes-search stage 2 * feat(projects): semantic-notes-search stage 3 * feat(projects): semantic-notes-search stage 4 * feat(projects): skill-installer stage 1 * feat(projects): skill-installer stage 2 * feat(projects): skill-installer stage 3 * feat(projects): skill-installer stage 4 * feat(projects): skill-router stage 1 * feat(projects): skill-router stage 2 * feat(projects): skill-router stage 3 * feat(projects): skill-router stage 4 * feat(projects): skill-scanner stage 1 * feat(projects): skill-scanner stage 2 * feat(projects): skill-scanner stage 3 * feat(projects): skill-scanner stage 4 * feat(projects): skill-validator stage 1 * feat(projects): skill-validator stage 2 * feat(projects): skill-validator stage 3 * feat(projects): skill-validator stage 4 * feat(projects): source-grounded-study-coach stage 1 * feat(projects): source-grounded-study-coach stage 2 * feat(projects): source-grounded-study-coach stage 3 * feat(projects): source-grounded-study-coach stage 4 * feat(projects): support-agent-with-google-adk stage 1 * feat(projects): support-agent-with-google-adk stage 2 * feat(projects): support-agent-with-google-adk stage 3 * feat(projects): support-agent-with-google-adk stage 4 * feat(projects): tiny-coding-agent stage 1 * feat(projects): tiny-coding-agent stage 2 * feat(projects): tiny-coding-agent stage 3 * feat(projects): tiny-coding-agent stage 4 * feat(projects): token-counter-and-cost-meter stage 1 * feat(projects): token-counter-and-cost-meter stage 2 * feat(projects): token-counter-and-cost-meter stage 3 * feat(projects): token-counter-and-cost-meter stage 4 * feat(projects): tool-call-firewall stage 1 * feat(projects): tool-call-firewall stage 2 * feat(projects): tool-call-firewall stage 3 * feat(projects): tool-call-firewall stage 4 * feat(projects): typed-workflow-agent-with-mastra stage 1 * feat(projects): typed-workflow-agent-with-mastra stage 2 * feat(projects): typed-workflow-agent-with-mastra stage 3 * feat(projects): typed-workflow-agent-with-mastra stage 4 * feat(projects): visual-evidence-library stage 1 * feat(projects): visual-evidence-library stage 2 * feat(projects): visual-evidence-library stage 3 * feat(projects): visual-evidence-library stage 4 * feat(projects): voice-note-transcriber-pipeline stage 1 * feat(projects): voice-note-transcriber-pipeline stage 2 * feat(projects): voice-note-transcriber-pipeline stage 3 * feat(projects): voice-note-transcriber-pipeline stage 4 * feat(projects): web-change-brief stage 1 * feat(projects): web-change-brief stage 2 * feat(projects): web-change-brief stage 3 * feat(projects): web-change-brief stage 4 * feat(projects): workflow-hooks stage 1 * feat(projects): workflow-hooks stage 2 * feat(projects): workflow-hooks stage 3 * feat(projects): workflow-hooks stage 4 * feat(projects): publish the practical application catalog * feat(projects): add 52 planned builds to the roadmap * fix(projects): make lesson animations explain computed changes Stage mechanisms should expose the decisions learners are implementing. Preserve record identity as state changes and keep project context below the selected lesson. * fix(projects): agent-budget-planner stage 01 contracts Keep the stage contract, learner scaffold and verification evidence consistent at the reviewed failure boundary. * fix(projects): agent-budget-planner stage 02 contracts Keep the stage contract, learner scaffold and verification evidence consistent at the reviewed failure boundary. * fix(projects): agent-budget-planner stage 03 contracts Keep the stage contract, learner scaffold and verification evidence consistent at the reviewed failure boundary. * fix(projects): agent-budget-planner stage 04 contracts Keep the stage contract, learner scaffold and verification evidence consistent at the reviewed failure boundary. * fix(projects): agent-trace-debugger stage 03 contracts Keep the stage contract, learner scaffold and verification evidence consistent at the reviewed failure boundary. * fix(projects): browser-agent stage 02 contracts Keep the stage contract, learner scaffold and verification evidence consistent at the reviewed failure boundary. * fix(projects): browser-agent stage 03 contracts Keep the stage contract, learner scaffold and verification evidence consistent at the reviewed failure boundary. * fix(projects): browser-agent stage 04 contracts Keep the stage contract, learner scaffold and verification evidence consistent at the reviewed failure boundary. * fix(projects): calendar-focus-planner stage 01 contracts Keep the stage contract, learner scaffold and verification evidence consistent at the reviewed failure boundary. * fix(projects): cloud-agent-with-aws-strands stage 01 contracts Keep the stage contract, learner scaffold and verification evidence consistent at the reviewed failure boundary. * fix(projects): cloud-agent-with-aws-strands stage 02 contracts Keep the stage contract, learner scaffold and verification evidence consistent at the reviewed failure boundary. * fix(projects): cloud-agent-with-aws-strands stage 04 contracts Keep the stage contract, learner scaffold and verification evidence consistent at the reviewed failure boundary. * fix(projects): desktop-control stage 04 contracts Keep the stage contract, learner scaffold and verification evidence consistent at the reviewed failure boundary. * fix(projects): distributed-eval-farm stage 01 contracts Keep the stage contract, learner scaffold and verification evidence consistent at the reviewed failure boundary. * fix(projects): distributed-eval-farm stage 02 contracts Keep the stage contract, learner scaffold and verification evidence consistent at the reviewed failure boundary. * fix(projects): distributed-eval-farm stage 04 contracts Keep the stage contract, learner scaffold and verification evidence consistent at the reviewed failure boundary. * fix(projects): document-extraction-desk stage 03 contracts Keep the stage contract, learner scaffold and verification evidence consistent at the reviewed failure boundary. * fix(projects): durable-agent-jobs stage 01 contracts Keep the stage contract, learner scaffold and verification evidence consistent at the reviewed failure boundary. * fix(projects): durable-agent-jobs stage 02 contracts Keep the stage contract, learner scaffold and verification evidence consistent at the reviewed failure boundary. * fix(projects): durable-agent-jobs stage 03 contracts Keep the stage contract, learner scaffold and verification evidence consistent at the reviewed failure boundary. * fix(projects): durable-agent-jobs stage 04 contracts Keep the stage contract, learner scaffold and verification evidence consistent at the reviewed failure boundary. * fix(projects): feedback-theme-board stage 01 contracts Keep the stage contract, learner scaffold and verification evidence consistent at the reviewed failure boundary. * fix(projects): inbox-triage-desk stage 03 contracts Keep the stage contract, learner scaffold and verification evidence consistent at the reviewed failure boundary. * fix(projects): llm-gateway-with-fallbacks stage 01 contracts Keep the stage contract, learner scaffold and verification evidence consistent at the reviewed failure boundary. * fix(projects): llm-gateway-with-fallbacks stage 04 contracts Keep the stage contract, learner scaffold and verification evidence consistent at the reviewed failure boundary. * fix(projects): mcp-at-scale review corrections Keep the stage contract, learner scaffold and verification evidence consistent at the reviewed failure boundary. * fix(projects): meeting-notes-to-actions stage 01 contracts Keep the stage contract, learner scaffold and verification evidence consistent at the reviewed failure boundary. * fix(projects): meeting-notes-to-actions stage 04 contracts Keep the stage contract, learner scaffold and verification evidence consistent at the reviewed failure boundary. * fix(projects): memory-server stage 04 contracts Keep the stage contract, learner scaffold and verification evidence consistent at the reviewed failure boundary. * fix(projects): pr-review-reporter stage 01 contracts Keep the stage contract, learner scaffold and verification evidence consistent at the reviewed failure boundary. * fix(projects): pr-review-reporter stage 03 contracts Keep the stage contract, learner scaffold and verification evidence consistent at the reviewed failure boundary. * fix(projects): pr-review-reporter stage 04 contracts Keep the stage contract, learner scaffold and verification evidence consistent at the reviewed failure boundary. * fix(projects): research-report-agent stage 01 contracts Keep the stage contract, learner scaffold and verification evidence consistent at the reviewed failure boundary. * fix(projects): retrieval-evaluation-lab review corrections Keep the stage contract, learner scaffold and verification evidence consistent at the reviewed failure boundary. * fix(projects): rust-agent-shell stage 01 contracts Keep the stage contract, learner scaffold and verification evidence consistent at the reviewed failure boundary. * fix(projects): rust-agent-shell stage 04 contracts Keep the stage contract, learner scaffold and verification evidence consistent at the reviewed failure boundary. * fix(projects): semantic-notes-search stage 03 contracts Keep the stage contract, learner scaffold and verification evidence consistent at the reviewed failure boundary. * fix(projects): skill-installer review corrections Keep the stage contract, learner scaffold and verification evidence consistent at the reviewed failure boundary. * fix(projects): skill-router stage 02 contracts Keep the stage contract, learner scaffold and verification evidence consistent at the reviewed failure boundary. * fix(projects): skill-router stage 04 contracts Keep the stage contract, learner scaffold and verification evidence consistent at the reviewed failure boundary. * fix(projects): tiny-coding-agent stage 02 contracts Keep the stage contract, learner scaffold and verification evidence consistent at the reviewed failure boundary. * fix(projects): voice-note-transcriber-pipeline stage 03 contracts Keep the stage contract, learner scaffold and verification evidence consistent at the reviewed failure boundary. * fix(projects): web-change-brief stage 03 contracts Keep the stage contract, learner scaffold and verification evidence consistent at the reviewed failure boundary. * fix(projects): workflow-hooks stage 01 contracts Keep the stage contract, learner scaffold and verification evidence consistent at the reviewed failure boundary. * fix(projects): site review corrections Keep the stage contract, learner scaffold and verification evidence consistent at the reviewed failure boundary. * docs(projects): record verified review corrections
192 lines
6.0 KiB
Python
192 lines
6.0 KiB
Python
"""Render actual command transcripts or browser captures into GIF recordings.
|
|
|
|
Authoring dependency: Pillow and ffmpeg. Neither is needed by learners.
|
|
"""
|
|
|
|
import argparse
|
|
import html
|
|
import json
|
|
import os
|
|
from pathlib import Path
|
|
import shutil
|
|
import subprocess
|
|
import tempfile
|
|
import textwrap
|
|
import time
|
|
|
|
|
|
def execute(command, cwd, expected=0):
|
|
start = time.monotonic()
|
|
result = subprocess.run(
|
|
command, cwd=cwd, capture_output=True, text=True, timeout=180
|
|
)
|
|
output = result.stdout + result.stderr
|
|
if result.returncode != expected:
|
|
raise RuntimeError(
|
|
f"Expected exit {expected}, got {result.returncode}: {command}\n{output}"
|
|
)
|
|
return {
|
|
"command": command,
|
|
"cwd": str(cwd),
|
|
"exitCode": result.returncode,
|
|
"output": output,
|
|
"durationMs": round((time.monotonic() - start) * 1000),
|
|
}
|
|
|
|
|
|
def frame(lines, title):
|
|
from PIL import Image, ImageDraw, ImageFont
|
|
|
|
image = Image.new("RGB", (1120, 630), "#111a29")
|
|
draw = ImageDraw.Draw(image)
|
|
candidates = [
|
|
"/System/Library/Fonts/Menlo.ttc",
|
|
"/usr/share/fonts/truetype/dejavu/DejaVuSansMono.ttf",
|
|
]
|
|
font = next(
|
|
(ImageFont.truetype(p, 18) for p in candidates if Path(p).exists()),
|
|
ImageFont.load_default(),
|
|
)
|
|
draw.text((28, 20), title[:85], font=font, fill="#9fb2d7")
|
|
draw.line((28, 55, 1092, 55), fill="#31415c")
|
|
for n, line in enumerate(lines[-22:]):
|
|
draw.text(
|
|
(28, 78 + n * 23),
|
|
line,
|
|
font=font,
|
|
fill="#89d9af" if line.startswith("$ ") else "#e4eaf5",
|
|
)
|
|
return image
|
|
|
|
|
|
def encode(frames, output, duration=450):
|
|
output = Path(output)
|
|
output.parent.mkdir(parents=True, exist_ok=True)
|
|
frames[-1].save(output.with_suffix(".png"))
|
|
if shutil.which("ffmpeg"):
|
|
with tempfile.TemporaryDirectory(prefix="aifs-gif-") as folder:
|
|
for i, image in enumerate(frames):
|
|
image.save(Path(folder) / f"{i:04d}.png")
|
|
subprocess.run(
|
|
[
|
|
"ffmpeg",
|
|
"-hide_banner",
|
|
"-loglevel",
|
|
"error",
|
|
"-y",
|
|
"-framerate",
|
|
str(1000 / duration),
|
|
"-i",
|
|
str(Path(folder) / "%04d.png"),
|
|
"-filter_complex",
|
|
"[0:v]split[a][b];[a]palettegen[p];[b][p]paletteuse",
|
|
"-loop",
|
|
"0",
|
|
str(output),
|
|
],
|
|
check=True,
|
|
)
|
|
else:
|
|
frames[0].save(
|
|
output, save_all=True, append_images=frames[1:], duration=duration, loop=0
|
|
)
|
|
|
|
|
|
def terminal(records, output, title):
|
|
frames, lines = [], []
|
|
for record in records:
|
|
lines.extend(
|
|
textwrap.wrap("$ " + " ".join(record["command"]), width=96) or [""]
|
|
)
|
|
frames.append(frame(lines, title))
|
|
chunks = []
|
|
for line in record["output"].splitlines():
|
|
chunks.extend(
|
|
textwrap.wrap(line.expandtabs(2), width=96, replace_whitespace=False)
|
|
or [""]
|
|
)
|
|
for offset in range(0, len(chunks), 4):
|
|
lines.extend(chunks[offset : offset + 4])
|
|
frames.append(frame(lines, title))
|
|
lines.append(f"exit {record['exitCode']}")
|
|
frames.extend([frame(lines, title)] * 3)
|
|
if not frames:
|
|
raise ValueError("Recording needs at least one command")
|
|
encode(frames, output)
|
|
Path(output).with_suffix(".json").write_text(
|
|
json.dumps(
|
|
{
|
|
"title": title,
|
|
"recordedAt": time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()),
|
|
"records": records,
|
|
},
|
|
indent=2,
|
|
)
|
|
+ "\n"
|
|
)
|
|
|
|
|
|
def browser(url, output, title, browse, actions):
|
|
from PIL import Image
|
|
|
|
def call(*args):
|
|
return subprocess.run(
|
|
[browse, *args], check=True, capture_output=True, text=True, timeout=45
|
|
)
|
|
|
|
call("viewport", "1120x630")
|
|
call("goto", url)
|
|
frames = []
|
|
with tempfile.TemporaryDirectory(prefix="aifs-browser-gif-", dir="/tmp") as folder:
|
|
for i, action in enumerate([[]] + actions):
|
|
if action:
|
|
call(*action)
|
|
path = str(Path(folder) / f"{i}.png")
|
|
call("screenshot", "--viewport", path)
|
|
frames.extend([Image.open(path).convert("RGB").copy()] * 3)
|
|
encode(frames, output, 650)
|
|
Path(output).with_suffix(".json").write_text(
|
|
json.dumps(
|
|
{"title": title, "url": url, "actions": actions, "kind": "browser capture"},
|
|
indent=2,
|
|
)
|
|
+ "\n"
|
|
)
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(description=__doc__)
|
|
parser.add_argument(
|
|
"--recipe", type=Path, help="JSON {cwd,title,commands:[{argv,expectExit?}]}"
|
|
)
|
|
parser.add_argument("--browser", help="URL to capture")
|
|
parser.add_argument(
|
|
"--actions", type=Path, help="JSON array of browse command arrays"
|
|
)
|
|
parser.add_argument("--browse", default=os.environ.get("BROWSE_BIN", "browse"))
|
|
parser.add_argument("--output", required=True, type=Path)
|
|
parser.add_argument("--title", default="Recorded project run")
|
|
args = parser.parse_args()
|
|
if args.browser:
|
|
browser(
|
|
args.browser,
|
|
args.output,
|
|
args.title,
|
|
args.browse,
|
|
json.loads(args.actions.read_text()) if args.actions else [],
|
|
)
|
|
elif args.recipe:
|
|
recipe = json.loads(args.recipe.read_text())
|
|
cwd = Path(recipe.get("cwd", ".")).resolve()
|
|
records = [
|
|
execute(command["argv"], cwd, command.get("expectExit", 0))
|
|
for command in recipe["commands"]
|
|
]
|
|
terminal(records, args.output, recipe.get("title", args.title))
|
|
else:
|
|
parser.error("choose --recipe or --browser")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|