mirror of https://github.com/dapr/dapr-agents.git
112 lines
3.6 KiB
Python
112 lines
3.6 KiB
Python
from typing import List, Dict, Any, Optional
|
||
|
||
|
||
def update_step_statuses(plan: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
|
||
"""
|
||
Ensures step and sub-step statuses follow logical progression:
|
||
• Parent completes if all substeps complete.
|
||
• Parent goes in_progress if any substep is in_progress.
|
||
• If substeps start completing, parent moves in_progress.
|
||
• If parent was completed but a substep reverts to in_progress, parent downgrades.
|
||
• Standalone steps (no substeps) are only updated via explicit status_updates.
|
||
"""
|
||
for step in plan:
|
||
subs = step.get("substeps", None)
|
||
|
||
# --- NO substeps: do nothing here (explicit updates only) ---
|
||
if subs is None:
|
||
continue
|
||
|
||
# If substeps is not a list or is an empty list, treat as no‐substeps too:
|
||
if not isinstance(subs, list) or len(subs) == 0:
|
||
continue
|
||
|
||
# Collect child statuses
|
||
statuses = {ss["status"] for ss in subs}
|
||
|
||
# 1. All done → parent done
|
||
if statuses == {"completed"}:
|
||
step["status"] = "completed"
|
||
|
||
# 2. Any in_progress → parent in_progress
|
||
elif "in_progress" in statuses:
|
||
step["status"] = "in_progress"
|
||
|
||
# 3. Some done, parent not yet started → bump to in_progress
|
||
elif "completed" in statuses and step["status"] == "not_started":
|
||
step["status"] = "in_progress"
|
||
|
||
# 4. If parent was completed but a child is in_progress, downgrade
|
||
elif step["status"] == "completed" and any(s != "completed" for s in statuses):
|
||
step["status"] = "in_progress"
|
||
|
||
return plan
|
||
|
||
|
||
def validate_plan_structure(plan: List[Dict[str, Any]]) -> bool:
|
||
"""
|
||
Validates if the plan structure follows the correct schema.
|
||
|
||
Args:
|
||
plan (List[Dict[str, Any]]): The execution plan.
|
||
|
||
Returns:
|
||
bool: True if the plan structure is valid, False otherwise.
|
||
"""
|
||
required_keys = {"step", "description", "status"}
|
||
for step in plan:
|
||
if not required_keys.issubset(step.keys()):
|
||
return False
|
||
if "substeps" in step:
|
||
for substep in step["substeps"]:
|
||
if not {"substep", "description", "status"}.issubset(substep.keys()):
|
||
return False
|
||
return True
|
||
|
||
|
||
def find_step_in_plan(
|
||
plan: List[Dict[str, Any]], step: int, substep: Optional[float] = None
|
||
) -> Optional[Dict[str, Any]]:
|
||
"""
|
||
Finds a specific step or substep in a plan.
|
||
|
||
Args:
|
||
plan (List[Dict[str, Any]]): The execution plan.
|
||
step (int): The step number to find.
|
||
substep (Optional[float]): The substep number (if applicable).
|
||
|
||
Returns:
|
||
Dict[str, Any] | None: The found step/substep dictionary or None if not found.
|
||
"""
|
||
for step_entry in plan:
|
||
if step_entry["step"] == step:
|
||
if substep is None:
|
||
return step_entry
|
||
|
||
for sub in step_entry.get("substeps", []):
|
||
if sub["substep"] == substep:
|
||
return sub
|
||
return None
|
||
|
||
|
||
def restructure_plan(
|
||
plan: List[Dict[str, Any]], updates: List[Dict[str, Any]]
|
||
) -> List[Dict[str, Any]]:
|
||
"""
|
||
Applies restructuring updates to the task execution plan.
|
||
|
||
Args:
|
||
plan (List[Dict[str, Any]]): The current execution plan.
|
||
updates (List[Dict[str, Any]]): A list of updates to apply.
|
||
|
||
Returns:
|
||
List[Dict[str, Any]]: The updated execution plan.
|
||
"""
|
||
for update in updates:
|
||
step_id = update["step"]
|
||
step_entry = find_step_in_plan(plan, step_id)
|
||
if step_entry:
|
||
step_entry.update(update)
|
||
|
||
return plan
|