diff --git a/astrbot/__main__.py b/astrbot/__main__.py index 59f40fd79..b3ca09708 100644 --- a/astrbot/__main__.py +++ b/astrbot/__main__.py @@ -44,9 +44,14 @@ logo_tmpl = r""" def check_env() -> None: - if not (sys.version_info.major == 3 and sys.version_info.minor >= 10): - logger.error("请使用 Python3.10+ 运行本项目。") - exit() + # Python version check: require 3.12 or 3.13 + if not (sys.version_info.major == 3 and sys.version_info.minor in (12, 13)): + print( + f"❌ Python 版本错误: 当前使用 Python {sys.version_info.major}.{sys.version_info.minor}\n" + "请使用 uv tool install -e . --force -p 3.12 或 -p 3.13 重新安装", + file=sys.stderr, + ) + sys.exit(1) astrbot_root = get_astrbot_root() if astrbot_root not in sys.path: diff --git a/astrbot/_internal/agents/openai_agents/runner.py b/astrbot/_internal/agents/openai_agents/runner.py index e34484338..973e914c3 100644 --- a/astrbot/_internal/agents/openai_agents/runner.py +++ b/astrbot/_internal/agents/openai_agents/runner.py @@ -6,18 +6,29 @@ and the openai-agents library from OpenAI. from __future__ import annotations +import sys +from collections.abc import AsyncGenerator from typing import Any +if sys.version_info >= (3, 12): + from typing import override +else: + from typing_extensions import override + from agents import Agent as OpenAIAgent from agents import Model, Runner, RunResult from agents.run_config import RunConfig from astrbot.core.agent.hooks import BaseAgentRunHooks +from astrbot.core.agent.response import AgentResponse, AgentResponseData from astrbot.core.agent.run_context import ContextWrapper, TContext from astrbot.core.agent.runners.base import AgentState, BaseAgentRunner +from astrbot.core.agent.tool_executor import BaseFunctionToolExecutor from astrbot.core.agent.tool import FunctionTool -from astrbot.core.provider.entities import LLMResponse +from astrbot.core.message.message_event_result import MessageChain +from astrbot.core.provider.entities import LLMResponse, ProviderRequest from astrbot.core.provider.func_tool_manager import FunctionToolManager +from astrbot.core.provider.provider import Provider from .tool_adapter import astrbot_tool_to_agents_tool @@ -82,10 +93,25 @@ class OpenAIAgentsRunner(BaseAgentRunner[TContext]): return tool.handler raise ValueError(f"Tool handler not found for: {tool_name}") + @override 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.""" @@ -103,13 +129,17 @@ class OpenAIAgentsRunner(BaseAgentRunner[TContext]): **self._openai_agents_kwargs, ) - async def step(self) -> Any: + @override + async def step(self) -> AsyncGenerator[AgentResponse, None]: """Process a single step of the agent (not directly supported).""" raise NotImplementedError( "step() is not directly supported. Use step_until_done() instead." ) - async def step_until_done(self, max_step: int = 50) -> Any: + @override + async def step_until_done( + self, max_step: int + ) -> AsyncGenerator[AgentResponse, None]: """Run the agent until completion or max steps.""" if not self._agent or not self._run_context: raise RuntimeError("Agent not initialized. Call reset() first.") @@ -125,12 +155,17 @@ class OpenAIAgentsRunner(BaseAgentRunner[TContext]): run_config=run_config, ) self._state = AgentState.DONE + + if self._run_result: + chain = MessageChain().message(self._run_result.final_output) + yield AgentResponse( + type="llm_result", + data=AgentResponseData(chain=chain), + ) except Exception: self._state = AgentState.ERROR raise - return self._run_result - def done(self) -> bool: """Check if the agent has completed its task.""" return self._state == AgentState.DONE @@ -140,7 +175,6 @@ class OpenAIAgentsRunner(BaseAgentRunner[TContext]): if not self._run_result: return None return LLMResponse( - content=self._run_result.final_output, - model=self._run_result.model, - usage=self._run_result.usage, + role="assistant", + completion_text=self._run_result.final_output, ) diff --git a/astrbot/cli/commands/cmd_run.py b/astrbot/cli/commands/cmd_run.py index 0d6ea75fa..4870e9628 100644 --- a/astrbot/cli/commands/cmd_run.py +++ b/astrbot/cli/commands/cmd_run.py @@ -57,6 +57,15 @@ from filelock import FileLock, Timeout from astrbot.cli.utils import DashboardManager from astrbot.runtime_bootstrap import initialize_runtime_bootstrap +# Python version check: require 3.12 or 3.13 +if not (sys.version_info.major == 3 and sys.version_info.minor in (12, 13)): + print( + f"❌ Python 版本错误: 当前使用 Python {sys.version_info.major}.{sys.version_info.minor}\n" + "请使用 uv tool install -e . --force -p 3.12 或 -p 3.13 重新安装", + file=sys.stderr, + ) + sys.exit(1) + initialize_runtime_bootstrap() # Regular expression to find bash-like parameter expansions: # ${VAR:-default} or ${VAR} diff --git a/astrbot/core/computer/booters/base.py b/astrbot/core/computer/booters/base.py index 29eb2281f..b2541ca60 100644 --- a/astrbot/core/computer/booters/base.py +++ b/astrbot/core/computer/booters/base.py @@ -1,5 +1,6 @@ from __future__ import annotations +import abc from typing import TYPE_CHECKING from ..olayer import ( @@ -13,15 +14,21 @@ if TYPE_CHECKING: from astrbot.core.agent.tool import FunctionTool -class ComputerBooter: +class ComputerBooter(abc.ABC): @property - def fs(self) -> FileSystemComponent: ... + @abc.abstractmethod + def fs(self) -> FileSystemComponent: + raise NotImplementedError("Subclass must implement fs property") @property - def python(self) -> PythonComponent: ... + @abc.abstractmethod + def python(self) -> PythonComponent: + raise NotImplementedError("Subclass must implement python property") @property - def shell(self) -> ShellComponent: ... + @abc.abstractmethod + def shell(self) -> ShellComponent: + raise NotImplementedError("Subclass must implement shell property") @property def capabilities(self) -> tuple[str, ...] | None: @@ -36,24 +43,29 @@ class ComputerBooter: def browser(self) -> BrowserComponent | None: return None - async def boot(self, session_id: str) -> None: ... + @abc.abstractmethod + async def boot(self, session_id: str) -> None: + raise NotImplementedError("Subclass must implement boot method") - async def shutdown(self) -> None: ... + @abc.abstractmethod + async def shutdown(self) -> None: + raise NotImplementedError("Subclass must implement shutdown method") async def upload_file(self, path: str, file_name: str) -> dict: """Upload file to the computer. Should return a dict with `success` (bool) and `file_path` (str) keys. """ - ... + raise NotImplementedError("Subclass must implement upload_file method") async def download_file(self, remote_path: str, local_path: str) -> None: """Download file from the computer.""" - ... + raise NotImplementedError("Subclass must implement download_file method") + @abc.abstractmethod async def available(self) -> bool: """Check if the computer is available.""" - ... + raise NotImplementedError("Subclass must implement available method") @classmethod def get_default_tools(cls) -> list[FunctionTool]: