- Introduce WorkflowAgentStage to manage agent stage configurations and enhance interaction with the workflow engine. - Implement WorkflowEdgeEvaluator for priority-aware edge evaluation, improving routing decisions based on conditions and user turns. - Update WorkflowBrain to handle user turns and routing more effectively, ensuring agents cannot have only one default path. - Enhance CallEndCoordinator to track speech events and manage call termination based on queued speech. - Add new models and output handling for workflow interactions, improving clarity and maintainability. - Update tests to validate the new routing logic and agent behavior under various scenarios.
93 lines
2.3 KiB
Python
93 lines
2.3 KiB
Python
"""Readable runtime values shared by Workflow orchestration modules."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from dataclasses import dataclass
|
|
from enum import StrEnum
|
|
from typing import Any
|
|
|
|
|
|
class WorkflowStatus(StrEnum):
|
|
"""The small set of states useful to operators and future debug tooling."""
|
|
|
|
STARTING = "starting"
|
|
WAITING_USER = "waiting_user"
|
|
ROUTING = "routing"
|
|
RUNNING_AGENT = "running_agent"
|
|
RUNNING_ACTION = "running_action"
|
|
HANDOFF = "handoff"
|
|
ENDED = "ended"
|
|
|
|
|
|
class RouteStatus(StrEnum):
|
|
"""A routing error is deliberately different from a valid no-match."""
|
|
|
|
MATCHED = "matched"
|
|
NO_MATCH = "no_match"
|
|
ERROR = "error"
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class UserTurn:
|
|
"""One committed user turn that may cross automatic Workflow nodes."""
|
|
|
|
id: int
|
|
text: str
|
|
|
|
|
|
@dataclass
|
|
class WorkflowRuntimeState:
|
|
"""Mutable per-call state; graph definitions remain immutable."""
|
|
|
|
current_node_id: str
|
|
status: WorkflowStatus = WorkflowStatus.STARTING
|
|
pending_user_turn: UserTurn | None = None
|
|
transition_id: int = 0
|
|
automatic_hops: int = 0
|
|
ended: bool = False
|
|
_next_turn_id: int = 1
|
|
|
|
def begin_user_turn(self, text: str) -> UserTurn:
|
|
turn = UserTurn(id=self._next_turn_id, text=text)
|
|
self._next_turn_id += 1
|
|
self.pending_user_turn = turn
|
|
self.automatic_hops = 0
|
|
return turn
|
|
|
|
def enter(self, node_id: str, status: WorkflowStatus) -> None:
|
|
self.current_node_id = node_id
|
|
self.status = status
|
|
|
|
def begin_transition(self) -> int:
|
|
self.transition_id += 1
|
|
return self.transition_id
|
|
|
|
def consume_user_turn(self) -> UserTurn | None:
|
|
turn = self.pending_user_turn
|
|
self.pending_user_turn = None
|
|
return turn
|
|
|
|
def finish(self) -> None:
|
|
self.ended = True
|
|
self.status = WorkflowStatus.ENDED
|
|
self.pending_user_turn = None
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class LLMRouteResult:
|
|
"""Control-plane result returned by the small routing LLM."""
|
|
|
|
status: RouteStatus
|
|
function_name: str | None = None
|
|
error: str | None = None
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class EdgeEvaluation:
|
|
"""Final graph-level decision after expression, LLM and default handling."""
|
|
|
|
status: RouteStatus
|
|
edge: dict[str, Any] | None = None
|
|
error: str | None = None
|
|
|