feat: initial release - Hunzi agent framework v0.1.0
This commit is contained in:
@@ -0,0 +1,168 @@
|
||||
"""Workflow definition models — dataclasses for describing DAG-based workflows."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import uuid
|
||||
from dataclasses import dataclass, field
|
||||
from datetime import datetime
|
||||
from enum import Enum
|
||||
from typing import Any, Dict, List, Optional
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Enums
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
class StepStatus(str, Enum):
|
||||
"""Execution status of a single workflow step."""
|
||||
|
||||
PENDING = "pending"
|
||||
RUNNING = "running"
|
||||
COMPLETED = "completed"
|
||||
FAILED = "failed"
|
||||
SKIPPED = "skipped"
|
||||
|
||||
|
||||
class WorkflowStatus(str, Enum):
|
||||
"""Overall workflow execution status."""
|
||||
|
||||
PENDING = "pending"
|
||||
RUNNING = "running"
|
||||
COMPLETED = "completed"
|
||||
FAILED = "failed"
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Step Definition
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
@dataclass
|
||||
class StepDef:
|
||||
"""工作流步骤定义"""
|
||||
|
||||
name: str
|
||||
agent_id: str # 使用哪个 Agent
|
||||
prompt: str # 给 Agent 的提示
|
||||
inputs: List[str] = field(default_factory=list) # 依赖的上游步骤名
|
||||
condition: Optional[str] = None # 条件表达式(可选),如 "step_a.output == 'yes'"
|
||||
parallel: bool = False # 是否可并行(与同层级无依赖的其他步骤一起执行)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Workflow Definition
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
@dataclass
|
||||
class Workflow:
|
||||
"""工作流定义"""
|
||||
|
||||
id: str = field(default_factory=lambda: str(uuid.uuid4()))
|
||||
name: str = ""
|
||||
description: str = ""
|
||||
steps: List[StepDef] = field(default_factory=list)
|
||||
created_at: datetime = field(default_factory=datetime.utcnow)
|
||||
|
||||
# -- serialization --------------------------------------------------------
|
||||
|
||||
def to_dict(self) -> Dict[str, Any]:
|
||||
"""Serialize workflow to a plain dict."""
|
||||
return {
|
||||
"id": self.id,
|
||||
"name": self.name,
|
||||
"description": self.description,
|
||||
"steps": [
|
||||
{
|
||||
"name": step.name,
|
||||
"agent_id": step.agent_id,
|
||||
"prompt": step.prompt,
|
||||
"inputs": step.inputs,
|
||||
"condition": step.condition,
|
||||
"parallel": step.parallel,
|
||||
}
|
||||
for step in self.steps
|
||||
],
|
||||
"created_at": self.created_at.isoformat(),
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls, data: Dict[str, Any]) -> "Workflow":
|
||||
"""Deserialize workflow from a plain dict."""
|
||||
return cls(
|
||||
id=data.get("id", str(uuid.uuid4())),
|
||||
name=data.get("name", ""),
|
||||
description=data.get("description", ""),
|
||||
steps=[
|
||||
StepDef(
|
||||
name=s["name"],
|
||||
agent_id=s["agent_id"],
|
||||
prompt=s["prompt"],
|
||||
inputs=s.get("inputs", []),
|
||||
condition=s.get("condition"),
|
||||
parallel=s.get("parallel", False),
|
||||
)
|
||||
for s in data.get("steps", [])
|
||||
],
|
||||
created_at=datetime.fromisoformat(data["created_at"]) if data.get("created_at") else datetime.utcnow(),
|
||||
)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Execution Results
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
@dataclass
|
||||
class StepResult:
|
||||
"""Result of executing a single step."""
|
||||
|
||||
name: str
|
||||
status: StepStatus = StepStatus.PENDING
|
||||
output: Any = None
|
||||
error: Optional[str] = None
|
||||
started_at: Optional[datetime] = None
|
||||
completed_at: Optional[datetime] = None
|
||||
|
||||
|
||||
@dataclass
|
||||
class WorkflowResult:
|
||||
"""Complete result of a workflow execution."""
|
||||
|
||||
workflow_id: str
|
||||
workflow_name: str
|
||||
status: WorkflowStatus = WorkflowStatus.PENDING
|
||||
step_results: Dict[str, StepResult] = field(default_factory=dict)
|
||||
started_at: Optional[datetime] = None
|
||||
completed_at: Optional[datetime] = None
|
||||
error: Optional[str] = None
|
||||
|
||||
# -- helpers --------------------------------------------------------------
|
||||
|
||||
@property
|
||||
def all_completed(self) -> bool:
|
||||
return all(
|
||||
r.status in (StepStatus.COMPLETED, StepStatus.SKIPPED)
|
||||
for r in self.step_results.values()
|
||||
)
|
||||
|
||||
@property
|
||||
def any_failed(self) -> bool:
|
||||
return any(r.status == StepStatus.FAILED for r in self.step_results.values())
|
||||
|
||||
def to_dict(self) -> Dict[str, Any]:
|
||||
return {
|
||||
"workflow_id": self.workflow_id,
|
||||
"workflow_name": self.workflow_name,
|
||||
"status": self.status.value,
|
||||
"step_results": {
|
||||
name: {
|
||||
"status": r.status.value,
|
||||
"output": r.output,
|
||||
"error": r.error,
|
||||
"started_at": r.started_at.isoformat() if r.started_at else None,
|
||||
"completed_at": r.completed_at.isoformat() if r.completed_at else None,
|
||||
}
|
||||
for name, r in self.step_results.items()
|
||||
},
|
||||
"started_at": self.started_at.isoformat() if self.started_at else None,
|
||||
"completed_at": self.completed_at.isoformat() if self.completed_at else None,
|
||||
"error": self.error,
|
||||
}
|
||||
Reference in New Issue
Block a user