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)):
|
if bool(request_data.get("backup_usr", True)):
|
||||||
backup_destination = create_usr_backup(
|
backup_destination = create_usr_backup(
|
||||||
repo_dir=REPO_DIR,
|
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")),
|
backup_name=str(request_data.get("backup_name", "agent-zero-usr-backup.zip")),
|
||||||
conflict_policy=str(request_data.get("backup_conflict_policy", "rename")),
|
conflict_policy=str(request_data.get("backup_conflict_policy", "rename")),
|
||||||
logger=logger,
|
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 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`
|
- The default file name format is `usr-YYYYMMDD-HHMMSS.zip`
|
||||||
- Conflict handling supports rename, overwrite, or fail-before-restart
|
- 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:
|
def get_default_backup_dir(repo_dir: str | Path | None = None) -> Path:
|
||||||
repository = get_repo_dir(repo_dir)
|
return Path("/root/update-backups")
|
||||||
return repository / "tmp" / "self-update-backups"
|
|
||||||
|
|
||||||
|
|
||||||
def get_repo_dir(repo_dir: str | Path | None = None) -> Path:
|
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
|
## 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**
|
- **Bot lifecycle**
|
||||||
- Managed by a `job_loop` extension that starts, restarts, or stops bots whenever plugin settings change.
|
- Managed by a `job_loop` extension that starts, restarts, or stops bots whenever plugin settings change.
|
||||||
- Supports both long-polling and webhook delivery modes.
|
- Supports both long-polling and webhook delivery modes.
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
from helpers.api import ApiHandler, Request
|
from helpers.api import ApiHandler, Request
|
||||||
from helpers.errors import format_error
|
from helpers.errors import format_error
|
||||||
|
from plugins._telegram_integration.helpers.dependencies import ensure_dependencies
|
||||||
|
|
||||||
|
|
||||||
class TestConnection(ApiHandler):
|
class TestConnection(ApiHandler):
|
||||||
|
|
@ -18,6 +19,7 @@ class TestConnection(ApiHandler):
|
||||||
return {"success": False, "results": results}
|
return {"success": False, "results": results}
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
ensure_dependencies()
|
||||||
from plugins._telegram_integration.helpers.bot_manager import test_token
|
from plugins._telegram_integration.helpers.bot_manager import test_token
|
||||||
ok, message = await test_token(token)
|
ok, message = await test_token(token)
|
||||||
results.append({
|
results.append({
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
from helpers.api import ApiHandler, Request, Response
|
from helpers.api import ApiHandler, Request, Response
|
||||||
from helpers.print_style import PrintStyle
|
from helpers.print_style import PrintStyle
|
||||||
|
from plugins._telegram_integration.helpers.dependencies import ensure_dependencies
|
||||||
|
|
||||||
|
|
||||||
class TelegramWebhook(ApiHandler):
|
class TelegramWebhook(ApiHandler):
|
||||||
|
|
@ -18,6 +19,7 @@ class TelegramWebhook(ApiHandler):
|
||||||
return ["POST"]
|
return ["POST"]
|
||||||
|
|
||||||
async def process(self, input: dict, request: Request) -> dict | Response:
|
async def process(self, input: dict, request: Request) -> dict | Response:
|
||||||
|
ensure_dependencies()
|
||||||
from aiogram.types import Update
|
from aiogram.types import Update
|
||||||
|
|
||||||
from plugins._telegram_integration.helpers.bot_manager import get_bot
|
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.errors import format_error
|
||||||
from helpers.print_style import PrintStyle
|
from helpers.print_style import PrintStyle
|
||||||
from helpers import plugins
|
from helpers import plugins
|
||||||
|
from plugins._telegram_integration.helpers.dependencies import ensure_dependencies, has_aiogram
|
||||||
|
|
||||||
|
|
||||||
PLUGIN_NAME: str = "_telegram_integration"
|
PLUGIN_NAME: str = "_telegram_integration"
|
||||||
|
|
@ -13,6 +14,19 @@ PLUGIN_NAME: str = "_telegram_integration"
|
||||||
class TelegramBotManager(Extension):
|
class TelegramBotManager(Extension):
|
||||||
|
|
||||||
async def execute(self, **kwargs: Any) -> None:
|
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 (
|
from plugins._telegram_integration.helpers.bot_manager import (
|
||||||
get_all_bots,
|
get_all_bots,
|
||||||
create_bot,
|
create_bot,
|
||||||
|
|
@ -32,12 +46,6 @@ class TelegramBotManager(Extension):
|
||||||
|
|
||||||
cleanup_old_attachments()
|
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()
|
running = get_all_bots()
|
||||||
|
|
||||||
# Stop bots that are no longer enabled
|
# Stop bots that are no longer enabled
|
||||||
|
|
|
||||||
|
|
@ -2,10 +2,14 @@ from helpers.extension import Extension
|
||||||
from helpers.print_style import PrintStyle
|
from helpers.print_style import PrintStyle
|
||||||
from helpers.errors import format_error
|
from helpers.errors import format_error
|
||||||
from agent import AgentContext, LoopData, UserMessage
|
from agent import AgentContext, LoopData, UserMessage
|
||||||
from plugins._telegram_integration.helpers.handler import (
|
from plugins._telegram_integration.helpers.constants import (
|
||||||
CTX_TG_BOT, CTX_TG_ATTACHMENTS, CTX_TG_KEYBOARD,
|
CTX_TG_BOT,
|
||||||
CTX_TG_TYPING_STOP, CTX_TG_REPLY_TO,
|
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
|
MAX_SEND_RETRIES: int = 2
|
||||||
CTX_SEND_FAILURES: str = "_telegram_send_failures"
|
CTX_SEND_FAILURES: str = "_telegram_send_failures"
|
||||||
|
|
@ -46,6 +50,7 @@ class TelegramAutoReply(Extension):
|
||||||
attachments: list[str],
|
attachments: list[str],
|
||||||
keyboard: list[list[dict]] | None,
|
keyboard: list[list[dict]] | None,
|
||||||
):
|
):
|
||||||
|
ensure_dependencies()
|
||||||
from plugins._telegram_integration.helpers.handler import send_telegram_reply
|
from plugins._telegram_integration.helpers.handler import send_telegram_reply
|
||||||
|
|
||||||
error = await send_telegram_reply(
|
error = await send_telegram_reply(
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
from helpers.extension import Extension
|
from helpers.extension import Extension
|
||||||
from agent import LoopData
|
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):
|
class TelegramContextPrompt(Extension):
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,11 @@
|
||||||
from helpers.extension import Extension
|
from helpers.extension import Extension
|
||||||
from helpers.tool import Response
|
from helpers.tool import Response
|
||||||
from plugins._telegram_integration.helpers.handler import (
|
from plugins._telegram_integration.helpers.constants import (
|
||||||
CTX_TG_BOT,
|
CTX_TG_BOT,
|
||||||
CTX_TG_ATTACHMENTS,
|
CTX_TG_ATTACHMENTS,
|
||||||
CTX_TG_KEYBOARD,
|
CTX_TG_KEYBOARD,
|
||||||
)
|
)
|
||||||
|
from plugins._telegram_integration.helpers.dependencies import ensure_dependencies
|
||||||
|
|
||||||
|
|
||||||
class TelegramResponseIntercept(Extension):
|
class TelegramResponseIntercept(Extension):
|
||||||
|
|
@ -40,6 +41,7 @@ class TelegramResponseIntercept(Extension):
|
||||||
await self._send_inline(context, tool, response)
|
await self._send_inline(context, tool, response)
|
||||||
|
|
||||||
async def _send_inline(self, context, tool, response: Response):
|
async def _send_inline(self, context, tool, response: Response):
|
||||||
|
ensure_dependencies()
|
||||||
from plugins._telegram_integration.helpers.handler import send_telegram_reply
|
from plugins._telegram_integration.helpers.handler import send_telegram_reply
|
||||||
|
|
||||||
agent = self.agent
|
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 import telegram_client as tc
|
||||||
from plugins._telegram_integration.helpers.bot_manager import get_bot
|
from plugins._telegram_integration.helpers.bot_manager import get_bot
|
||||||
|
from plugins._telegram_integration.helpers.constants import (
|
||||||
|
PLUGIN_NAME,
|
||||||
PLUGIN_NAME = "_telegram_integration"
|
DOWNLOAD_FOLDER,
|
||||||
DOWNLOAD_FOLDER = "usr/uploads"
|
STATE_FILE,
|
||||||
STATE_FILE = "usr/plugins/_telegram_integration/state.json"
|
CTX_TG_BOT,
|
||||||
|
CTX_TG_BOT_CFG,
|
||||||
# Context data keys
|
CTX_TG_CHAT_ID,
|
||||||
CTX_TG_BOT = "telegram_bot"
|
CTX_TG_USER_ID,
|
||||||
CTX_TG_BOT_CFG = "telegram_bot_cfg"
|
CTX_TG_USERNAME,
|
||||||
CTX_TG_CHAT_ID = "telegram_chat_id"
|
CTX_TG_TYPING_STOP,
|
||||||
CTX_TG_USER_ID = "telegram_user_id"
|
CTX_TG_REPLY_TO,
|
||||||
CTX_TG_USERNAME = "telegram_username"
|
CTX_TG_ATTACHMENTS,
|
||||||
CTX_TG_TYPING_STOP = "_telegram_typing_stop"
|
CTX_TG_KEYBOARD,
|
||||||
CTX_TG_REPLY_TO = "_telegram_reply_to_message_id"
|
)
|
||||||
|
|
||||||
# Transient
|
|
||||||
CTX_TG_ATTACHMENTS = "_telegram_response_attachments"
|
|
||||||
CTX_TG_KEYBOARD = "_telegram_response_keyboard"
|
|
||||||
|
|
||||||
# Chat mapping: (bot_name, tg_user_id) → AgentContext ID
|
# Chat mapping: (bot_name, tg_user_id) → AgentContext ID
|
||||||
|
|
||||||
|
|
@ -590,4 +586,3 @@ def _inherit_model_override(ctx: AgentContext):
|
||||||
)
|
)
|
||||||
if source:
|
if source:
|
||||||
ctx.set_data("chat_model_override", source.get_data("chat_model_override"))
|
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
|
beautifulsoup4>=4.12.3
|
||||||
boto3>=1.35.0
|
boto3>=1.35.0
|
||||||
exchangelib>=5.4.3
|
exchangelib>=5.4.3
|
||||||
aiogram>=3.15.0
|
|
||||||
pywinpty==3.0.2; sys_platform == "win32"
|
pywinpty==3.0.2; sys_platform == "win32"
|
||||||
python-socketio>=5.14.2
|
python-socketio>=5.14.2
|
||||||
uvicorn>=0.38.0
|
uvicorn>=0.38.0
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue