"""Pipeline orchestrator — builds the graph + middleware chain and executes.""" from __future__ import annotations import logging from app.graph.builder import ReportGraph from app.graph.state import ReportState from app.middleware.chain import MiddlewareChain from app.middleware.client_context import ClientContextMiddleware from app.middleware.token_budget import TokenBudgetMiddleware from app.middleware.compliance import ComplianceMiddleware from app.middleware.memory import MemoryMiddleware logger = logging.getLogger(__name__) def build_middleware() -> MiddlewareChain: """Assemble the middleware chain in execution order.""" chain = MiddlewareChain() chain.add(ClientContextMiddleware()) # 1. load client profile chain.add(MemoryMiddleware()) # 2. inject memory facts chain.add(TokenBudgetMiddleware()) # 3. set token budget chain.add(ComplianceMiddleware()) # 4. scan output for PII (after only) return chain class PipelineOrchestrator: """Top-level entry: creates graph + middleware, runs end-to-end.""" def __init__(self): self.middleware = build_middleware() self.graph = ReportGraph(middleware=self.middleware) async def run(self, state: ReportState) -> ReportState: logger.info(f"[orchestrator] starting report {state.id}") state = await self.graph.run(state) logger.info( f"[orchestrator] finished report {state.id} — " f"status={'OK' if not state.error else 'FAILED'}, " f"files={len(state.generated_files)}" ) return state