mirror of
https://github.com/hoshikawa2/compass_backoffice.git
synced 2026-07-09 13:54:20 +00:00
70 lines
3.0 KiB
Python
70 lines
3.0 KiB
Python
from __future__ import annotations
|
|
|
|
from typing import Any
|
|
|
|
from app.channels.backoffice_rest_adapter import BackofficeChannelEnvelope, BackofficeRestChannelAdapter
|
|
from app.workflows.backoffice_workflow_executor import BackofficeWorkflowExecutor
|
|
|
|
|
|
class BackofficeWorkflowDispatcher:
|
|
"""Framework-facing dispatcher for Backoffice operational workflows.
|
|
|
|
FastAPI routes call this dispatcher with a channel envelope. The dispatcher
|
|
first normalizes the envelope through the framework ChannelGateway and only
|
|
then dispatches the requested LangGraph workflow. This keeps REST as a
|
|
channel adapter concern and prevents routes from coupling directly to a
|
|
domain workflow executor.
|
|
"""
|
|
|
|
def __init__(self, *, channel_gateway: Any, executor: BackofficeWorkflowExecutor, telemetry: Any, adapter: BackofficeRestChannelAdapter | None = None):
|
|
self.channel_gateway = channel_gateway
|
|
self.executor = executor
|
|
self.telemetry = telemetry
|
|
self.adapter = adapter or BackofficeRestChannelAdapter()
|
|
|
|
async def execute(self, envelope: BackofficeChannelEnvelope, *, app_state: Any = None) -> dict[str, Any]:
|
|
msg = await self.adapter.normalize(self.channel_gateway, envelope)
|
|
context = dict(getattr(msg, "context", None) or envelope.gateway_payload())
|
|
metadata = {
|
|
**(envelope.metadata or {}),
|
|
"tenant_id": envelope.tenant_id,
|
|
"agent_id": envelope.agent_id,
|
|
"channel": envelope.channel,
|
|
"normalized_channel": getattr(msg, "channel", envelope.channel),
|
|
"channel_id": getattr(msg, "channel_id", None),
|
|
"message_id": envelope.message_id,
|
|
"user_id": getattr(msg, "user_id", envelope.user_id),
|
|
"business_context": envelope.business_context,
|
|
"channel_context": context,
|
|
"framework_entrypoint": "channel_gateway",
|
|
}
|
|
await self._event("backoffice.channel.normalized", {
|
|
"workflow_id": envelope.workflow_id,
|
|
"transaction_id": envelope.transaction_id,
|
|
"channel": envelope.channel,
|
|
"agent_id": envelope.agent_id,
|
|
"legacy_contract": envelope.metadata.get("legacy_contract"),
|
|
})
|
|
final_state = await self.executor.execute_workflow(
|
|
envelope.workflow_id,
|
|
payload=envelope.payload,
|
|
transaction_id=envelope.transaction_id,
|
|
app_state=app_state,
|
|
metadata=metadata,
|
|
)
|
|
await self._event("backoffice.workflow.dispatched", {
|
|
"workflow_id": envelope.workflow_id,
|
|
"transaction_id": envelope.transaction_id,
|
|
"current_step": str(final_state.get("current_step")),
|
|
"has_error": bool(final_state.get("error")),
|
|
})
|
|
return final_state
|
|
|
|
async def _event(self, name: str, payload: dict[str, Any]) -> None:
|
|
try:
|
|
await self.telemetry.event(name, payload, kind="workflow")
|
|
except TypeError:
|
|
await self.telemetry.event(name, payload)
|
|
except Exception:
|
|
pass
|