import abc import asyncio from collections.abc import AsyncGenerator from enum import Enum, auto from typing import Any, Generic from astrbot import logger from astrbot.core.agent.hooks import BaseAgentRunHooks from astrbot.core.agent.response import AgentResponse from astrbot.core.agent.run_context import ContextWrapper, TContext from astrbot.core.agent.tool_executor import BaseFunctionToolExecutor from astrbot.core.provider.entities import LLMResponse, ProviderRequest from astrbot.core.provider.provider import Provider class AgentState(Enum): """Defines the state of the agent.""" IDLE = auto() # Initial state RUNNING = auto() # Currently processing DONE = auto() # Completed ERROR = auto() # Error state class BaseAgentRunner(Generic[TContext]): def __init__( self, ): self.tasks: set[asyncio.Task[object]] = set() self._state = AgentState.IDLE @abc.abstractmethod async def reset( self, provider: Provider, request: ProviderRequest, run_context: ContextWrapper[TContext], tool_executor: BaseFunctionToolExecutor[TContext], agent_hooks: BaseAgentRunHooks[TContext], streaming: bool = False, enforce_max_turns: int = -1, llm_compress_instruction: str | None = None, llm_compress_keep_recent: int = 0, llm_compress_provider: Provider | None = None, truncate_turns: int = 1, custom_token_counter: Any = None, custom_compressor: Any = None, tool_schema_mode: str | None = "full", fallback_providers: list[Provider] | None = None, provider_config: dict | None = None, **kwargs: Any, ) -> None: """Reset the agent to its initial state. This method should be called before starting a new run. """ ... @abc.abstractmethod def step(self) -> AsyncGenerator[AgentResponse, None]: """Process a single step of the agent.""" ... @abc.abstractmethod def step_until_done(self, max_step: int) -> AsyncGenerator[AgentResponse, None]: """Process steps until the agent is done.""" ... @abc.abstractmethod def done(self) -> bool: """Check if the agent has completed its task. Returns True if the agent is done, False otherwise. """ ... @abc.abstractmethod def get_final_llm_resp(self) -> LLMResponse | None: """Get the final observation from the agent. This method should be called after the agent is done. """ ... def _transition_state(self, new_state: AgentState) -> None: """Transition the agent state.""" if self._state != new_state: logger.debug(f"Agent state transition: {self._state} -> {new_state}") self._state = new_state