mirror of
https://github.com/AstrBotDevs/AstrBot
synced 2026-07-15 17:30:13 +08:00
feat: add Python version check requiring 3.12 or 3.13
- Add version check at startup in both __main__.py and cmd_run.py - Suggest using `uv run -m astrbot` or reinstalling with uv - Add ABC base class and abstract methods to ComputerBooter - Improve type annotations in OpenAIAgentsRunner
This commit is contained in:
@@ -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:
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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]:
|
||||
|
||||
Reference in New Issue
Block a user