Add COMPOUND node execution and S-expression API

- Execute COMPOUND nodes with combined FFmpeg filter chain
- Handle TRANSFORM, RESIZE, SEGMENT filters in chain
- Migrate orchestrator to S-expression recipes (remove YAML)
- Update API endpoints to use recipe_sexp parameter
- Extract analysis nodes from recipe for dynamic analysis

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
gilesb
2026-01-12 01:26:26 +00:00
parent 3599f3779b
commit 8e0b473925
4 changed files with 391 additions and 79 deletions

View File

@@ -11,7 +11,6 @@ import uuid
from datetime import datetime, timezone
from typing import Dict, List, Optional
import yaml
from fastapi import APIRouter, Depends, HTTPException
from pydantic import BaseModel
@@ -26,9 +25,8 @@ RUNS_KEY_PREFIX = "artdag:run:"
class PlanRequest(BaseModel):
recipe_yaml: str
recipe_sexp: str
input_hashes: Dict[str, str]
features: List[str] = ["beats", "energy"]
class ExecutePlanRequest(BaseModel):
@@ -37,9 +35,8 @@ class ExecutePlanRequest(BaseModel):
class RecipeRunRequest(BaseModel):
recipe_yaml: str
recipe_sexp: str
input_hashes: Dict[str, str]
features: List[str] = ["beats", "energy"]
def compute_run_id(input_hashes: List[str], recipe: str, recipe_hash: str = None) -> str:
@@ -68,9 +65,8 @@ async def generate_plan_endpoint(
try:
task = generate_plan.delay(
recipe_yaml=request.recipe_yaml,
recipe_sexp=request.recipe_sexp,
input_hashes=request.input_hashes,
features=request.features,
)
# Wait for result (plan generation is usually fast)
@@ -136,15 +132,16 @@ async def run_recipe_endpoint(
Returns immediately with run_id. Poll /api/run/{run_id} for status.
"""
from tasks.orchestrate import run_recipe
from artdag.sexp import compile_string
import database
redis = get_redis_client()
cache = get_cache_manager()
# Parse recipe name
# Parse recipe name from S-expression
try:
recipe_data = yaml.safe_load(request.recipe_yaml)
recipe_name = recipe_data.get("name", "unknown")
compiled = compile_string(request.recipe_sexp)
recipe_name = compiled.name or "unknown"
except Exception:
recipe_name = "unknown"
@@ -152,7 +149,7 @@ async def run_recipe_endpoint(
run_id = compute_run_id(
list(request.input_hashes.values()),
recipe_name,
hashlib.sha3_256(request.recipe_yaml.encode()).hexdigest()
hashlib.sha3_256(request.recipe_sexp.encode()).hexdigest()
)
# Check if already completed
@@ -171,9 +168,8 @@ async def run_recipe_endpoint(
# Submit to Celery
try:
task = run_recipe.delay(
recipe_yaml=request.recipe_yaml,
recipe_sexp=request.recipe_sexp,
input_hashes=request.input_hashes,
features=request.features,
run_id=run_id,
)