fischer-agentkit/src/agentkit/tools/advance_phase.py

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}.",
}