Phase 2: Production Bible — Character + Location bibles from scene data
Layer 3 implementation: - Character Bible: canonical names, aliases, arcs, relationships, wardrobe states, emotional arcs, reference prompts — all grounded in scene evidence - Location Bible: canonical names, variants, descriptions, types, features, mood associations, reference prompts — all grounded in scene evidence - Combined Production Bible output for downstream layers - Bible validator: duplicate detection, scene reference checks, hallucination detection, UNKNOWN field flagging - Prompt contracts: L3_character_bible_v1, L3_location_bible_v1 - Named versioned output: character_bible_v1.json, location_bible_v1.json, production_bible_v1.json - CLI: --phase 2 runs bible only, --phase omitted runs both phases - OutputWriter: added write_named/write_named_raw for non-scene outputs Tested on the_last_backup: 3 characters, 5 locations, 0 hallucinations, 3 warnings (UNKNOWN physical_description — correct behavior) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -11,13 +11,14 @@ from dotenv import load_dotenv
|
||||
def main():
|
||||
load_dotenv()
|
||||
|
||||
parser = argparse.ArgumentParser(description="AI Movie Production Pipeline — Phase 1")
|
||||
parser.add_argument("--script", type=str, help="Path to .fountain script file")
|
||||
parser = argparse.ArgumentParser(description="AI Movie Production Pipeline")
|
||||
parser.add_argument("--script", type=str, help="Path to .fountain script file (Phase 1)")
|
||||
parser.add_argument("--project", type=str, help="Project name (determines output directory)")
|
||||
parser.add_argument("--phase", type=int, default=None, choices=[1, 2], help="Run specific phase only (1=ingestion+extraction, 2=bible)")
|
||||
parser.add_argument("--model", type=str, default="qwen3:14b", help="Model ID (default: qwen3:14b)")
|
||||
parser.add_argument("--backend", type=str, default="ollama", choices=["ollama", "anthropic"], help="AI backend (default: ollama)")
|
||||
parser.add_argument("--ollama-url", type=str, default="http://localhost:11434", help="Ollama server URL")
|
||||
parser.add_argument("--scene", type=int, default=None, help="Process only this scene number")
|
||||
parser.add_argument("--scene", type=int, default=None, help="Process only this scene number (Phase 1)")
|
||||
parser.add_argument("--dry-run", action="store_true", help="Validate inputs only, no AI calls")
|
||||
parser.add_argument("--force", action="store_true", help="Ignore cache, re-run even if unchanged")
|
||||
parser.add_argument("--test", action="store_true", help="Run test suite against test_scripts/")
|
||||
@@ -29,36 +30,63 @@ def main():
|
||||
run_tests(args.model, args.backend, args.ollama_url, args.output_dir)
|
||||
return
|
||||
|
||||
if not args.script or not args.project:
|
||||
parser.error("--script and --project are required (unless using --test)")
|
||||
if not args.project:
|
||||
parser.error("--project is required (unless using --test)")
|
||||
|
||||
api_key = os.environ.get("ANTHROPIC_API_KEY", "")
|
||||
if args.backend == "anthropic" and not api_key and not args.dry_run:
|
||||
print("ERROR: ANTHROPIC_API_KEY not set. Set it in .env or environment.")
|
||||
sys.exit(1)
|
||||
|
||||
if not os.path.exists(args.script):
|
||||
print(f"ERROR: Script file not found: {args.script}")
|
||||
sys.exit(1)
|
||||
run_phase1_flag = args.phase is None or args.phase == 1
|
||||
run_phase2_flag = args.phase is None or args.phase == 2
|
||||
|
||||
from src.execution.runner import run_phase1
|
||||
# Phase 1: Script Ingestion + Understanding
|
||||
if run_phase1_flag:
|
||||
if not args.script:
|
||||
if args.phase == 1 or args.phase is None:
|
||||
parser.error("--script is required for Phase 1")
|
||||
else:
|
||||
if not os.path.exists(args.script):
|
||||
print(f"ERROR: Script file not found: {args.script}")
|
||||
sys.exit(1)
|
||||
|
||||
result = run_phase1(
|
||||
script_path=args.script,
|
||||
project_name=args.project,
|
||||
api_key=api_key,
|
||||
model=args.model,
|
||||
backend=args.backend,
|
||||
ollama_url=args.ollama_url,
|
||||
output_dir=args.output_dir,
|
||||
scene_filter=args.scene,
|
||||
dry_run=args.dry_run,
|
||||
force=args.force,
|
||||
)
|
||||
from src.execution.runner import run_phase1
|
||||
|
||||
if not result.success:
|
||||
print(f"\nPIPELINE FAILED: {result.stop_reason}")
|
||||
sys.exit(1)
|
||||
result = run_phase1(
|
||||
script_path=args.script,
|
||||
project_name=args.project,
|
||||
api_key=api_key,
|
||||
model=args.model,
|
||||
backend=args.backend,
|
||||
ollama_url=args.ollama_url,
|
||||
output_dir=args.output_dir,
|
||||
scene_filter=args.scene,
|
||||
dry_run=args.dry_run,
|
||||
force=args.force,
|
||||
)
|
||||
|
||||
if not result.success:
|
||||
print(f"\nPHASE 1 FAILED: {result.stop_reason}")
|
||||
sys.exit(1)
|
||||
|
||||
# Phase 2: Production Bible
|
||||
if run_phase2_flag:
|
||||
from src.bible.runner import run_phase2
|
||||
|
||||
bible_result = run_phase2(
|
||||
project_name=args.project,
|
||||
model=args.model,
|
||||
backend=args.backend,
|
||||
ollama_url=args.ollama_url,
|
||||
api_key=api_key,
|
||||
output_dir=args.output_dir,
|
||||
dry_run=args.dry_run,
|
||||
)
|
||||
|
||||
if not bible_result.success:
|
||||
print(f"\nPHASE 2 FAILED: {bible_result.stop_reason}")
|
||||
sys.exit(1)
|
||||
|
||||
print("\nPIPELINE COMPLETE")
|
||||
sys.exit(0)
|
||||
|
||||
Reference in New Issue
Block a user