refactor(core): decouple actors and remove workflow templates (#67)

Removes the deprecated `workflow_template` concept entirely across both backend API routers, internal logic handling within the `supervisory_node` and `consciousness_node`, and front-end components. Enables `consciousness_node` to work autonomously.

Also refactors core package structure to enforce the "one python package, one Ray Actor" architectural rule. `GlobalWorkflowManager`, `WorkflowRunningEngine`, `PostgresDatabase`, and `WorkerCluster` have been moved to their own top-level decoupled package directories with properly exported `__init__.py` modules. Test suites have been relocated and import paths updated across the system.

Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
Co-authored-by: zhaoxi826 <198742034+zhaoxi826@users.noreply.github.com>
This commit is contained in:
2026-05-06 15:05:47 +08:00
committed by GitHub
parent b3ea4cd8d9
commit 209ba45477
97 changed files with 1872 additions and 1498 deletions
+43 -22
View File
@@ -16,69 +16,88 @@ from typing import List, Optional, Union, Literal, Dict, Any
from pydantic import BaseModel, Field, model_validator
from pretor.utils.logger import get_logger
logger = get_logger('workflow')
logger = get_logger("workflow")
NodeType = Literal[
"consciousness_node", "control_node", "supervisory_node", "skill_individual"
]
class EventInfo(BaseModel):
"""EventInfo 核心组件类。
这是一个领域数据模型或功能封装类,承载了 EventInfo 相关的内聚属性定义与状态维护。它的存在隔离了局部的业务复杂性,并对外提供了类型安全的访问接口。 """
这是一个领域数据模型或功能封装类,承载了 EventInfo 相关的内聚属性定义与状态维护。它的存在隔离了局部的业务复杂性,并对外提供了类型安全的访问接口。"""
platform: str
user_name: str
class LogicGate(BaseModel):
"""LogicGate 核心组件类。
这是一个领域数据模型或功能封装类,承载了 LogicGate 相关的内聚属性定义与状态维护。它的存在隔离了局部的业务复杂性,并对外提供了类型安全的访问接口。 """
这是一个领域数据模型或功能封装类,承载了 LogicGate 相关的内聚属性定义与状态维护。它的存在隔离了局部的业务复杂性,并对外提供了类型安全的访问接口。"""
if_fail: str = Field(..., description="失败跳转目标,如 'jump_to_step_1'")
if_pass: Literal["continue", "exit"] = Field(default="continue", description="成功后的动作")
if_pass: Literal["continue", "exit"] = Field(
default="continue", description="成功后的动作"
)
class WorkStep(BaseModel):
"""WorkStep 核心组件类。
这是一个领域数据模型或功能封装类,承载了 WorkStep 相关的内聚属性定义与状态维护。它的存在隔离了局部的业务复杂性,并对外提供了类型安全的访问接口。 """
这是一个领域数据模型或功能封装类,承载了 WorkStep 相关的内聚属性定义与状态维护。它的存在隔离了局部的业务复杂性,并对外提供了类型安全的访问接口。"""
step: int = Field(..., gt=0, description="步骤序号,严格自增")
name: str = Field(..., description="步骤名称")
node: NodeType = Field(..., description="负责执行的节点类型")
action: str = Field(..., description="执行的原子动作")
desc: str = Field(..., description="动作细节的自然语言描述,包含人工规范指导")
inputs: Optional[Union[str, List[str]]] = Field(default=None, description="前置依赖输出")
inputs: Optional[Union[str, List[str]]] = Field(
default=None, description="前置依赖输出"
)
outputs: Optional[str] = Field(default=None, description="当前步骤产出物变量名")
agent_id: Optional[str] = Field(default=None, description="分配给 skill_individual 的 Skill Individual 真实 agent_id,不可用名称代替")
agent_id: Optional[str] = Field(
default=None,
description="分配给 skill_individual 的 Skill Individual 真实 agent_id,不可用名称代替",
)
logic_gate: Optional[LogicGate] = Field(default=None, description="逻辑跳转控制")
status: Literal["waiting", "running", "completed", "failed"] = Field(
default="waiting",
description="执行状态 (LLM建议保留默认值)"
default="waiting", description="执行状态 (LLM建议保留默认值)"
)
class WorkflowStatus(BaseModel):
"""WorkflowStatus 核心组件类。
这是一个领域数据模型或功能封装类,承载了 WorkflowStatus 相关的内聚属性定义与状态维护。它的存在隔离了局部的业务复杂性,并对外提供了类型安全的访问接口。 """
这是一个领域数据模型或功能封装类,承载了 WorkflowStatus 相关的内聚属性定义与状态维护。它的存在隔离了局部的业务复杂性,并对外提供了类型安全的访问接口。"""
step: int = Field(default=1, gt=0, description="当前运行到的工作流步数")
status: Literal["waiting_llm_working", "waiting_tool_working", "llm_working", "tool_working"] = Field(
default="waiting_llm_working",
description="当前系统调度状态"
)
status: Literal[
"waiting_llm_working", "waiting_tool_working", "llm_working", "tool_working"
] = Field(default="waiting_llm_working", description="当前系统调度状态")
class PretorWorkflow(BaseModel):
"""PretorWorkflow 核心组件类。
这是一个领域数据模型或功能封装类,承载了 PretorWorkflow 相关的内聚属性定义与状态维护。它的存在隔离了局部的业务复杂性,并对外提供了类型安全的访问接口。 """
这是一个领域数据模型或功能封装类,承载了 PretorWorkflow 相关的内聚属性定义与状态维护。它的存在隔离了局部的业务复杂性,并对外提供了类型安全的访问接口。"""
title: str = Field(..., description="工作流的标题")
work_link: List[WorkStep] = Field(..., description="工作链逻辑定义")
# ---------------- 以下为系统级管控字段,LLM 无需关心 ---------------- #
trace_id: str | None = Field(description="系统自动生成的追溯ID")
version: str = Field(default="v1.0", description="系统协议版本号")
command: Optional[str] = Field(default=None, description="触发此工作流的原始命令")
output: Dict[str, Any] = Field(default_factory=dict, description="工作流最终产出结果")
status: WorkflowStatus = Field(default_factory=WorkflowStatus, description="运行时状态对象")
output: Dict[str, Any] = Field(
default_factory=dict, description="工作流最终产出结果"
)
status: WorkflowStatus = Field(
default_factory=WorkflowStatus, description="运行时状态对象"
)
event_info: EventInfo | None = Field(default=None)
context_memory: Dict[str, Any] = Field(default_factory=dict)
@model_validator(mode='after')
def validate_workflow_integrity(self) -> 'PretorWorkflow':
@model_validator(mode="after")
def validate_workflow_integrity(self) -> "PretorWorkflow":
"""执行与 validate workflow integrity 相关的核心业务流转操作。
该方法封装了具体的算法策略或状态控制逻辑,确保操作能够在事务上下文中被原子且一致地执行。
Returns: ('PretorWorkflow'): 经由当前业务模型加工处理后所输出的具体数据实例或领域模型对象。 """
Returns: ('PretorWorkflow'): 经由当前业务模型加工处理后所输出的具体数据实例或领域模型对象。"""
steps = [s.step for s in self.work_link]
expected = list(range(1, len(steps) + 1))
if steps != expected:
@@ -90,9 +109,11 @@ class PretorWorkflow(BaseModel):
try:
target = int(s.logic_gate.if_fail.split("_")[-1])
if target > max_step or target < 1:
raise ValueError(f"Step {s.step} 的跳转目标 Step {target} 越界了!")
raise ValueError(
f"Step {s.step} 的跳转目标 Step {target} 越界了!"
)
except ValueError as e:
if "越界" in str(e):
raise e
raise ValueError(f"LogicGate 格式错误: {s.logic_gate.if_fail}")
return self
return self
-393
View File
@@ -1,393 +0,0 @@
# Copyright 2026 zhaoxi826
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from pretor.utils.ray_hook import ray_actor_hook
import ray
import asyncio
from pretor.core.workflow.workflow import PretorWorkflow, WorkStep, EventInfo
from typing import Optional, Dict, Union, Any, List
from pretor.utils.error import WorkflowError, WorkflowExit
from pretor.api.platform.event import PretorEvent
from pretor.core.individual.control_node.template import ForWorkflowInput as ControlForWorkflowInput, \
ForWorkflow as ControlForWorkflow
from pretor.core.individual.consciousness_node.template import (
ForWorkflowInput as ConsciousnessForWorkflowInput,
ForSupervisoryInput,
ForSupervisoryNode,
ForWorkflow as ConsciousnessForWorkflow,
ForWorkflowEngineInput,
ForWorkflowEngine
)
from pretor.core.individual.supervisory_node.template import TerminationMessage
import pathlib
def get_workflow_template(workflow_name: str) -> str:
"""检索并获取特定的 workflow template 数据集合或实例对象。
根据提供的查询条件或上下文凭证,从数据库、缓存或第三方服务中读取对应的资源状态。
Args: workflow_name (str): 赋予该实体的人类可读名称或标题字符串,主要用于前端 UI 展示、日志记录或模糊检索。
Returns: (str): 处理流程所输出的具体字符串产物,可能是新生成的 ID 序列、格式化好的文本片段或 LLM 推理的回答内容。 """
workflow_template = pathlib.Path(__file__).parent.parent.parent / "workflow_template" / (workflow_name + "_workflow_template.json")
with open(workflow_template, "r", encoding="utf-8") as workflow_template_file:
workflow_template = workflow_template_file.read()
return workflow_template
class WorkflowEngine:
"""WorkflowEngine 核心组件类。
这是一个领域数据模型或功能封装类,承载了 WorkflowEngine 相关的内聚属性定义与状态维护。它的存在隔离了局部的业务复杂性,并对外提供了类型安全的访问接口。 """
def __init__(self,
workflow: PretorWorkflow,
consciousness_node=None,
control_node=None,
supervisory_node=None):
from pretor.utils.logger import get_logger
self.logger = get_logger('workflow_runner')
self.workflow: PretorWorkflow = workflow
"""工作流:当前WorkflowEngine待执行的workflow"""
self._steps_by_id: Dict[int, WorkStep] = {step.step: step for step in self.workflow.work_link}
"""步骤表:将当前workflow的步骤序号和步骤内容存放"""
self.consciousness_node = consciousness_node
"""意识节点"""
self.control_node = control_node
"""控制节点"""
self.supervisory_node = supervisory_node
"""监督节点"""
self._gwm = ray_actor_hook("global_workflow_manager").global_workflow_manager
async def _push_sse(self, msg: str) -> None:
"""执行与 push sse 相关的核心业务流转操作。
该方法封装了具体的算法策略或状态控制逻辑,确保操作能够在事务上下文中被原子且一致地执行。
Args: msg (str): 控制逻辑流向的具体字符串参数,指定了期望的 msg 内容。
Returns: (None): 经由当前业务模型加工处理后所输出的具体数据实例或领域模型对象。 """
try:
await self._gwm.put_pending.remote(self.workflow.trace_id, msg)
except Exception:
pass
def _prepare_inputs(self, inputs: Optional[Union[str, List[str]]]) -> Any:
"""
准备输入的方法
Args:
inputs: 待输入的名称
Returns:
"""
match inputs:
case None:
return None
case str(name):
return self.workflow.context_memory.get(name)
case list(names):
return {k: self.workflow.context_memory.get(k) for k in names}
async def run(self):
"""
run方法
处理并执行workflow的方法
"""
self.logger.info(f"🚀 工作流引擎启动: {self.workflow.title} [Trace ID: {self.workflow.trace_id}]")
await self._push_sse(f"[工作流启动] {self.workflow.title}")
max_step = len(self.workflow.work_link)
while 1 <= self.workflow.status.step <= max_step:
current_step_id = self.workflow.status.step
current_step = self._steps_by_id.get(current_step_id)
if not current_step:
self.logger.error(f"严重错误:找不到步骤 {current_step_id},工作流强制终止。")
self.workflow.status.status = "failed"
await self._push_sse(f"[工作流失败] 找不到步骤 {current_step_id}")
break
self.logger.info(f"▶️ 开始执行 Step {current_step_id}: [{current_step.node}] -> {current_step.action}")
current_step.status = "running"
await self._push_sse(f"[Step {current_step_id}] {current_step.name}: {current_step.desc}")
try:
step_input_data = self._prepare_inputs(current_step.inputs)
step_result, is_success = await self._dispatch_to_node(current_step, step_input_data)
if is_success:
if current_step.outputs:
self.workflow.context_memory[current_step.outputs] = step_result
self.logger.debug(f"Step {current_step_id} 产出已保存至变量: '{current_step.outputs}'")
current_step.status = "completed"
await self._push_sse(f"[Step {current_step_id} 完成] {current_step.name}")
else:
self.logger.warning(f"Step {current_step_id} 执行遇到业务失败/驳回。")
current_step.status = "failed"
await self._push_sse(f"[Step {current_step_id} 失败] {current_step.name}")
self._handle_logic_gate(current_step, is_success)
except WorkflowExit:
self.logger.info("命中 if_pass='exit',工作流被主动要求结束。")
await self._push_sse("[工作流结束] 主动退出")
break
except WorkflowError as e:
self.logger.error(f"{e},终止工作流。")
self.workflow.status.status = "failed"
await self._push_sse(f"[工作流失败] {e}")
break
except Exception as e:
self.logger.error(f"❌ Step {current_step_id} 发生系统级未捕获异常: {e}", exc_info=True)
current_step.status = "failed"
self.workflow.status.status = "failed"
await self._push_sse(f"[工作流异常] {e}")
break
self.logger.info(f"✅ 工作流 {self.workflow.title} 执行步骤结束。")
self.workflow.output = self.workflow.context_memory
await self._push_sse(f"[工作流完成] {self.workflow.title}")
await self._report_results()
async def _report_results(self):
"""
结果汇报函数
在工作流结束后执行
Returns:
"""
if self.workflow.status.status == "failed":
self.logger.warning("工作流执行失败,跳过正常汇报流程。")
return
try:
self.logger.info("开始生成工作流结束技术报告...")
report = ""
if self.consciousness_node:
supervisory_input = ForSupervisoryInput(
workflow=self.workflow,
original_command=self.workflow.command or "未知命令"
)
report_obj = await self.consciousness_node.working.remote(supervisory_input)
if isinstance(report_obj, ForSupervisoryNode):
report = report_obj.output
elif isinstance(report_obj, str):
report = report_obj
self.logger.debug(f"生成的报告摘要: {report[:100]}...")
else:
self.logger.warning("未提供 consciousness_node 句柄,跳过报告生成。")
if self.supervisory_node:
term_msg = TerminationMessage(
platform=self.workflow.event_info.platform,
user_name=self.workflow.event_info.user_name,
message=f"工作流执行完毕。系统报告:{report}"
)
user_response = await self.supervisory_node.working.remote(term_msg)
self.workflow.context_memory["_final_user_response"] = user_response
self.logger.info(f"Supervisory 最终回复:{user_response}")
else:
self.logger.warning("未提供 supervisory_node 句柄,跳过用户反馈生成。")
except Exception:
self.logger.exception("生成工作流执行汇报时发生错误")
async def _dispatch_to_node(self, step: WorkStep, input_data: Any) -> tuple[Any, bool]:
"""
分流器
调用当前step的执行对象
Args:
step: WorkStep对象,当前需要执行的step
input_data: 输入数据
Returns:
返回llm的输出和一个bool类型的判断
"""
self.logger.debug(f"正在向 {step.node} 节点发送动作 {step.action}...")
try:
if step.node == "control_node":
if not self.control_node:
raise WorkflowError("未提供 control_node 句柄!")
payload = ControlForWorkflowInput(workflow_step=step)
# 可选:如果 input_data 需要合并,可以扩展 ControlForWorkflowInput 或将其放在 context_memory
result_obj = await self.control_node.working.remote(payload)
if isinstance(result_obj, ControlForWorkflow):
return result_obj.output, True
return result_obj, True
elif step.node == "consciousness_node":
if not self.consciousness_node:
raise WorkflowError("未提供 consciousness_node 句柄!")
original_cmd = self.workflow.command or ""
payload = ConsciousnessForWorkflowInput(
workflow_step=step,
original_command=original_cmd
)
result_obj = await self.consciousness_node.working.remote(payload)
if isinstance(result_obj, ConsciousnessForWorkflow):
return result_obj.output, True
return result_obj, True
elif step.node == "skill_individual":
self.logger.info(f"正在通过 WorkerCluster 调度 skill_individual 执行 {step.action}")
try:
from pretor.utils.ray_hook import ray_actor_hook
worker_cluster = ray_actor_hook("worker_cluster").worker_cluster
task_id = f"{self.workflow.trace_id}_step_{step.step}"
agent_id = step.agent_id or f"default_{step.node}"
task_event = {
"action": step.action,
"description": step.desc,
"input_data": input_data,
"context_memory": self.workflow.context_memory
}
result_response = await worker_cluster.submit_task.remote(task_id, agent_id, task_event)
if result_response.get("success"):
return result_response.get("data"), True
else:
self.logger.error(f"WorkerCluster 执行 {step.node} 失败: {result_response.get('error')}")
return result_response.get("error"), False
except Exception as e:
self.logger.exception(f"调度 WorkerCluster 执行 {step.node} 时发生异常: {e}")
raise WorkflowError(f"WorkerCluster 调度异常: {e}")
else:
raise WorkflowError(f"未知的节点类型:{step.node}")
except Exception:
self.logger.exception(f"节点 {step.node} 执行动作 {step.action} 失败")
return None, False
def _handle_logic_gate(self, step: WorkStep, is_success: bool):
"""
状态机,检测任务执行情况
Args:
step: WorkStep对象,当前执行的step
is_success: bool类型,当前步骤是否成功
"""
gate = step.logic_gate
if is_success:
if gate and gate.if_pass == "exit":
raise WorkflowExit()
self.workflow.status.step += 1
else:
if not gate or not gate.if_fail:
raise WorkflowError(f"步骤 {step.step} 失败且未配置 if_fail 兜底方案")
match gate.if_fail.split("_"):
case ["jump", "to", "step", target] if target.isdigit():
target_step = int(target)
self.logger.warning(f"触发逻辑门分支!从 Step {step.step} 跳转至 Step {target_step}")
self.workflow.status.step = target_step
case _:
raise WorkflowError(f"未知的 if_fail 格式: {gate.if_fail}")
@ray.remote
class WorkflowRunningEngine:
"""WorkflowRunningEngine 核心组件类。
这是一个领域数据模型或功能封装类,承载了 WorkflowRunningEngine 相关的内聚属性定义与状态维护。它的存在隔离了局部的业务复杂性,并对外提供了类型安全的访问接口。 """
def __init__(self, consciousness_node=None, control_node=None, supervisory_node=None):
from pretor.utils.logger import get_logger
self.logger = get_logger('workflow_runner')
self.runner_engine = {}
self.workflow_queue: asyncio.Queue[PretorEvent] = None
self.consciousness_node = consciousness_node
self.control_node = control_node
self.supervisory_node = supervisory_node
self.global_state_machine = None
async def run(self):
# Move actor hook to async start so we don't race during __init__ across cluster
"""执行与 run 相关的核心业务流转操作。
该方法封装了具体的算法策略或状态控制逻辑,确保操作能够在事务上下文中被原子且一致地执行。 """
self.global_state_machine = ray_actor_hook("global_state_machine").global_state_machine
self.workflow_queue = asyncio.Queue()
self.runner_engine = {
f"runner_{i}": asyncio.create_task(self.runner(i))
for i in range(10)
}
async def put_event(self, event: PretorEvent) -> None:
"""执行与 put event 相关的核心业务流转操作。
该方法封装了具体的算法策略或状态控制逻辑,确保操作能够在事务上下文中被原子且一致地执行。
Args: event (PretorEvent): 由事件总线或工作流引擎分发过来的事件载荷,封装了触发此次调用的上下文快照与任务目标指令。
Returns: (None): 经由当前业务模型加工处理后所输出的具体数据实例或领域模型对象。 """
await self.workflow_queue.put(event)
async def resume_workflow(self, event: PretorEvent) -> None:
"""Resume an incomplete workflow that was loaded from the database."""
self.logger.info(f"Resuming workflow {event.trace_id}")
workflow_engine = WorkflowEngine(event.workflow,
self.consciousness_node,
self.control_node,
self.supervisory_node)
# Assuming you want to schedule it via a task
asyncio.create_task(workflow_engine.run())
async def runner(self, i: int) -> None:
"""
runner方法,从self.workflow_queue中不断取出任务并执行
Args:
i: runner序列号
"""
while True:
try:
event = await self.workflow_queue.get()
self.logger.info(f"WorkflowRunningEngine: runner_{i} 接收到事件 {event.trace_id} 准备生成工作流。")
if not self.consciousness_node:
raise WorkflowError("未配置 consciousness_node,无法生成工作流")
workflow_template_name = event.context.get("workflow_template", "")
workflow_template = get_workflow_template(workflow_template_name) if workflow_template_name else None
available_skills = None
if self.global_state_machine:
try:
all_individuals = await self.global_state_machine.list_individuals.remote()
available_skills = []
for agent_id, config in all_individuals.items():
if config.get("agent_type") == "skill_individual" or config.get("type") == "skill_individual":
available_skills.append({
"agent_id": agent_id,
"name": config.get("agent_name", "Unknown"),
"description": config.get("description", "")
})
except Exception as e:
self.logger.warning(f"获取Skill Individual列表失败: {e}")
payload = ForWorkflowEngineInput(
original_command=event.message,
workflow_template=workflow_template,
available_skills=available_skills
)
result_obj = await self.consciousness_node.working.remote(payload)
if isinstance(result_obj, ForWorkflowEngine):
workflow = result_obj.workflow
workflow.trace_id = event.trace_id
workflow.command = event.message
workflow.event_info = EventInfo(platform=event.platform,
user_name=event.user_name,)
self.logger.info(
f"WorkflowRunningEngine: runner_{i} 成功生成工作流 {workflow.trace_id}:{workflow.title}")
global_workflow_manager = ray_actor_hook("global_workflow_manager").global_workflow_manager
await global_workflow_manager.update_workflow.remote(event.trace_id, workflow)
workflow_engine = WorkflowEngine(workflow,
self.consciousness_node,
self.control_node,
self.supervisory_node)
await workflow_engine.run()
else:
self.logger.error(f"WorkflowRunningEngine: runner_{i} 无法生成工作流,返回类型为 {type(result_obj)}")
except asyncio.CancelledError:
self.logger.info(f"WorkflowRunningEngine: runner_{i} 被取消。")
raise
except Exception as e:
self.logger.error(f"WorkflowRunningEngine: runner_{i} 遇到未捕获的异常: {e}", exc_info=True)
@@ -1,14 +0,0 @@
# Copyright 2026 zhaoxi826
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
@@ -1,46 +0,0 @@
# Copyright 2026 zhaoxi826
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from pydantic import BaseModel, model_validator
from typing import Dict,List
class WorkflowTemplateStep(BaseModel):
"""WorkflowTemplateStep 核心组件类。
这是一个领域数据模型或功能封装类,承载了 WorkflowTemplateStep 相关的内聚属性定义与状态维护。它的存在隔离了局部的业务复杂性,并对外提供了类型安全的访问接口。 """
step: int
node: str
action: str
desc: str
input: List[str]
output: List[str]
logic_gate: Dict[str, str]
class WorkflowTemplate(BaseModel):
"""WorkflowTemplate 核心组件类。
这是一个领域数据模型或功能封装类,承载了 WorkflowTemplate 相关的内聚属性定义与状态维护。它的存在隔离了局部的业务复杂性,并对外提供了类型安全的访问接口。 """
name: str
desc: str
work_link: list[WorkflowTemplateStep]
@model_validator(mode='after')
def validate_steps(self) -> 'WorkflowTemplate':
"""执行与 validate steps 相关的核心业务流转操作。
该方法封装了具体的算法策略或状态控制逻辑,确保操作能够在事务上下文中被原子且一致地执行。
Returns: ('WorkflowTemplate'): 经由当前业务模型加工处理后所输出的具体数据实例或领域模型对象。 """
steps = [s.step for s in self.work_link]
if len(steps) != len(set(steps)):
raise ValueError("Step numbers in work_link must be unique")
return self
@@ -1,32 +0,0 @@
# Copyright 2026 zhaoxi826
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from pathlib import Path
from pretor.core.workflow.workflow_template_generator.workflow_template import WorkflowTemplate
class WorkflowTemplateGenerator:
"""WorkflowTemplateGenerator 核心组件类。
这是一个领域数据模型或功能封装类,承载了 WorkflowTemplateGenerator 相关的内聚属性定义与状态维护。它的存在隔离了局部的业务复杂性,并对外提供了类型安全的访问接口。 """
@staticmethod
def generate_workflow_template(workflow_template: WorkflowTemplate) -> WorkflowTemplate:
"""执行与 generate workflow template 相关的核心业务流转操作。
该方法封装了具体的算法策略或状态控制逻辑,确保操作能够在事务上下文中被原子且一致地执行。
Args: workflow_template (WorkflowTemplate): 参与 generate workflow template 逻辑运算或数据构建的上下文依赖对象。
Returns: (WorkflowTemplate): 经由当前业务模型加工处理后所输出的具体数据实例或领域模型对象。 """
output_dir = Path("pretor") / "workflow_template"
if not output_dir.exists():
output_dir.mkdir(parents=True)
output_file = output_dir / f"{workflow_template.name}_workflow_template.json"
with output_file.open("w", encoding="utf-8") as f:
f.write(workflow_template.model_dump_json(indent=4))
@@ -1,76 +0,0 @@
# Copyright 2026 zhaoxi826
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import json
from pretor.core.workflow.workflow_template_generator.workflow_template_generator import WorkflowTemplateGenerator
from pathlib import Path
from pretor.core.workflow.workflow_template_generator.workflow_template import WorkflowTemplate
from pretor.utils.logger import get_logger
logger = get_logger('workflow_template_manager')
class WorkflowManager:
"""WorkflowManager 核心组件类。
这是一个管理器类,职责集中在维护整个系统内有关 Workflow 资源的全局生命周期。它提供了注册机制、状态同步以及跨组件的统一查询入口,确保系统中该类型资源的实例一致性与可控性。 """
def __init__(self):
self.workflow_template_generator = WorkflowTemplateGenerator()
self.workflow_templates_registry = {}
self.template_path = Path("pretor/workflow_template")
self._load_workflow_template()
def _load_workflow_template(self) -> None:
"""执行与 load workflow template 相关的核心业务流转操作。
该方法封装了具体的算法策略或状态控制逻辑,确保操作能够在事务上下文中被原子且一致地执行。
Returns: (None): 经由当前业务模型加工处理后所输出的具体数据实例或领域模型对象。 """
for workflow_template_file in self.template_path.glob("*_workflow_template.json"):
with workflow_template_file.open("r",encoding="utf-8") as f:
try:
workflow_template = json.load(f)
self.workflow_templates_registry[workflow_template.get("name")] = workflow_template.get("desc")
except json.decoder.JSONDecodeError:
logger.warning(f"{workflow_template_file}不是json文件或格式错误")
except KeyError:
logger.warning(f"{workflow_template_file}不符合workflow_template格式")
def generate_workflow_template(self, workflow_template: WorkflowTemplate) -> None:
"""执行与 generate workflow template 相关的核心业务流转操作。
该方法封装了具体的算法策略或状态控制逻辑,确保操作能够在事务上下文中被原子且一致地执行。
Args: workflow_template (WorkflowTemplate): 参与 generate workflow template 逻辑运算或数据构建的上下文依赖对象。
Returns: (None): 经由当前业务模型加工处理后所输出的具体数据实例或领域模型对象。 """
try:
workflow_template = self.workflow_template_generator.generate_workflow_template(workflow_template=workflow_template)
self.workflow_templates_registry[workflow_template.name] = workflow_template.desc
except Exception:
logger.exception("Failed to generate workflow template")
def add_workflow_template(self, template_name: str, workflow_template: WorkflowTemplate) -> None:
"""创建并持久化新的 workflow template 实体。
接收构建参数,执行必要的数据校验与默认值填充后,将新记录安全地写入底层存储或系统注册表中。
Args: template_name (str): 赋予该实体的人类可读名称或标题字符串,主要用于前端 UI 展示、日志记录或模糊检索。 workflow_template (WorkflowTemplate): 参与 add workflow template 逻辑运算或数据构建的上下文依赖对象。
Returns: (None): 经由当前业务模型加工处理后所输出的具体数据实例或领域模型对象。 """
self.generate_workflow_template(workflow_template)
def get_all_workflow_templates(self) -> dict:
"""检索并获取特定的 all workflow templates 数据集合或实例对象。
根据提供的查询条件或上下文凭证,从数据库、缓存或第三方服务中读取对应的资源状态。
Returns: (dict): 高度聚合的字典结构数据,将多维度的属性特征或统计指标组合后一并返回。 """
return self.workflow_templates_registry
def delete_workflow_template(self, template_name: str) -> None:
"""安全地移除或注销 workflow template。
执行物理删除或逻辑删除操作,并妥善清理相关的关联数据及占用资源。
Args: template_name (str): 赋予该实体的人类可读名称或标题字符串,主要用于前端 UI 展示、日志记录或模糊检索。
Returns: (None): 经由当前业务模型加工处理后所输出的具体数据实例或领域模型对象。 """
if template_name in self.workflow_templates_registry:
del self.workflow_templates_registry[template_name]