chore: 迁移服务器前同步本地修改

This commit is contained in:
2026-08-05 21:26:13 +08:00
parent aa47a19e98
commit 1a914a2517
73 changed files with 908 additions and 1961 deletions
@@ -169,16 +169,16 @@ def _from_json_bytes(blob: bytes) -> Any:
def build_postgres_persistence(trace_id: str) -> PostgresStatePersistence:
"""生产环境构造 PostgresStatePersistence:从 ray_actor_hook 取 postgres handle。"""
from kilostar.utils.ray_hook import ray_actor_hook
"""生产环境构造 PostgresStatePersistence:取进程内 postgres 服务。"""
from kilostar.utils.actor import get_postgres
postgres_database = ray_actor_hook("postgres_database").postgres_database
postgres_database = get_postgres()
async def _write(tid: str, history: Any) -> None:
await postgres_database.upsert_workflow_graph_state.remote(tid, history)
await postgres_database.upsert_workflow_graph_state(tid, history)
async def _read(tid: str) -> Optional[Any]:
record = await postgres_database.get_workflow_graph_state.remote(tid)
record = await postgres_database.get_workflow_graph_state(tid)
if record is None:
return None
# ORM 模型 / dict / list 都兼容
+43 -51
View File
@@ -36,7 +36,6 @@ import datetime
from dataclasses import dataclass
from typing import Any, Awaitable, Callable, Dict, List, Optional
from kilostar.utils.ray_compat import remote_task, _STANDALONE
from pydantic import BaseModel, Field
from pydantic_graph import BaseNode, End, Graph, GraphRunContext
from pydantic_graph.persistence import BaseStatePersistence
@@ -74,7 +73,7 @@ StepExecutor = Callable[
class WorkflowDeps:
"""节点运行期依赖:所有外部 IO 都从这里走,便于测试 mock。
每个字段都是一个 awaitable,签名贴近原 ``.remote()`` 调用。生产路径由
每个字段都是一个 awaitable,签名贴近底层 actor 方法。生产路径由
``build_default_deps`` 现场组装真实 actor handle 包装;单测可以传任意
``AsyncMock``。
@@ -419,8 +418,8 @@ async def _default_skill_executor(
async def _default_consciousness_executor(
step_data: Dict[str, Any], state: WorkflowGraphState
) -> tuple[str, bool]:
"""生产环境的 consciousness 派发器:远程调用 ConsciousnessNode.working。"""
from kilostar.utils.ray_hook import ray_actor_hook
"""生产环境的 consciousness 派发器:调用 ConsciousnessNode.working。"""
from kilostar.utils.actor import get_consciousness
from kilostar.core.individual.consciousness_node.template import (
ForWorkflow,
ForWorkflowInput,
@@ -428,12 +427,12 @@ async def _default_consciousness_executor(
)
from kilostar.core.work.workflow.workflow import WorkflowStep
consciousness_node = ray_actor_hook("consciousness_node").consciousness_node
consciousness_node = get_consciousness()
payload = ForWorkflowInput(
workflow_step=WorkflowStep.model_validate(step_data),
original_command=state.original_command,
)
result = await consciousness_node.working.remote(payload)
result = await consciousness_node.working(payload)
if isinstance(result, ForWorkflow):
return result.output, True
if isinstance(result, ForregulatoryNode):
@@ -444,32 +443,30 @@ async def _default_consciousness_executor(
def build_default_deps() -> WorkflowDeps:
"""生产环境构造 ``WorkflowDeps``:把 ray actor handle 包装成 awaitable。
"""生产环境构造 ``WorkflowDeps``:把进程内服务包装成 awaitable。
抽出来是为了让 ``run_workflow_task`` 入口和测试入口共享同一套包装逻辑。
"""
from kilostar.utils.ray_hook import ray_actor_hook
from kilostar.utils.actor import get_postgres, get_gwm
postgres_database = ray_actor_hook("postgres_database").postgres_database
global_workflow_manager = ray_actor_hook(
"global_workflow_manager"
).global_workflow_manager
postgres_database = get_postgres()
global_workflow_manager = get_gwm()
async def _upsert_workflow_context(trace_id: str, **kwargs: Any) -> Any:
return await postgres_database.upsert_workflow_context.remote(
return await postgres_database.upsert_workflow_context(
trace_id, **kwargs
)
async def _update_workflow_status(trace_id: str, status: str) -> Any:
return await postgres_database.update_workflow_status.remote(
return await postgres_database.update_workflow_status(
trace_id, status
)
async def _put_pending(trace_id: str, message: str) -> Any:
return await global_workflow_manager.put_pending.remote(trace_id, message)
return await global_workflow_manager.put_pending(trace_id, message)
async def _get_received(trace_id: str) -> str:
return await global_workflow_manager.get_received.remote(trace_id)
return await global_workflow_manager.get_received(trace_id)
return WorkflowDeps(
upsert_workflow_context=_upsert_workflow_context,
@@ -547,11 +544,13 @@ async def resume_workflow_graph(
return final_output
@remote_task
def run_workflow_task(
async def run_workflow_task(
workflow_data: dict, trace_id: str, resume_only: bool = False
):
"""workflow 的 ray task 入口:一次性执行,跑完即销毁。
) -> None:
"""workflow 的后台任务入口:一次性执行,跑完即结束。
调用方用 ``asyncio.create_task(run_workflow_task(...))`` 以 fire-and-forget
方式在当前事件循环里后台执行。
生产路径下持久化交给 ``PostgresStatePersistence`` —— 即便进程崩溃,再 fire
一次相同 ``trace_id`` 的任务(或调 ``/workflow/{trace_id}/resume``)即可
@@ -564,8 +563,7 @@ def run_workflow_task(
否则会拿着空 ``workflow_data`` 空跑一个 ``work_link=[]`` 的 workflow 并误判
为 COMPLETED(静默 bug)。
ray task 是新进程,contextvars 不会从 caller 传过来,所以入口先 bind 一次
``trace_id``,让节点内的日志自动带上它。
入口先 bind 一次 ``trace_id``,让节点内的日志自动带上它。
"""
from kilostar.utils.request_context import trace_id_scope
from kilostar.core.work.workflow.graph_persistence import (
@@ -575,35 +573,29 @@ def run_workflow_task(
_logger = get_logger("workflow_task")
async def _entry() -> None:
with trace_id_scope(trace_id):
persistence = build_postgres_persistence(trace_id)
persistence.set_graph_types(workflow_graph)
with trace_id_scope(trace_id):
persistence = build_postgres_persistence(trace_id)
persistence.set_graph_types(workflow_graph)
recovered = False
try:
recovered = await persistence.hydrate()
except Exception as e:
if resume_only:
_logger.error(f"resume 失败:无法 hydrate 图持久化记录: {e}")
raise
recovered = False
try:
recovered = await persistence.hydrate()
except Exception as e:
if resume_only:
_logger.error(f"resume 失败:无法 hydrate 图持久化记录: {e}")
raise
recovered = False
if resume_only and not recovered:
msg = (
f"resume 失败:trace {trace_id} 没有可恢复的图持久化记录,"
"拒绝以全新模式空跑"
)
_logger.error(msg)
raise RuntimeError(msg)
if resume_only and not recovered:
msg = (
f"resume 失败:trace {trace_id} 没有可恢复的图持久化记录,"
"拒绝以全新模式空跑"
)
_logger.error(msg)
raise RuntimeError(msg)
if recovered:
await resume_workflow_graph(trace_id, persistence=persistence)
else:
await run_workflow_graph(
workflow_data, trace_id, persistence=persistence
)
if _STANDALONE:
return _entry()
else:
asyncio.run(_entry())
if recovered:
await resume_workflow_graph(trace_id, persistence=persistence)
else:
await run_workflow_graph(
workflow_data, trace_id, persistence=persistence
)