Flow Auto-Pilot banks: compile sheets into 11 attach-grouped prompt banks + runbook
82 remaining shots x2 takes = 164 gens (~4,160 cr of 9,080). Banks live in ~/Documents/flowext/banks/rsg/, grouped by (tier, attach-set) since the extension types text only — composer attachments/model set once per batch. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
4f7d21df2b
commit
91930f820b
39
production/flow/AUTOPILOT_RUNBOOK.md
Normal file
39
production/flow/AUTOPILOT_RUNBOOK.md
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
# RSG × Flow Auto-Pilot runbook (2026-07-20)
|
||||||
|
|
||||||
|
The sheets are compiled into 11 prompt banks at `~/Documents/flowext/banks/rsg/`
|
||||||
|
(built by `tools/make_flow_bank.py` — rerun it after editing any sheet). Each
|
||||||
|
bank = one (model tier, attach-set) combo, 2 takes per shot, ~4,160 credits
|
||||||
|
total. The extension types prompt text only, so per batch YOU set the composer
|
||||||
|
state once; then it drains unattended.
|
||||||
|
|
||||||
|
## Per-batch loop
|
||||||
|
1. Brave → the RECORD STORE GUY Flow project (not djsim/shop!).
|
||||||
|
2. Composer: attach the bank's characters (in the bank filename), model per
|
||||||
|
tier — `fast__*` = Veo 3.1 Fast, `quality__*` = Veo 3.1 Quality — outputs 1x,
|
||||||
|
Agent settings "Confirm before generating: Never".
|
||||||
|
3. Enqueue the bank (N = the number prefix):
|
||||||
|
`python3 ~/Documents/recordstoreguy/tools/make_flow_bank.py --enqueue N`
|
||||||
|
4. Extension popup: tick Auto-start. Walk away. Downloads land in
|
||||||
|
`~/Downloads/flowrinse/rsg/`.
|
||||||
|
5. Between banks: swap attachments/model, enqueue the next N.
|
||||||
|
|
||||||
|
## Bank order (biggest first)
|
||||||
|
01 fast Dez ×120 gens · 02 fast Dez+NameDropper ×6 · 03 fast Dez+BoringGuy ×4 ·
|
||||||
|
04 fast DeadWax ×2 · 05 fast no-chars ×2 · 06 fast BoringGuy ×2 ·
|
||||||
|
07 QUALITY Dez+DeadWax ×14 · 08 QUALITY Dez ×8 · 09 QUALITY Mildred ×2 ·
|
||||||
|
10 QUALITY Dez+Postman ×2 · 11 QUALITY Dez+Accountant ×2
|
||||||
|
|
||||||
|
## Gotchas
|
||||||
|
- 96 stale djsim wardrobe IMAGE tasks are still pending in the live queue.
|
||||||
|
Videos are served first so RSG preempts them, but when the videos drain the
|
||||||
|
images will fire into whatever tab is open — untick Auto-start when the rsg
|
||||||
|
category stops growing, or strip them from
|
||||||
|
`~/Library/Application Support/flowrinse/queue.jsonl` (backup first; ids not
|
||||||
|
in results.jsonl are the pending ones). They're also stashed-by-copy at
|
||||||
|
`~/Documents/flowext/banks/djsim_wardrobe_pending_20260720.jsonl`.
|
||||||
|
- Keepers: postprocess into the engine —
|
||||||
|
`tools/postprocess.sh <raw> <shot_id> <action|death>` → `engine/clips/`,
|
||||||
|
then tune windows in `?debug=1`. Clip filenames already match scenes.json.
|
||||||
|
- mf1 prompts end with a different lettering clause on purpose (the letters are
|
||||||
|
the hazard). Everything else ends with the standard no-lettering sentence.
|
||||||
|
- Log keepers in production/SHOTLOG.csv per the sheets.
|
||||||
93
tools/make_flow_bank.py
Normal file
93
tools/make_flow_bank.py
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Compile production/flow/*_SHOTS.md into Flow Auto-Pilot prompt banks.
|
||||||
|
|
||||||
|
The flowext extension types prompt TEXT only — attached characters and the
|
||||||
|
model/output-count are composer state set by hand once per batch. So shots are
|
||||||
|
grouped into one bank per (tier, attach-set); John opens the RSG Flow project,
|
||||||
|
attaches that bank's characters, sets the model (Fast/Quality) and 1x, then the
|
||||||
|
bank is enqueued and drains unattended. 2 takes per shot per the spend doctrine.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
make_flow_bank.py # write banks to ~/Documents/flowext/banks/rsg/
|
||||||
|
make_flow_bank.py --enqueue N # POST bank number N to localhost:8017/enqueue
|
||||||
|
"""
|
||||||
|
import json
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
import urllib.request
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
ROOT = Path(__file__).resolve().parent.parent
|
||||||
|
SHEETS = ["TRACK1", "TRACK2", "TRACK3", "TRACK4", "TRACK5", "TRACK6", "TRACK7",
|
||||||
|
"SHIFTS", "EXTRAS"]
|
||||||
|
BANKS = Path.home() / "Documents/flowext/banks/rsg"
|
||||||
|
SKIP = {"Logging", "t1_whitelabel", "attract_loop", "intro_qual"} # done or cut
|
||||||
|
TAKES = 2
|
||||||
|
SERVER = "http://localhost:8017"
|
||||||
|
|
||||||
|
|
||||||
|
def parse():
|
||||||
|
shots = [] # (id, chars tuple, quality bool, prompt)
|
||||||
|
for name in SHEETS:
|
||||||
|
text = (ROOT / f"production/flow/{name}_SHOTS.md").read_text()
|
||||||
|
for m in re.finditer(r"^## (.+?)\n(.*?)(?=^## |\Z)", text, re.M | re.S):
|
||||||
|
header, body = m.group(1), m.group(2)
|
||||||
|
sid = header.split()[0]
|
||||||
|
if sid in SKIP:
|
||||||
|
continue
|
||||||
|
quality = "QUALITY" in header
|
||||||
|
am = re.search(r"\[attach: ([^\]]+)\]", header) or \
|
||||||
|
re.search(r"\[([^\]]+)\]", header)
|
||||||
|
chars = () if not am or am.group(1) == "no characters" else \
|
||||||
|
tuple(c.strip() for c in am.group(1).split(","))
|
||||||
|
lines = [l.strip() for l in body.strip().splitlines() if l.strip()]
|
||||||
|
if lines and lines[0].startswith("(") and lines[0].endswith(")"):
|
||||||
|
lines = lines[1:] # production note, not paste text
|
||||||
|
prompt = re.sub(r"\s+", " ", " ".join(lines)).strip()
|
||||||
|
if len(prompt) < 80: # header wrap fragments / non-prompt sections
|
||||||
|
print(f" !! skipping {sid}: body too short ({len(prompt)} ch)")
|
||||||
|
continue
|
||||||
|
shots.append((sid, chars, quality, prompt))
|
||||||
|
return shots
|
||||||
|
|
||||||
|
|
||||||
|
def slug(chars):
|
||||||
|
return "+".join(re.sub(r"^the ", "", c.lower()).replace(" ", "") for c in chars) or "nochars"
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
shots = parse()
|
||||||
|
groups = {} # (quality, chars) -> [shots] — keep sheet order within a group
|
||||||
|
for s in shots:
|
||||||
|
groups.setdefault((s[2], s[1]), []).append(s)
|
||||||
|
# fast banks first (bulk of the game), quality after; big groups first
|
||||||
|
order = sorted(groups, key=lambda k: (k[0], -len(groups[k])))
|
||||||
|
BANKS.mkdir(parents=True, exist_ok=True)
|
||||||
|
for old in BANKS.glob("*.jsonl"):
|
||||||
|
old.unlink()
|
||||||
|
for n, key in enumerate(order, 1):
|
||||||
|
quality, chars = key
|
||||||
|
model = "Veo 3.1 - Quality" if quality else "Veo 3.1 - Fast"
|
||||||
|
path = BANKS / f"{n:02d}__{'quality' if quality else 'fast'}__{slug(chars)}.jsonl"
|
||||||
|
with open(path, "w") as f:
|
||||||
|
for sid, _, _, prompt in groups[key]:
|
||||||
|
for take in range(1, TAKES + 1):
|
||||||
|
f.write(json.dumps({
|
||||||
|
"id": f"rsg_{sid}__t{take}", "kind": "video", "model": model,
|
||||||
|
"title": f"{sid} take {take}", "prompt": prompt,
|
||||||
|
"category": "rsg"}) + "\n")
|
||||||
|
gens = len(groups[key]) * TAKES
|
||||||
|
cr = gens * (100 if quality else 10)
|
||||||
|
print(f"{path.name}: {len(groups[key])} shots x{TAKES} = {gens} gens ~{cr} cr"
|
||||||
|
f" [attach: {', '.join(chars) or 'none'}]")
|
||||||
|
if len(sys.argv) > 2 and sys.argv[1] == "--enqueue":
|
||||||
|
bank = sorted(BANKS.glob(f"{int(sys.argv[2]):02d}__*.jsonl"))[0]
|
||||||
|
for line in bank.read_text().splitlines():
|
||||||
|
req = urllib.request.Request(SERVER + "/enqueue", line.encode(),
|
||||||
|
{"Content-Type": "application/json"})
|
||||||
|
urllib.request.urlopen(req, timeout=5).read()
|
||||||
|
print(f"enqueued {bank.name}")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
Loading…
Reference in New Issue
Block a user