160 lines
6.1 KiB
Python
160 lines
6.1 KiB
Python
import asyncio
|
|
import os
|
|
import shutil
|
|
from typing import Any, Dict
|
|
|
|
from sqlmodel import Session, select
|
|
|
|
from core.docker_instance import docker_manager
|
|
from core.settings import BOTS_WORKSPACE_ROOT
|
|
from models.bot import BotInstance, BotMessage
|
|
from models.platform import BotActivityEvent, BotRequestUsage
|
|
from models.skill import BotSkillInstall
|
|
from models.topic import TopicItem, TopicTopic
|
|
from services.bot_service import (
|
|
_safe_float,
|
|
_safe_int,
|
|
read_bot_runtime_snapshot,
|
|
resolve_bot_runtime_env_params,
|
|
sync_bot_workspace_channels,
|
|
)
|
|
from services.bot_storage_service import write_bot_env_params
|
|
from services.cache_service import _invalidate_bot_detail_cache, _invalidate_bot_messages_cache
|
|
from services.platform_service import record_activity_event
|
|
from services.runtime_service import docker_callback, record_agent_loop_ready_warning
|
|
|
|
|
|
def _get_bot_or_404(session: Session, bot_id: str) -> BotInstance:
|
|
bot = session.get(BotInstance, bot_id)
|
|
if not bot:
|
|
raise ValueError("Bot not found")
|
|
return bot
|
|
|
|
|
|
async def start_bot_instance(session: Session, bot_id: str) -> Dict[str, Any]:
|
|
bot = _get_bot_or_404(session, bot_id)
|
|
if not bool(getattr(bot, "enabled", True)):
|
|
raise PermissionError("Bot is disabled. Enable it first.")
|
|
|
|
sync_bot_workspace_channels(session, bot_id)
|
|
runtime_snapshot = read_bot_runtime_snapshot(bot)
|
|
env_params = resolve_bot_runtime_env_params(bot_id)
|
|
write_bot_env_params(bot_id, env_params)
|
|
success = docker_manager.start_bot(
|
|
bot_id,
|
|
image_tag=bot.image_tag,
|
|
on_state_change=docker_callback,
|
|
env_vars=env_params,
|
|
cpu_cores=_safe_float(runtime_snapshot.get("cpu_cores"), 1.0),
|
|
memory_mb=_safe_int(runtime_snapshot.get("memory_mb"), 1024),
|
|
storage_gb=_safe_int(runtime_snapshot.get("storage_gb"), 10),
|
|
)
|
|
if not success:
|
|
bot.docker_status = "STOPPED"
|
|
session.add(bot)
|
|
session.commit()
|
|
raise RuntimeError(f"Failed to start container with image {bot.image_tag}")
|
|
|
|
actual_status = docker_manager.get_bot_status(bot_id)
|
|
bot.docker_status = actual_status
|
|
if actual_status != "RUNNING":
|
|
session.add(bot)
|
|
session.commit()
|
|
_invalidate_bot_detail_cache(bot_id)
|
|
raise RuntimeError("Bot container failed shortly after startup. Check bot logs/config.")
|
|
|
|
asyncio.create_task(record_agent_loop_ready_warning(bot_id))
|
|
session.add(bot)
|
|
record_activity_event(session, bot_id, "bot_started", channel="system", detail=f"Container started for {bot_id}")
|
|
session.commit()
|
|
_invalidate_bot_detail_cache(bot_id)
|
|
return {"status": "started"}
|
|
|
|
|
|
def stop_bot_instance(session: Session, bot_id: str) -> Dict[str, Any]:
|
|
bot = _get_bot_or_404(session, bot_id)
|
|
if not bool(getattr(bot, "enabled", True)):
|
|
raise PermissionError("Bot is disabled. Enable it first.")
|
|
|
|
docker_manager.stop_bot(bot_id)
|
|
bot.docker_status = "STOPPED"
|
|
session.add(bot)
|
|
record_activity_event(session, bot_id, "bot_stopped", channel="system", detail=f"Container stopped for {bot_id}")
|
|
session.commit()
|
|
_invalidate_bot_detail_cache(bot_id)
|
|
return {"status": "stopped"}
|
|
|
|
|
|
def enable_bot_instance(session: Session, bot_id: str) -> Dict[str, Any]:
|
|
bot = _get_bot_or_404(session, bot_id)
|
|
bot.enabled = True
|
|
session.add(bot)
|
|
record_activity_event(session, bot_id, "bot_enabled", channel="system", detail=f"Bot {bot_id} enabled")
|
|
session.commit()
|
|
_invalidate_bot_detail_cache(bot_id)
|
|
return {"status": "enabled", "enabled": True}
|
|
|
|
|
|
def disable_bot_instance(session: Session, bot_id: str) -> Dict[str, Any]:
|
|
bot = _get_bot_or_404(session, bot_id)
|
|
docker_manager.stop_bot(bot_id)
|
|
bot.enabled = False
|
|
bot.docker_status = "STOPPED"
|
|
if str(bot.current_state or "").upper() not in {"ERROR"}:
|
|
bot.current_state = "IDLE"
|
|
session.add(bot)
|
|
record_activity_event(session, bot_id, "bot_disabled", channel="system", detail=f"Bot {bot_id} disabled")
|
|
session.commit()
|
|
_invalidate_bot_detail_cache(bot_id)
|
|
return {"status": "disabled", "enabled": False}
|
|
|
|
|
|
def deactivate_bot_instance(session: Session, bot_id: str) -> Dict[str, Any]:
|
|
bot = _get_bot_or_404(session, bot_id)
|
|
docker_manager.stop_bot(bot_id)
|
|
bot.enabled = False
|
|
bot.docker_status = "STOPPED"
|
|
if str(bot.current_state or "").upper() not in {"ERROR"}:
|
|
bot.current_state = "IDLE"
|
|
session.add(bot)
|
|
record_activity_event(session, bot_id, "bot_deactivated", channel="system", detail=f"Bot {bot_id} deactivated")
|
|
session.commit()
|
|
_invalidate_bot_detail_cache(bot_id)
|
|
return {"status": "deactivated"}
|
|
|
|
|
|
def delete_bot_instance(session: Session, bot_id: str, delete_workspace: bool = True) -> Dict[str, Any]:
|
|
bot = _get_bot_or_404(session, bot_id)
|
|
docker_manager.stop_bot(bot_id)
|
|
|
|
messages = session.exec(select(BotMessage).where(BotMessage.bot_id == bot_id)).all()
|
|
for row in messages:
|
|
session.delete(row)
|
|
topic_items = session.exec(select(TopicItem).where(TopicItem.bot_id == bot_id)).all()
|
|
for row in topic_items:
|
|
session.delete(row)
|
|
topics = session.exec(select(TopicTopic).where(TopicTopic.bot_id == bot_id)).all()
|
|
for row in topics:
|
|
session.delete(row)
|
|
usage_rows = session.exec(select(BotRequestUsage).where(BotRequestUsage.bot_id == bot_id)).all()
|
|
for row in usage_rows:
|
|
session.delete(row)
|
|
activity_rows = session.exec(select(BotActivityEvent).where(BotActivityEvent.bot_id == bot_id)).all()
|
|
for row in activity_rows:
|
|
session.delete(row)
|
|
skill_install_rows = session.exec(select(BotSkillInstall).where(BotSkillInstall.bot_id == bot_id)).all()
|
|
for row in skill_install_rows:
|
|
session.delete(row)
|
|
|
|
session.delete(bot)
|
|
session.commit()
|
|
|
|
if delete_workspace:
|
|
workspace_root = os.path.join(BOTS_WORKSPACE_ROOT, bot_id)
|
|
if os.path.isdir(workspace_root):
|
|
shutil.rmtree(workspace_root, ignore_errors=True)
|
|
|
|
_invalidate_bot_detail_cache(bot_id)
|
|
_invalidate_bot_messages_cache(bot_id)
|
|
return {"status": "deleted", "workspace_deleted": bool(delete_workspace)}
|