Fix run_recipe endpoint to use content-addressable run_id
The run_recipe endpoint was still using uuid.uuid4() instead of compute_run_id(). Now it: - Computes deterministic run_id from inputs + recipe - Checks L1 cache before running - Checks L2 and pulls from IPFS if needed - Only runs Celery if output not found Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
65
server.py
65
server.py
@@ -1534,8 +1534,6 @@ async def run_recipe(recipe_id: str, request: RecipeRunRequest, ctx: UserContext
|
|||||||
# Build DAG from recipe
|
# Build DAG from recipe
|
||||||
dag = build_dag_from_recipe(yaml_config, request.inputs, recipe)
|
dag = build_dag_from_recipe(yaml_config, request.inputs, recipe)
|
||||||
|
|
||||||
# Create run
|
|
||||||
run_id = str(uuid.uuid4())
|
|
||||||
actor_id = ctx.actor_id
|
actor_id = ctx.actor_id
|
||||||
|
|
||||||
# Collect all input hashes
|
# Collect all input hashes
|
||||||
@@ -1544,12 +1542,71 @@ async def run_recipe(recipe_id: str, request: RecipeRunRequest, ctx: UserContext
|
|||||||
if fixed.content_hash:
|
if fixed.content_hash:
|
||||||
all_inputs.append(fixed.content_hash)
|
all_inputs.append(fixed.content_hash)
|
||||||
|
|
||||||
|
# Compute content-addressable run_id
|
||||||
|
run_id = compute_run_id(all_inputs, f"recipe:{recipe.name}")
|
||||||
|
output_name = f"{recipe.name}-{run_id[:8]}"
|
||||||
|
|
||||||
|
# Check L1 cache first
|
||||||
|
cached_run = await database.get_run_cache(run_id)
|
||||||
|
if cached_run:
|
||||||
|
output_hash = cached_run["output_hash"]
|
||||||
|
if cache_manager.has_content(output_hash):
|
||||||
|
logger.info(f"run_recipe: Cache hit for run_id={run_id[:16]}...")
|
||||||
|
return RunStatus(
|
||||||
|
run_id=run_id,
|
||||||
|
status="completed",
|
||||||
|
recipe=f"recipe:{recipe.name}",
|
||||||
|
inputs=all_inputs,
|
||||||
|
output_name=output_name,
|
||||||
|
created_at=cached_run.get("created_at", datetime.now(timezone.utc).isoformat()),
|
||||||
|
completed_at=cached_run.get("created_at", datetime.now(timezone.utc).isoformat()),
|
||||||
|
output_hash=output_hash,
|
||||||
|
username=actor_id,
|
||||||
|
provenance_cid=cached_run.get("provenance_cid"),
|
||||||
|
)
|
||||||
|
|
||||||
|
# Check L2 if not in L1
|
||||||
|
l2_server = ctx.l2_server
|
||||||
|
try:
|
||||||
|
l2_resp = http_requests.get(f"{l2_server}/assets/by-run-id/{run_id}", timeout=10)
|
||||||
|
if l2_resp.status_code == 200:
|
||||||
|
l2_data = l2_resp.json()
|
||||||
|
output_hash = l2_data.get("output_hash")
|
||||||
|
ipfs_cid = l2_data.get("ipfs_cid")
|
||||||
|
if output_hash and ipfs_cid:
|
||||||
|
logger.info(f"run_recipe: Found on L2, pulling from IPFS")
|
||||||
|
import ipfs_client
|
||||||
|
legacy_dir = CACHE_DIR / "legacy"
|
||||||
|
legacy_dir.mkdir(parents=True, exist_ok=True)
|
||||||
|
recovery_path = legacy_dir / output_hash
|
||||||
|
if ipfs_client.get_file(ipfs_cid, str(recovery_path)):
|
||||||
|
cache_manager._set_content_index(output_hash, output_hash)
|
||||||
|
cache_manager._set_ipfs_index(output_hash, ipfs_cid)
|
||||||
|
await database.save_run_cache(
|
||||||
|
run_id=run_id, output_hash=output_hash,
|
||||||
|
recipe=f"recipe:{recipe.name}", inputs=all_inputs,
|
||||||
|
ipfs_cid=ipfs_cid, provenance_cid=l2_data.get("provenance_cid"),
|
||||||
|
actor_id=actor_id,
|
||||||
|
)
|
||||||
|
return RunStatus(
|
||||||
|
run_id=run_id, status="completed",
|
||||||
|
recipe=f"recipe:{recipe.name}", inputs=all_inputs,
|
||||||
|
output_name=output_name,
|
||||||
|
created_at=datetime.now(timezone.utc).isoformat(),
|
||||||
|
completed_at=datetime.now(timezone.utc).isoformat(),
|
||||||
|
output_hash=output_hash, username=actor_id,
|
||||||
|
provenance_cid=l2_data.get("provenance_cid"),
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(f"run_recipe: L2 lookup failed: {e}")
|
||||||
|
|
||||||
|
# Not cached - run Celery
|
||||||
run = RunStatus(
|
run = RunStatus(
|
||||||
run_id=run_id,
|
run_id=run_id,
|
||||||
status="pending",
|
status="pending",
|
||||||
recipe=f"recipe:{recipe.name}",
|
recipe=f"recipe:{recipe.name}",
|
||||||
inputs=all_inputs,
|
inputs=all_inputs,
|
||||||
output_name=f"{recipe.name}-{run_id[:8]}",
|
output_name=output_name,
|
||||||
created_at=datetime.now(timezone.utc).isoformat(),
|
created_at=datetime.now(timezone.utc).isoformat(),
|
||||||
username=actor_id
|
username=actor_id
|
||||||
)
|
)
|
||||||
@@ -3652,6 +3709,7 @@ def render_page(title: str, content: str, actor_id: Optional[str] = None, active
|
|||||||
<a href="/runs" class="pb-3 px-1 font-medium transition-colors {runs_active}">Runs</a>
|
<a href="/runs" class="pb-3 px-1 font-medium transition-colors {runs_active}">Runs</a>
|
||||||
<a href="/recipes" class="pb-3 px-1 font-medium transition-colors {recipes_active}">Recipes</a>
|
<a href="/recipes" class="pb-3 px-1 font-medium transition-colors {recipes_active}">Recipes</a>
|
||||||
<a href="/media" class="pb-3 px-1 font-medium transition-colors {media_active}">Media</a>
|
<a href="/media" class="pb-3 px-1 font-medium transition-colors {media_active}">Media</a>
|
||||||
|
<a href="/download/client" class="pb-3 px-1 font-medium transition-colors text-gray-400 hover:text-white ml-auto" title="Download CLI client">Download Client</a>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<main>
|
<main>
|
||||||
@@ -3720,6 +3778,7 @@ def render_ui_html(actor_id: Optional[str] = None, tab: str = "runs") -> str:
|
|||||||
<a href="/runs" class="pb-3 px-1 font-medium transition-colors {runs_active}">Runs</a>
|
<a href="/runs" class="pb-3 px-1 font-medium transition-colors {runs_active}">Runs</a>
|
||||||
<a href="/recipes" class="pb-3 px-1 font-medium transition-colors {recipes_active}">Recipes</a>
|
<a href="/recipes" class="pb-3 px-1 font-medium transition-colors {recipes_active}">Recipes</a>
|
||||||
<a href="/media" class="pb-3 px-1 font-medium transition-colors {media_active}">Media</a>
|
<a href="/media" class="pb-3 px-1 font-medium transition-colors {media_active}">Media</a>
|
||||||
|
<a href="/download/client" class="pb-3 px-1 font-medium transition-colors text-gray-400 hover:text-white ml-auto" title="Download CLI client">Download Client</a>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<div id="content" hx-get="{content_url}" hx-trigger="load" hx-swap="innerHTML">
|
<div id="content" hx-get="{content_url}" hx-trigger="load" hx-swap="innerHTML">
|
||||||
|
|||||||
Reference in New Issue
Block a user