""" GraphEngine Manager for sending control commands via Redis channel. This module provides a simplified interface for controlling workflow executions using the new Redis command channel, without requiring user permission checks. Callers must provide a Redis client dependency from outside the workflow package. """ import logging from collections.abc import Sequence from typing import final from dify_graph.graph_engine.command_channels.redis_channel import RedisChannel, RedisClientProtocol from dify_graph.graph_engine.entities.commands import ( AbortCommand, GraphEngineCommand, PauseCommand, UpdateVariablesCommand, VariableUpdate, ) logger = logging.getLogger(__name__) @final class GraphEngineManager: """ Manager for sending control commands to GraphEngine instances. This class provides a simple interface for controlling workflow executions by sending commands through Redis channels, without user validation. """ _redis_client: RedisClientProtocol def __init__(self, redis_client: RedisClientProtocol) -> None: self._redis_client = redis_client def send_stop_command(self, task_id: str, reason: str | None = None) -> None: """ Send a stop command to a running workflow. Args: task_id: The task ID of the workflow to stop reason: Optional reason for stopping (defaults to "User requested stop") """ abort_command = AbortCommand(reason=reason or "User requested stop") self._send_command(task_id, abort_command) def send_pause_command(self, task_id: str, reason: str | None = None) -> None: """Send a pause command to a running workflow.""" pause_command = PauseCommand(reason=reason or "User requested pause") self._send_command(task_id, pause_command) def send_update_variables_command(self, task_id: str, updates: Sequence[VariableUpdate]) -> None: """Send a command to update variables in a running workflow.""" if not updates: return update_command = UpdateVariablesCommand(updates=updates) self._send_command(task_id, update_command) def _send_command(self, task_id: str, command: GraphEngineCommand) -> None: """Send a command to the workflow-specific Redis channel.""" if not task_id: return channel_key = f"workflow:{task_id}:commands" channel = RedisChannel(self._redis_client, channel_key) try: channel.send_command(command) except Exception: # Silently fail if Redis is unavailable # The legacy control mechanisms will still work logger.exception("Failed to send graph engine command %s for task %s", command.__class__.__name__, task_id)