app
AI Flow
Public Made by Adomby adom
Adom's AI Flow: a tool to help the AI follow all of the steps it takes to build a board.
main
John Lauer
source at 0.1.25: the whole crate workspace, the flow, the skills and the docs
f31cc25
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394
"""The run manifest: one JSON file per run that carries the clock, the plan, the decisions,
the stage outcomes, the capture markers and the final summary. Every command reads and
writes it, so the comparison between engines is what the file says, not what anyone remembers."""
import json
import os
import time
from pathlib import Path
STAGES = ["place", "route", "pour", "gate", "land", "measure", "analyze-current", "analyze-thermal", "finish"]
def now():
return time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime())
def parse(ts):
return time.mktime(time.strptime(ts, "%Y-%m-%dT%H:%M:%SZ")) - time.timezone
class Run:
def __init__(self, path):
self.dir = Path(path)
self.file = self.dir / "run.json"
self.data = json.load(open(self.file, encoding="utf-8")) if self.file.is_file() else None
def exists(self):
return self.data is not None
def save(self):
self.dir.mkdir(parents=True, exist_ok=True)
tmp = self.file.with_suffix(".tmp")
json.dump(self.data, open(tmp, "w", encoding="utf-8"), indent=1)
os.replace(tmp, self.file)
@staticmethod
def create(path, board, spec, engine, prompt_time, ai_thread, target=None, remote_board=None):
r = Run(path)
r.data = {
"version": 1, "engine": engine, "aiThread": ai_thread, "board": str(board), "spec": spec,
"target": target, "remoteBoard": remote_board,
"clock": {"promptTime": prompt_time, "startTime": now(), "finishTime": None},
"plan": None, "decisions": {}, "stages": {}, "captures": [], "markers": [], "boards": [str(board)],
"outcomes": {}, "summary": None,
}
r.save()
return r
# -- stages ---------------------------------------------------------------------
def stage_start(self, name, engine=None, note=None):
st = self.data["stages"].setdefault(name, {"runs": []})
st["runs"].append({"start": now(), "end": None, "engine": engine or self.engine_for(name), "note": note, "result": None})
self.mark("stage-start:" + name)
self.save()
def stage_end(self, name, result=None, note=None):
st = self.data["stages"].setdefault(name, {"runs": []})
if not st["runs"] or st["runs"][-1]["end"] is not None:
st["runs"].append({"start": now(), "end": None, "engine": self.engine_for(name), "note": None, "result": None})
st["runs"][-1]["end"] = now()
if result is not None:
st["runs"][-1]["result"] = result
if note:
st["runs"][-1]["note"] = note
self.mark("stage-end:" + name)
self.save()
def engine_for(self, name):
return self.data["decisions"].get(name) or ((self.data.get("plan") or {}).get("stages", {}).get(name, {}) or {}).get("default", "binary")
def mark(self, label):
self.data["markers"].append({"t": now(), "label": label})
def current_board(self):
return self.data["boards"][-1]
def add_board(self, path):
self.data["boards"].append(str(path))
self.save()
# -- time -----------------------------------------------------------------------
def elapsed_minutes(self):
t0 = parse(self.data["clock"]["promptTime"])
t1 = parse(self.data["clock"]["finishTime"]) if self.data["clock"]["finishTime"] else time.time()
return round((t1 - t0) / 60.0, 1)
def stage_minutes(self):
out = {}
for name, st in self.data["stages"].items():
tot = 0.0
for r in st["runs"]:
if r["end"]:
tot += parse(r["end"]) - parse(r["start"])
out[name] = round(tot / 60.0, 1)
return out