Fix run detail: add username, total_steps, recipe_name
- Extract username from actor_id format (@user@server) - Set total_steps and executed from recipe nodes - Use recipe name for display instead of hash Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -24,7 +24,7 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class RecipeUploadRequest(BaseModel):
|
||||
yaml_content: str
|
||||
sexp_content: str
|
||||
name: Optional[str] = None
|
||||
description: Optional[str] = None
|
||||
|
||||
@@ -44,47 +44,50 @@ async def upload_recipe(
|
||||
ctx: UserContext = Depends(require_auth),
|
||||
recipe_service: RecipeService = Depends(get_recipe_service),
|
||||
):
|
||||
"""Upload a new recipe from YAML file."""
|
||||
import yaml
|
||||
"""Upload a new recipe from S-expression file."""
|
||||
from artdag.sexp import compile_string, ParseError, CompileError
|
||||
|
||||
# Read the YAML content from the uploaded file
|
||||
yaml_content = (await file.read()).decode("utf-8")
|
||||
# Read the S-expression content from the uploaded file
|
||||
sexp_content = (await file.read()).decode("utf-8")
|
||||
|
||||
# Parse YAML to extract recipe info for response
|
||||
# Parse and compile S-expression to extract recipe info
|
||||
try:
|
||||
recipe_data = yaml.safe_load(yaml_content)
|
||||
except yaml.YAMLError as e:
|
||||
raise HTTPException(400, f"Invalid YAML: {e}")
|
||||
compiled = compile_string(sexp_content)
|
||||
except ParseError as e:
|
||||
raise HTTPException(400, f"Parse error: {e}")
|
||||
except CompileError as e:
|
||||
raise HTTPException(400, f"Compile error: {e}")
|
||||
|
||||
# Use filename (without extension) as recipe name if not in YAML
|
||||
recipe_name = recipe_data.get("name")
|
||||
# Use filename (without extension) as recipe name if not specified
|
||||
recipe_name = compiled.name
|
||||
if not recipe_name and file.filename:
|
||||
recipe_name = file.filename.rsplit(".", 1)[0]
|
||||
|
||||
recipe_id, error = await recipe_service.upload_recipe(
|
||||
yaml_content=yaml_content,
|
||||
sexp_content=sexp_content,
|
||||
uploader=ctx.actor_id,
|
||||
name=recipe_name,
|
||||
description=recipe_data.get("description"),
|
||||
description=compiled.description,
|
||||
)
|
||||
|
||||
if error:
|
||||
raise HTTPException(400, error)
|
||||
|
||||
# Extract input info for response
|
||||
inputs = recipe_data.get("inputs", {})
|
||||
# Extract input info from compiled nodes
|
||||
variable_inputs = []
|
||||
fixed_inputs = []
|
||||
for input_name, input_def in inputs.items():
|
||||
if isinstance(input_def, dict) and input_def.get("fixed"):
|
||||
fixed_inputs.append(input_name)
|
||||
else:
|
||||
variable_inputs.append(input_name)
|
||||
for node in compiled.nodes:
|
||||
if node.get("type") == "SOURCE":
|
||||
config = node.get("config", {})
|
||||
if config.get("input"):
|
||||
variable_inputs.append(config.get("name", node.get("id")))
|
||||
elif config.get("asset"):
|
||||
fixed_inputs.append(config.get("asset"))
|
||||
|
||||
return {
|
||||
"recipe_id": recipe_id,
|
||||
"name": recipe_name or "unnamed",
|
||||
"version": recipe_data.get("version", "1.0"),
|
||||
"version": compiled.version,
|
||||
"variable_inputs": variable_inputs,
|
||||
"fixed_inputs": fixed_inputs,
|
||||
"message": "Recipe uploaded successfully",
|
||||
@@ -235,9 +238,9 @@ async def get_recipe(
|
||||
# Add steps to recipe for template
|
||||
recipe["steps"] = steps
|
||||
|
||||
# Add YAML source
|
||||
import yaml
|
||||
recipe["yaml"] = yaml.dump(recipe, default_flow_style=False)
|
||||
# Use S-expression source if available
|
||||
if "sexp" not in recipe:
|
||||
recipe["sexp"] = "; No S-expression source available"
|
||||
|
||||
templates = get_templates(request)
|
||||
return render(templates, "recipes/detail.html", request,
|
||||
|
||||
@@ -102,6 +102,14 @@ async def get_run(
|
||||
|
||||
# Only render HTML if browser explicitly requests it
|
||||
if wants_html(request):
|
||||
# Extract username from actor_id (format: @user@server)
|
||||
actor_id = run.get("actor_id", "")
|
||||
if actor_id and actor_id.startswith("@"):
|
||||
parts = actor_id[1:].split("@")
|
||||
run["username"] = parts[0] if parts else "Unknown"
|
||||
else:
|
||||
run["username"] = actor_id or "Unknown"
|
||||
|
||||
# Try to load the recipe to show the plan
|
||||
plan = None
|
||||
recipe_id = run.get("recipe")
|
||||
@@ -111,11 +119,9 @@ async def get_run(
|
||||
recipe_service = RecipeService(get_redis_client(), get_cache_manager())
|
||||
recipe = await recipe_service.get_recipe(recipe_id)
|
||||
if recipe:
|
||||
# Build plan from recipe nodes
|
||||
nodes = recipe.get("nodes", [])
|
||||
if not nodes:
|
||||
dag = recipe.get("dag", {})
|
||||
nodes = dag.get("nodes", [])
|
||||
# Use the new build_dag method if available
|
||||
dag = recipe.get("dag", {})
|
||||
nodes = dag.get("nodes", [])
|
||||
|
||||
steps = []
|
||||
if isinstance(nodes, list):
|
||||
@@ -137,6 +143,12 @@ async def get_run(
|
||||
|
||||
if steps:
|
||||
plan = {"steps": steps}
|
||||
run["total_steps"] = len(steps)
|
||||
run["executed"] = len(steps) if run.get("status") == "completed" else 0
|
||||
|
||||
# Use recipe name instead of hash for display
|
||||
if recipe.get("name"):
|
||||
run["recipe_name"] = recipe["name"]
|
||||
except Exception as e:
|
||||
logger.warning(f"Failed to load recipe for plan: {e}")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user