ForcePilot/backend/package/yuxi/channels/pipeline/base.py

80 lines
2.5 KiB
Python
Raw Normal View History

from __future__ import annotations
from collections.abc import Awaitable, Callable
from typing import Any
from yuxi.channels.pipeline.context import PipelineContext
from yuxi.utils.logging_config import logger
PipelineStage = Callable[
["BaseInboundPipeline", PipelineContext],
Awaitable[PipelineContext | None],
]
class BaseInboundPipeline:
def __init__(self, adapter: Any):
self._adapter = adapter
self._stages: list[PipelineStage] = []
async def initialize(self) -> None:
self._stages = await self._build_stages()
logger.info(
f"[Pipeline] {self.__class__.__name__} initialized with "
f"{len(self._stages)} stages: "
f"{[getattr(s, '__name__', s.__class__.__name__) for s in self._stages]}"
)
async def _build_stages(self) -> list[PipelineStage]:
return []
async def process(
self,
event_type: str,
event_data: dict[str, Any],
) -> PipelineContext | None:
ctx = self._build_context(event_type, event_data)
for idx, stage in enumerate(self._stages, start=1):
stage_name = self._resolve_stage_name(stage, idx)
try:
result = await stage(self, ctx)
except Exception as e:
logger.error(
f"[Pipeline::{self.__class__.__name__}] Stage [{idx}] '{stage_name}' raised exception: {e}",
exc_info=True,
)
return None
if result is None:
reason = ctx._stop_reason or f"stage_{idx}:{stage_name}"
logger.debug(
f"[Pipeline] Message dropped at [{idx}] '{stage_name}': {reason} | ctx={ctx.debug_summary()}"
)
return None
ctx = result
if ctx.stopped:
logger.debug(f"[Pipeline] Message stopped at [{idx}] '{stage_name}': {ctx._stop_reason}")
return None
return ctx
def _build_context(self, event_type: str, event_data: dict[str, Any]) -> PipelineContext:
return PipelineContext(event_type=event_type, event_data=event_data)
@staticmethod
def _resolve_stage_name(stage: PipelineStage, idx: int) -> str:
name = getattr(stage, "__name__", "")
if name and not name.startswith("_"):
return name
if hasattr(stage, "__self__"):
return type(stage.__self__).__name__
return f"stage_{idx}"
@property
def adapter(self) -> Any:
return self._adapter