Source code for rath.flow.selector
"""``Selector`` workflow: LLM-routed choice over self-describing workflows."""
from __future__ import annotations
from collections.abc import Callable
from rath.flow.agent_param import AgentParam
from rath.flow.empty import EmptyWorkflow
from rath.flow.workflow import Workflow
from rath.llm import RathLLMStreamDelta
from rath.llm.provider import Provider
from rath.session import Session, select_session
from rath.session.loop import SessionLoopExecutor
_DEFAULT_SELECT_INSTRUCTION = (
"You are a router. Given the conversation and a numbered menu of candidate "
"workflows, reply with the single best index, or -1 if none applies / the task "
"is already complete."
)
[docs]
class Selector(Workflow):
"""Pick the next :class:`~rath.flow.workflow.Workflow` for a session.
Sibling of :class:`~rath.flow.agent.Agent` / :class:`~rath.flow.compressor.Compressor`.
"""
def __init__(
self,
provider: Provider,
*,
select_instruction: str = _DEFAULT_SELECT_INSTRUCTION,
description: str = "",
on_event: Callable[[RathLLMStreamDelta], None] | None = None,
):
super().__init__(description=description)
self._on_event = on_event
self._test_executor: SessionLoopExecutor | None = None
self.agent = AgentParam(
agent_session=Session.from_agent_prompt(select_instruction),
provider=provider,
)
[docs]
def forward( # type: ignore[override]
self, session: Session, *workflows: Workflow
) -> Workflow:
"""Return the workflow the model picks for ``session``, or an
:class:`~rath.flow.empty.EmptyWorkflow` when no candidate fits / the session
is complete.
NOTE: this deliberately deviates from the base ``forward(session) -> Session``
contract. ``Selector`` is a routing *decision* component, not a session
transformer: it returns the chosen :class:`Workflow` (never ``None``), and the
caller dispatches it (``session = chosen(session)``). Completion is signalled by
returning an ``EmptyWorkflow`` (a no-op), detected via
``isinstance(result, EmptyWorkflow)``.
"""
idx = -1
if workflows:
idx, _ = select_session(
session,
self.agent.agent_session,
*(wf.description for wf in workflows),
agent_provider=self.agent.provider,
executor=self._test_executor,
)
return workflows[idx] if 0 <= idx < len(workflows) else EmptyWorkflow()
__all__ = ["Selector"]