83 lines
2.9 KiB
Python
83 lines
2.9 KiB
Python
"""AdvancePhaseTool — LLM-driven phase transition (G6, KTD6).
|
|
|
|
Registered alongside other tools when ReActEngine has a phase_policy set.
|
|
The LLM calls this tool to signal "I'm done planning, move to building".
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import logging
|
|
from typing import TYPE_CHECKING
|
|
|
|
from agentkit.tools.base import Tool
|
|
|
|
if TYPE_CHECKING:
|
|
from agentkit.core.react import ReActEngine
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
class AdvancePhaseTool(Tool):
|
|
"""Tool that advances the ReActEngine's current phase.
|
|
|
|
KTD6: LLM-driven phase transitions. Auto-advance is opt-in via
|
|
``plan_exec.auto_advance_after_steps``; this tool is the manual path.
|
|
|
|
The tool holds a weak reference to the engine (via bound method
|
|
``engine.advance_phase``) — registered only when phase_policy is set.
|
|
"""
|
|
|
|
def __init__(
|
|
self,
|
|
engine: "ReActEngine",
|
|
name: str = "advance_phase",
|
|
description: str | None = None,
|
|
version: str = "1.0.0",
|
|
tags: list[str] | None = None,
|
|
):
|
|
super().__init__(
|
|
name=name,
|
|
description=description
|
|
or (
|
|
"Advance the PLAN_EXEC phase state machine to the next phase "
|
|
"(Planning → Building → Verification → Delivery). Call this "
|
|
"when you have finished the current phase's work and are ready "
|
|
"to move on. Returns the new phase name or an error if you "
|
|
"are already at the final (Delivery) phase."
|
|
),
|
|
input_schema={
|
|
"type": "object",
|
|
"properties": {},
|
|
"additionalProperties": False,
|
|
},
|
|
version=version,
|
|
tags=tags or ["phase", "control"],
|
|
)
|
|
self._engine = engine
|
|
|
|
async def execute(self, **kwargs) -> dict[str, object]:
|
|
# Capture previous phase before transition (engine is single-threaded per request).
|
|
previous = self._engine.current_phase
|
|
new_phase = self._engine.advance_phase()
|
|
if new_phase is None:
|
|
# Either no policy set, or already at DELIVERY.
|
|
current = self._engine.current_phase
|
|
if current is None:
|
|
return {
|
|
"is_error": True,
|
|
"error": "no_phase_policy",
|
|
"message": "No phase policy is set — advance_phase is a no-op.",
|
|
}
|
|
return {
|
|
"is_error": True,
|
|
"error": "already_at_final_phase",
|
|
"message": (f"Already at final phase ({current.value}). Cannot advance further."),
|
|
"current_phase": current.value,
|
|
}
|
|
return {
|
|
"is_error": False,
|
|
"previous_phase": previous.value if previous else "",
|
|
"current_phase": new_phase.value,
|
|
"message": f"Phase advanced to {new_phase.value}.",
|
|
}
|