Files
my-v0-server/app/processor/base.py
T
2026-10-01 09:25:01 -07:00

68 lines
2.3 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import inspect
import time
from abc import ABC, abstractmethod
from contextlib import contextmanager
from typing import TypeVar
from langgraph.errors import GraphInterrupt
from loguru import logger
from app.config import Settings, settings
from app.processor.progress import node_progress
T = TypeVar("T")
class BaseNode(ABC):
name: str = "base_node"
def __init__(self, config: Settings | None = None):
self.config = config if config is not None else settings
self.logger = logger.bind(node=self.name)
@contextmanager
def _instrument(self, state):
"""统一记录日志、耗时和进度事件,异常保持原类型向上抛出"""
start_time = time.perf_counter()
self.logger.info("节点开始")
try:
with node_progress(self.name, state):
yield
except GraphInterrupt:
raise
except Exception:
self.logger.error("节点执行失败")
raise
else:
self.logger.info("节点完成")
finally:
self.logger.info(f"节点耗时 {time.perf_counter() - start_time:.3f} 秒")
def __call__(self, state: T) -> T:
# 异步节点直接调用时返回协程,日志与进度在 _acall 内等待完成后记录
if inspect.iscoroutinefunction(self.process):
return self._acall(state)
with self._instrument(state):
return self.process(state)
async def _acall(self, state: T) -> T:
"""异步节点入口,由图根据 process 是否为协程选择"""
with self._instrument(state):
return await self.process(state)
@abstractmethod
def process(self, state: T) -> T:
"""子类实现节点核心逻辑,返回状态更新"""
def fail_on_invalid_output(self, state: T, error: ValueError, reason: str) -> T:
"""模型输出未通过校验时收尾为失败结果,原因交给失败收尾由模型向用户说明"""
self.logger.warning(f"{reason}: {error}")
return {**state, "status": "failed", "failure_reason": f"{reason}:{error}"}
def log_step(self, step_name: str, message: str = ""):
"""记录步骤日志。"""
log_msg = f"[{step_name}]"
if message:
log_msg += f" {message}"
self.logger.info(log_msg)