mirror of
https://github.com/agent0ai/agent-zero.git
synced 2026-04-28 03:30:23 +00:00
Change default self-update backup directory from /a0/tmp to /root and add lazy aiogram dependency loading for Telegram plugin
- Update default backup path from /a0/tmp/self-update-backups to /root/update-backups in self_update_manager.py, helpers/self_update.py, and documentation - Move aiogram from global requirements.txt to plugin-local requirements for _telegram_integration - Add ensure_dependencies() helper that installs aiogram on-demand via uv pip install - Add has_aiogram() check to avoid
This commit is contained in:
parent
68ad5aca46
commit
247c8d845f
15 changed files with 135 additions and 35 deletions
|
|
@ -614,7 +614,7 @@ def execute_pending_update(
|
|||
if bool(request_data.get("backup_usr", True)):
|
||||
backup_destination = create_usr_backup(
|
||||
repo_dir=REPO_DIR,
|
||||
backup_path=str(request_data.get("backup_path", "/a0/tmp/self-update-backups")),
|
||||
backup_path=str(request_data.get("backup_path", "/root/update-backups")),
|
||||
backup_name=str(request_data.get("backup_name", "agent-zero-usr-backup.zip")),
|
||||
conflict_policy=str(request_data.get("backup_conflict_policy", "rename")),
|
||||
logger=logger,
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ Because these files live in `/exe`, you can recover from an older downgraded `/a
|
|||
|
||||
The updater can create a zip backup of `/a0/usr` before replacing repository files.
|
||||
|
||||
- The default backup directory is `/a0/tmp/self-update-backups`
|
||||
- The default backup directory is `/root/update-backups`
|
||||
- The default file name format is `usr-YYYYMMDD-HHMMSS.zip`
|
||||
- Conflict handling supports rename, overwrite, or fail-before-restart
|
||||
|
||||
|
|
|
|||
|
|
@ -108,8 +108,7 @@ def get_log_text() -> str:
|
|||
|
||||
|
||||
def get_default_backup_dir(repo_dir: str | Path | None = None) -> Path:
|
||||
repository = get_repo_dir(repo_dir)
|
||||
return repository / "tmp" / "self-update-backups"
|
||||
return Path("/root/update-backups")
|
||||
|
||||
|
||||
def get_repo_dir(repo_dir: str | Path | None = None) -> Path:
|
||||
|
|
|
|||
|
|
@ -8,6 +8,9 @@ This plugin connects one or more Telegram bots to Agent Zero. Each bot runs inde
|
|||
|
||||
## Main Behavior
|
||||
|
||||
- **Dependency management**
|
||||
- Keeps `aiogram` in the plugin-local `requirements.txt` instead of the global root requirements.
|
||||
- Active code paths call `helpers/dependencies.py::ensure_dependencies()` to install `aiogram` into the framework runtime on first use via `uv pip install --python <current interpreter> -r plugins/_telegram_integration/requirements.txt`.
|
||||
- **Bot lifecycle**
|
||||
- Managed by a `job_loop` extension that starts, restarts, or stops bots whenever plugin settings change.
|
||||
- Supports both long-polling and webhook delivery modes.
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
from helpers.api import ApiHandler, Request
|
||||
from helpers.errors import format_error
|
||||
from plugins._telegram_integration.helpers.dependencies import ensure_dependencies
|
||||
|
||||
|
||||
class TestConnection(ApiHandler):
|
||||
|
|
@ -18,6 +19,7 @@ class TestConnection(ApiHandler):
|
|||
return {"success": False, "results": results}
|
||||
|
||||
try:
|
||||
ensure_dependencies()
|
||||
from plugins._telegram_integration.helpers.bot_manager import test_token
|
||||
ok, message = await test_token(token)
|
||||
results.append({
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
from helpers.api import ApiHandler, Request, Response
|
||||
from helpers.print_style import PrintStyle
|
||||
from plugins._telegram_integration.helpers.dependencies import ensure_dependencies
|
||||
|
||||
|
||||
class TelegramWebhook(ApiHandler):
|
||||
|
|
@ -18,6 +19,7 @@ class TelegramWebhook(ApiHandler):
|
|||
return ["POST"]
|
||||
|
||||
async def process(self, input: dict, request: Request) -> dict | Response:
|
||||
ensure_dependencies()
|
||||
from aiogram.types import Update
|
||||
|
||||
from plugins._telegram_integration.helpers.bot_manager import get_bot
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ from helpers.extension import Extension
|
|||
from helpers.errors import format_error
|
||||
from helpers.print_style import PrintStyle
|
||||
from helpers import plugins
|
||||
from plugins._telegram_integration.helpers.dependencies import ensure_dependencies, has_aiogram
|
||||
|
||||
|
||||
PLUGIN_NAME: str = "_telegram_integration"
|
||||
|
|
@ -13,6 +14,19 @@ PLUGIN_NAME: str = "_telegram_integration"
|
|||
class TelegramBotManager(Extension):
|
||||
|
||||
async def execute(self, **kwargs: Any) -> None:
|
||||
config = plugins.get_plugin_config(PLUGIN_NAME) or {}
|
||||
bots_cfg = config.get("bots", [])
|
||||
enabled_names = {
|
||||
b["name"] for b in bots_cfg if b.get("enabled") and b.get("name") and b.get("token")
|
||||
}
|
||||
|
||||
# Avoid installing aiogram on idle ticks when Telegram is not configured.
|
||||
if not enabled_names and not has_aiogram():
|
||||
return
|
||||
|
||||
if enabled_names:
|
||||
ensure_dependencies()
|
||||
|
||||
from plugins._telegram_integration.helpers.bot_manager import (
|
||||
get_all_bots,
|
||||
create_bot,
|
||||
|
|
@ -32,12 +46,6 @@ class TelegramBotManager(Extension):
|
|||
|
||||
cleanup_old_attachments()
|
||||
|
||||
config = plugins.get_plugin_config(PLUGIN_NAME) or {}
|
||||
bots_cfg = config.get("bots", [])
|
||||
enabled_names = {
|
||||
b["name"] for b in bots_cfg if b.get("enabled") and b.get("name") and b.get("token")
|
||||
}
|
||||
|
||||
running = get_all_bots()
|
||||
|
||||
# Stop bots that are no longer enabled
|
||||
|
|
|
|||
|
|
@ -2,10 +2,14 @@ from helpers.extension import Extension
|
|||
from helpers.print_style import PrintStyle
|
||||
from helpers.errors import format_error
|
||||
from agent import AgentContext, LoopData, UserMessage
|
||||
from plugins._telegram_integration.helpers.handler import (
|
||||
CTX_TG_BOT, CTX_TG_ATTACHMENTS, CTX_TG_KEYBOARD,
|
||||
CTX_TG_TYPING_STOP, CTX_TG_REPLY_TO,
|
||||
from plugins._telegram_integration.helpers.constants import (
|
||||
CTX_TG_BOT,
|
||||
CTX_TG_ATTACHMENTS,
|
||||
CTX_TG_KEYBOARD,
|
||||
CTX_TG_TYPING_STOP,
|
||||
CTX_TG_REPLY_TO,
|
||||
)
|
||||
from plugins._telegram_integration.helpers.dependencies import ensure_dependencies
|
||||
|
||||
MAX_SEND_RETRIES: int = 2
|
||||
CTX_SEND_FAILURES: str = "_telegram_send_failures"
|
||||
|
|
@ -46,6 +50,7 @@ class TelegramAutoReply(Extension):
|
|||
attachments: list[str],
|
||||
keyboard: list[list[dict]] | None,
|
||||
):
|
||||
ensure_dependencies()
|
||||
from plugins._telegram_integration.helpers.handler import send_telegram_reply
|
||||
|
||||
error = await send_telegram_reply(
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
from helpers.extension import Extension
|
||||
from agent import LoopData
|
||||
from plugins._telegram_integration.helpers.handler import CTX_TG_BOT, CTX_TG_BOT_CFG
|
||||
from plugins._telegram_integration.helpers.constants import CTX_TG_BOT, CTX_TG_BOT_CFG
|
||||
|
||||
|
||||
class TelegramContextPrompt(Extension):
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
from helpers.extension import Extension
|
||||
from helpers.tool import Response
|
||||
from plugins._telegram_integration.helpers.handler import (
|
||||
from plugins._telegram_integration.helpers.constants import (
|
||||
CTX_TG_BOT,
|
||||
CTX_TG_ATTACHMENTS,
|
||||
CTX_TG_KEYBOARD,
|
||||
)
|
||||
from plugins._telegram_integration.helpers.dependencies import ensure_dependencies
|
||||
|
||||
|
||||
class TelegramResponseIntercept(Extension):
|
||||
|
|
@ -40,6 +41,7 @@ class TelegramResponseIntercept(Extension):
|
|||
await self._send_inline(context, tool, response)
|
||||
|
||||
async def _send_inline(self, context, tool, response: Response):
|
||||
ensure_dependencies()
|
||||
from plugins._telegram_integration.helpers.handler import send_telegram_reply
|
||||
|
||||
agent = self.agent
|
||||
|
|
|
|||
16
plugins/_telegram_integration/helpers/constants.py
Normal file
16
plugins/_telegram_integration/helpers/constants.py
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
PLUGIN_NAME = "_telegram_integration"
|
||||
DOWNLOAD_FOLDER = "usr/uploads"
|
||||
STATE_FILE = "usr/plugins/_telegram_integration/state.json"
|
||||
|
||||
# Context data keys
|
||||
CTX_TG_BOT = "telegram_bot"
|
||||
CTX_TG_BOT_CFG = "telegram_bot_cfg"
|
||||
CTX_TG_CHAT_ID = "telegram_chat_id"
|
||||
CTX_TG_USER_ID = "telegram_user_id"
|
||||
CTX_TG_USERNAME = "telegram_username"
|
||||
CTX_TG_TYPING_STOP = "_telegram_typing_stop"
|
||||
CTX_TG_REPLY_TO = "_telegram_reply_to_message_id"
|
||||
|
||||
# Transient
|
||||
CTX_TG_ATTACHMENTS = "_telegram_response_attachments"
|
||||
CTX_TG_KEYBOARD = "_telegram_response_keyboard"
|
||||
68
plugins/_telegram_integration/helpers/dependencies.py
Normal file
68
plugins/_telegram_integration/helpers/dependencies.py
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import importlib
|
||||
import importlib.util
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import threading
|
||||
from pathlib import Path
|
||||
|
||||
from helpers.errors import format_error
|
||||
from helpers.print_style import PrintStyle
|
||||
|
||||
|
||||
_LOCK = threading.Lock()
|
||||
_CHECKED = False
|
||||
_PLUGIN_DIR = Path(__file__).resolve().parents[1]
|
||||
_REQUIREMENTS_FILE = _PLUGIN_DIR / "requirements.txt"
|
||||
|
||||
|
||||
def has_aiogram() -> bool:
|
||||
return importlib.util.find_spec("aiogram") is not None
|
||||
|
||||
|
||||
def ensure_dependencies() -> None:
|
||||
global _CHECKED
|
||||
|
||||
if _CHECKED and has_aiogram():
|
||||
return
|
||||
|
||||
with _LOCK:
|
||||
if _CHECKED and has_aiogram():
|
||||
return
|
||||
if has_aiogram():
|
||||
_CHECKED = True
|
||||
return
|
||||
|
||||
_install_aiogram()
|
||||
importlib.invalidate_caches()
|
||||
|
||||
if not has_aiogram():
|
||||
raise RuntimeError("Telegram dependency 'aiogram' is still unavailable after installation")
|
||||
|
||||
_CHECKED = True
|
||||
|
||||
|
||||
def _install_aiogram() -> None:
|
||||
uv = shutil.which("uv")
|
||||
if not uv:
|
||||
raise RuntimeError("Telegram plugin requires 'uv' to install aiogram automatically")
|
||||
if not _REQUIREMENTS_FILE.is_file():
|
||||
raise RuntimeError(f"Telegram plugin requirements file not found: {_REQUIREMENTS_FILE}")
|
||||
|
||||
cmd = [
|
||||
uv,
|
||||
"pip",
|
||||
"install",
|
||||
"--python",
|
||||
sys.executable,
|
||||
"-r",
|
||||
str(_REQUIREMENTS_FILE),
|
||||
]
|
||||
|
||||
PrintStyle.info("Telegram: aiogram not found, installing plugin dependency")
|
||||
try:
|
||||
subprocess.check_call(cmd, cwd=str(_PLUGIN_DIR))
|
||||
except Exception as e:
|
||||
raise RuntimeError(f"Failed to install Telegram dependency 'aiogram': {format_error(e)}") from e
|
||||
|
|
@ -21,24 +21,20 @@ from initialize import initialize_agent
|
|||
|
||||
from plugins._telegram_integration.helpers import telegram_client as tc
|
||||
from plugins._telegram_integration.helpers.bot_manager import get_bot
|
||||
|
||||
|
||||
PLUGIN_NAME = "_telegram_integration"
|
||||
DOWNLOAD_FOLDER = "usr/uploads"
|
||||
STATE_FILE = "usr/plugins/_telegram_integration/state.json"
|
||||
|
||||
# Context data keys
|
||||
CTX_TG_BOT = "telegram_bot"
|
||||
CTX_TG_BOT_CFG = "telegram_bot_cfg"
|
||||
CTX_TG_CHAT_ID = "telegram_chat_id"
|
||||
CTX_TG_USER_ID = "telegram_user_id"
|
||||
CTX_TG_USERNAME = "telegram_username"
|
||||
CTX_TG_TYPING_STOP = "_telegram_typing_stop"
|
||||
CTX_TG_REPLY_TO = "_telegram_reply_to_message_id"
|
||||
|
||||
# Transient
|
||||
CTX_TG_ATTACHMENTS = "_telegram_response_attachments"
|
||||
CTX_TG_KEYBOARD = "_telegram_response_keyboard"
|
||||
from plugins._telegram_integration.helpers.constants import (
|
||||
PLUGIN_NAME,
|
||||
DOWNLOAD_FOLDER,
|
||||
STATE_FILE,
|
||||
CTX_TG_BOT,
|
||||
CTX_TG_BOT_CFG,
|
||||
CTX_TG_CHAT_ID,
|
||||
CTX_TG_USER_ID,
|
||||
CTX_TG_USERNAME,
|
||||
CTX_TG_TYPING_STOP,
|
||||
CTX_TG_REPLY_TO,
|
||||
CTX_TG_ATTACHMENTS,
|
||||
CTX_TG_KEYBOARD,
|
||||
)
|
||||
|
||||
# Chat mapping: (bot_name, tg_user_id) → AgentContext ID
|
||||
|
||||
|
|
@ -590,4 +586,3 @@ def _inherit_model_override(ctx: AgentContext):
|
|||
)
|
||||
if source:
|
||||
ctx.set_data("chat_model_override", source.get_data("chat_model_override"))
|
||||
|
||||
|
|
|
|||
1
plugins/_telegram_integration/requirements.txt
Normal file
1
plugins/_telegram_integration/requirements.txt
Normal file
|
|
@ -0,0 +1 @@
|
|||
aiogram>=3.15.0
|
||||
|
|
@ -48,7 +48,6 @@ html2text>=2024.2.26
|
|||
beautifulsoup4>=4.12.3
|
||||
boto3>=1.35.0
|
||||
exchangelib>=5.4.3
|
||||
aiogram>=3.15.0
|
||||
pywinpty==3.0.2; sys_platform == "win32"
|
||||
python-socketio>=5.14.2
|
||||
uvicorn>=0.38.0
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue