qwen-hackthon / workflow /engine.py
Yousef
Add all project files
f97e4ec
Raw
History Blame Contribute Delete
7.89 kB
import json
import uuid
import re
def sanitize_for_json(obj):
if isinstance(obj, str):
return re.sub(r'[\x00-\x08\x0b\x0c\x0e-\x1f]', '', obj)
if isinstance(obj, dict):
return {k: sanitize_for_json(v) for k, v in obj.items()}
if isinstance(obj, list):
return [sanitize_for_json(v) for v in obj]
return obj
WORKFLOWS = {
"support": {
"steps": [
"triage",
"resolve",
"draft_response",
"quality_check",
"human_approval",
"send"
],
"description": "End-to-end customer support ticket handling"
},
"refund": {
"steps": [
"verify_purchase",
"check_policy",
"calculate_refund",
"human_approval",
"process_refund",
"notify_customer"
],
"description": "Refund processing workflow"
}
}
STEP_LABELS = {
"triage": "Classifying issue",
"resolve": "Finding solution",
"draft_response": "Drafting response",
"quality_check": "Reviewing quality",
"human_approval": "Awaiting your approval",
"send": "Sending response",
"verify_purchase": "Verifying purchase",
"check_policy": "Checking policy",
"calculate_refund": "Calculating refund",
"process_refund": "Processing refund",
"notify_customer": "Notifying customer"
}
STEP_ICONS = {
"triage": "🔍",
"resolve": "🔧",
"draft_response": "✍️",
"quality_check": "✅",
"human_approval": "🙏",
"send": "📤",
"verify_purchase": "🔍",
"check_policy": "📋",
"calculate_refund": "💰",
"process_refund": "⚙️",
"notify_customer": "📧"
}
class WorkflowEngine:
def __init__(self):
self.active_workflows = {}
self.agents = {}
def register_agents(self, agents_dict: dict):
self.agents = agents_dict
def create_workflow(self, session_id: str, workflow_type: str = "support") -> dict:
steps = list(WORKFLOWS[workflow_type]["steps"])
self.active_workflows[session_id] = {
"type": workflow_type,
"current_step": 0,
"steps": steps,
"status": "in_progress",
"data": {},
"human_approval_needed": False,
"session_id": session_id
}
return self.active_workflows[session_id]
def get_workflow(self, session_id: str) -> dict:
return self.active_workflows.get(session_id)
def get_status(self, session_id: str) -> dict:
wf = self.active_workflows.get(session_id)
if not wf:
return None
step_idx = wf["current_step"]
current_step = wf["steps"][step_idx] if step_idx < len(wf["steps"]) else None
return {
"workflow_id": session_id,
"type": wf["type"],
"current_step": current_step,
"step_number": step_idx,
"total_steps": len(wf["steps"]),
"status": wf["status"],
"needs_human": wf["human_approval_needed"],
"data": wf["data"],
"steps": wf["steps"]
}
def execute_step(self, session_id: str) -> dict:
wf = self.active_workflows.get(session_id)
if not wf or wf["status"] in ("completed", "rejected"):
return None
if wf["human_approval_needed"]:
return None
step = wf["steps"][wf["current_step"]]
result = self._run_step(session_id, step)
wf["data"][step] = result
step_number = wf["current_step"]
if step == "human_approval":
wf["human_approval_needed"] = True
wf["status"] = "awaiting_approval"
else:
wf["current_step"] += 1
if wf["current_step"] >= len(wf["steps"]):
wf["status"] = "completed"
return {
"step": step,
"label": STEP_LABELS.get(step, step),
"icon": STEP_ICONS.get(step, "📌"),
"step_number": step_number,
"total_steps": len(wf["steps"]),
"result": result,
"status": wf["status"],
"needs_human": wf["human_approval_needed"]
}
def _run_step(self, session_id: str, step_name: str) -> dict:
agent = self.agents.get(step_name)
wf = self.active_workflows[session_id]
context_parts = []
for k, v in wf["data"].items():
if isinstance(v, dict):
output = v.get("output", str(v))
else:
output = str(v)
context_parts.append(f"Step '{k}': {output[:500]}")
context = "\n".join(context_parts)
messages = [{"role": "user", "content": f"Execute workflow step: {step_name}\n\nPrevious context:\n{context}"}]
if agent:
try:
output = agent.run(messages, context="")
return {"output": output, "status": "completed"}
except Exception as e:
return {"output": f"Error: {str(e)}", "status": "error"}
else:
return {"output": f"Auto-completing step '{step_name}'", "status": "auto"}
def approve(self, session_id: str) -> bool:
wf = self.active_workflows.get(session_id)
if wf and wf["human_approval_needed"]:
wf["human_approval_needed"] = False
wf["status"] = "in_progress"
wf["data"]["human_approval"] = {"status": "approved"}
wf["current_step"] += 1
if wf["current_step"] >= len(wf["steps"]):
wf["status"] = "completed"
return True
return False
def reject(self, session_id: str, reason: str = "") -> bool:
wf = self.active_workflows.get(session_id)
if wf and wf["human_approval_needed"]:
wf["human_approval_needed"] = False
wf["status"] = "rejected"
wf["data"]["human_approval"] = {"status": "rejected", "reason": reason}
return True
return False
def can_execute_more(self, session_id: str) -> bool:
wf = self.active_workflows.get(session_id)
if not wf:
return False
return (wf["status"] == "in_progress" and
wf["current_step"] < len(wf["steps"]) and
not wf["human_approval_needed"])
def execute_all_stream(self, session_id: str):
while self.can_execute_more(session_id):
result = self.execute_step(session_id)
if not result:
break
yield f"data: {json.dumps(sanitize_for_json({
'type': 'workflow_step',
'step': result['step'],
'label': result['label'],
'icon': result['icon'],
'step_number': result['step_number'],
'total_steps': result['total_steps'],
'result': result['result'],
'status': result['status']
}))}\n\n"
if result.get("needs_human"):
yield f"data: {json.dumps(sanitize_for_json({
'type': 'approval_request',
'step': result['step'],
'label': result['label'],
'icon': result['icon'],
'workflow_id': session_id,
'reason': 'Human approval required before proceeding',
'data': result['result']
}))}\n\n"
return
status = self.get_status(session_id)
if status and status["status"] == "completed":
yield f"data: {json.dumps({'type': 'workflow_complete', 'workflow_id': session_id})}\n\n"
elif status and status["status"] == "in_progress":
yield f"data: {json.dumps({'type': 'workflow_idle', 'workflow_id': session_id})}\n\n"
workflow_engine = WorkflowEngine()