Compare commits
No commits in common. "805f7a017e66383169e9fcaf05a428fa048eb9f2" and "9406d26afdf614cdeb4aefe9a275b7c651cb52e0" have entirely different histories.
805f7a017e
...
9406d26afd
13 changed files with 238 additions and 4150 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -12,9 +12,6 @@ __pycache__/
|
||||||
.env.development
|
.env.development
|
||||||
.env.test
|
.env.test
|
||||||
docker-compose.override.yml
|
docker-compose.override.yml
|
||||||
|
|
||||||
test_browser.py
|
|
||||||
|
|
||||||
export*
|
export*
|
||||||
__pycache__/model_tools.cpython-310.pyc
|
__pycache__/model_tools.cpython-310.pyc
|
||||||
__pycache__/web_tools.cpython-310.pyc
|
__pycache__/web_tools.cpython-310.pyc
|
||||||
|
|
|
||||||
21
Dockerfile
21
Dockerfile
|
|
@ -1,24 +1,21 @@
|
||||||
FROM python:3.12-slim
|
FROM nikolaik/python-nodejs:python3.11-nodejs20
|
||||||
|
|
||||||
COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/
|
|
||||||
|
|
||||||
RUN apt-get update && apt-get install -y \
|
RUN apt-get update && apt-get install -y \
|
||||||
curl git docker.io \
|
curl \
|
||||||
|
git \
|
||||||
|
docker.io \
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
ENV UV_PROJECT_ENVIRONMENT=/app/venv \
|
|
||||||
HERMES_HOME=/app/hermes_data \
|
|
||||||
PYTHONUNBUFFERED=1 \
|
|
||||||
PATH="/app/venv/bin:$PATH"
|
|
||||||
|
|
||||||
RUN mkdir -p /app/hermes_code /app/hermes_data
|
RUN mkdir -p /app/hermes_code /app/hermes_data
|
||||||
|
|
||||||
WORKDIR /app/hermes_code
|
WORKDIR /app/hermes_code
|
||||||
|
|
||||||
COPY pyproject.toml uv.lock ./
|
COPY pyproject.toml requirements.txt* ./
|
||||||
RUN uv sync --frozen --no-install-project --extra tg
|
|
||||||
|
RUN pip install --no-cache-dir browser-use playwright python-telegram-bot
|
||||||
|
|
||||||
COPY . .
|
COPY . .
|
||||||
|
|
||||||
RUN uv sync --frozen --extra tg
|
RUN pip install -e .
|
||||||
|
|
||||||
CMD ["python", "-m", "gateway.run"]
|
CMD ["python", "-m", "gateway.run"]
|
||||||
|
|
|
||||||
|
|
@ -1,27 +0,0 @@
|
||||||
FROM debian:bookworm-slim
|
|
||||||
|
|
||||||
ENV DEBIAN_FRONTEND=noninteractive
|
|
||||||
|
|
||||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
|
||||||
chromium \
|
|
||||||
xvfb \
|
|
||||||
fluxbox \
|
|
||||||
x11vnc \
|
|
||||||
novnc \
|
|
||||||
websockify \
|
|
||||||
dbus-x11 \
|
|
||||||
socat \
|
|
||||||
procps \
|
|
||||||
curl \
|
|
||||||
ca-certificates \
|
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
|
||||||
|
|
||||||
WORKDIR /src
|
|
||||||
RUN mkdir -p /src/browser_data
|
|
||||||
|
|
||||||
COPY entrypoint.sh /entrypoint.sh
|
|
||||||
RUN chmod +x /entrypoint.sh
|
|
||||||
|
|
||||||
EXPOSE 6080 9222
|
|
||||||
|
|
||||||
ENTRYPOINT ["/entrypoint.sh"]
|
|
||||||
|
|
@ -1,39 +0,0 @@
|
||||||
export DISPLAY=:99
|
|
||||||
|
|
||||||
mkdir -p /var/run/dbus
|
|
||||||
dbus-uuidgen > /var/lib/dbus/machine-id
|
|
||||||
dbus-daemon --config-file=/usr/share/dbus-1/system.conf --print-address &
|
|
||||||
|
|
||||||
Xvfb :99 -screen 0 1280x720x16 -ac +extension GLX +render -noreset &
|
|
||||||
sleep 2
|
|
||||||
|
|
||||||
fluxbox &
|
|
||||||
x11vnc -display :99 -nopw -listen 0.0.0.0 -xkb -forever -shared &
|
|
||||||
websockify --web=/usr/share/novnc/ 6080 localhost:5900 &
|
|
||||||
|
|
||||||
socat TCP-LISTEN:9222,fork,reuseaddr TCP:127.0.0.1:9223 &
|
|
||||||
|
|
||||||
echo "--- Запуск Chromium в режиме Local-Only (Port 9223) ---"
|
|
||||||
|
|
||||||
while true; do
|
|
||||||
rm -f /src/browser_data/SingletonLock
|
|
||||||
|
|
||||||
chromium \
|
|
||||||
--no-sandbox \
|
|
||||||
--disable-dev-shm-usage \
|
|
||||||
--remote-debugging-port=9223 \
|
|
||||||
--remote-debugging-address=127.0.0.1 \
|
|
||||||
--remote-allow-origins=* \
|
|
||||||
--window-size=1280,720 \
|
|
||||||
--user-data-dir=/src/browser_data \
|
|
||||||
--disable-blink-features=AutomationControlled \
|
|
||||||
--no-first-run \
|
|
||||||
--disable-gpu \
|
|
||||||
--mute-audio \
|
|
||||||
--no-default-browser-check \
|
|
||||||
--disable-software-rasterizer \
|
|
||||||
--disable-features=site-per-process
|
|
||||||
|
|
||||||
echo "Chromium упал или был закрыт агентом, рестарт через 2 секунды..."
|
|
||||||
sleep 2
|
|
||||||
done
|
|
||||||
|
|
@ -1,4 +1,22 @@
|
||||||
services:
|
services:
|
||||||
|
browser:
|
||||||
|
image: browserless/chrome:latest
|
||||||
|
container_name: hermes_browser
|
||||||
|
ports:
|
||||||
|
- "3000:3000"
|
||||||
|
environment:
|
||||||
|
- MAX_CONCURRENT_SESSIONS=5
|
||||||
|
- SCREEN_WIDTH=1280
|
||||||
|
- SCREEN_HEIGHT=720
|
||||||
|
- ENABLE_DEBUGGER=true
|
||||||
|
restart: always
|
||||||
|
networks:
|
||||||
|
- hermes-net
|
||||||
|
deploy:
|
||||||
|
resources:
|
||||||
|
limits:
|
||||||
|
memory: 2G
|
||||||
|
|
||||||
agent:
|
agent:
|
||||||
build: .
|
build: .
|
||||||
container_name: hermes-brain
|
container_name: hermes-brain
|
||||||
|
|
@ -6,7 +24,7 @@ services:
|
||||||
- .env
|
- .env
|
||||||
volumes:
|
volumes:
|
||||||
- .:/app/hermes_code:ro
|
- .:/app/hermes_code:ro
|
||||||
|
|
||||||
- ${HERMES_DATA_PATH}/config.yaml:/app/hermes_data/config.yaml:ro
|
- ${HERMES_DATA_PATH}/config.yaml:/app/hermes_data/config.yaml:ro
|
||||||
- ${HERMES_DATA_PATH}/SOUL.md:/app/hermes_data/SOUL.md:ro
|
- ${HERMES_DATA_PATH}/SOUL.md:/app/hermes_data/SOUL.md:ro
|
||||||
- ./.env:/app/hermes_data/.env:ro
|
- ./.env:/app/hermes_data/.env:ro
|
||||||
|
|
@ -19,7 +37,7 @@ services:
|
||||||
- ${HERMES_DATA_PATH}/memories:/app/hermes_data/memories:rw
|
- ${HERMES_DATA_PATH}/memories:/app/hermes_data/memories:rw
|
||||||
- ${HERMES_WORKSPACE_PATH}/hermes:/app/hermes_data/workspace:rw
|
- ${HERMES_WORKSPACE_PATH}/hermes:/app/hermes_data/workspace:rw
|
||||||
environment:
|
environment:
|
||||||
- BROWSER_URL=http://browser:9222
|
- BROWSER_URL=ws://browser:3000
|
||||||
depends_on:
|
depends_on:
|
||||||
- browser
|
- browser
|
||||||
stdin_open: true
|
stdin_open: true
|
||||||
|
|
@ -32,31 +50,6 @@ services:
|
||||||
limits:
|
limits:
|
||||||
memory: 1.5G
|
memory: 1.5G
|
||||||
|
|
||||||
browser:
|
|
||||||
build:
|
|
||||||
context: ./browser_env
|
|
||||||
dockerfile: Dockerfile.browser
|
|
||||||
container_name: hermes-browser
|
|
||||||
networks:
|
|
||||||
hermes-net:
|
|
||||||
aliases:
|
|
||||||
- browser
|
|
||||||
shm_size: '2gb'
|
|
||||||
volumes:
|
|
||||||
- browser_profiles:/src/browser_data
|
|
||||||
restart: always
|
|
||||||
|
|
||||||
tunnel:
|
|
||||||
image: cloudflare/cloudflared:latest
|
|
||||||
container_name: hermes-tunnel
|
|
||||||
restart: always
|
|
||||||
command: tunnel --url http://browser:6080
|
|
||||||
networks:
|
|
||||||
- hermes-net
|
|
||||||
|
|
||||||
volumes:
|
|
||||||
browser_profiles:
|
|
||||||
|
|
||||||
networks:
|
networks:
|
||||||
hermes-net:
|
hermes-net:
|
||||||
driver: bridge
|
driver: bridge
|
||||||
|
|
|
||||||
|
|
@ -144,7 +144,7 @@ def _discover_tools():
|
||||||
"tools.image_generation_tool",
|
"tools.image_generation_tool",
|
||||||
"tools.skills_tool",
|
"tools.skills_tool",
|
||||||
"tools.skill_manager_tool",
|
"tools.skill_manager_tool",
|
||||||
# "tools.browser_tool",
|
"tools.browser_tool",
|
||||||
"tools.cronjob_tools",
|
"tools.cronjob_tools",
|
||||||
"tools.rl_training_tool",
|
"tools.rl_training_tool",
|
||||||
"tools.tts_tool",
|
"tools.tts_tool",
|
||||||
|
|
@ -158,7 +158,6 @@ def _discover_tools():
|
||||||
"tools.send_message_tool",
|
"tools.send_message_tool",
|
||||||
"tools.honcho_tools",
|
"tools.honcho_tools",
|
||||||
"tools.homeassistant_tool",
|
"tools.homeassistant_tool",
|
||||||
"tools.browser_use_tool"
|
|
||||||
]
|
]
|
||||||
import importlib
|
import importlib
|
||||||
for mod_name in _modules:
|
for mod_name in _modules:
|
||||||
|
|
|
||||||
|
|
@ -34,17 +34,12 @@ dependencies = [
|
||||||
"faster-whisper>=1.0.0,<2",
|
"faster-whisper>=1.0.0,<2",
|
||||||
# Skills Hub (GitHub App JWT auth — optional, only needed for bot identity)
|
# Skills Hub (GitHub App JWT auth — optional, only needed for bot identity)
|
||||||
"PyJWT[crypto]>=2.10.1,<3",
|
"PyJWT[crypto]>=2.10.1,<3",
|
||||||
"browser-use>=0.12.5",
|
|
||||||
"playwright>=1.49.0",
|
|
||||||
"playwright-stealth>=1.0.6",
|
|
||||||
"langchain-openai>=1.1.12",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[project.optional-dependencies]
|
[project.optional-dependencies]
|
||||||
modal = ["swe-rex[modal]>=1.4.0,<2"]
|
modal = ["swe-rex[modal]>=1.4.0,<2"]
|
||||||
daytona = ["daytona>=0.148.0,<1"]
|
daytona = ["daytona>=0.148.0,<1"]
|
||||||
dev = ["pytest>=9.0.2,<10", "pytest-asyncio>=1.3.0,<2", "pytest-xdist>=3.0,<4", "mcp>=1.2.0,<2"]
|
dev = ["pytest>=9.0.2,<10", "pytest-asyncio>=1.3.0,<2", "pytest-xdist>=3.0,<4", "mcp>=1.2.0,<2"]
|
||||||
tg = ["python-telegram-bot>=22.6,<23", "aiohttp>=3.13.3,<4"]
|
|
||||||
messaging = ["python-telegram-bot>=22.6,<23", "discord.py[voice]>=2.7.1,<3", "aiohttp>=3.13.3,<4", "slack-bolt>=1.18.0,<2", "slack-sdk>=3.27.0,<4"]
|
messaging = ["python-telegram-bot>=22.6,<23", "discord.py[voice]>=2.7.1,<3", "aiohttp>=3.13.3,<4", "slack-bolt>=1.18.0,<2", "slack-sdk>=3.27.0,<4"]
|
||||||
cron = ["croniter>=6.0.0,<7"]
|
cron = ["croniter>=6.0.0,<7"]
|
||||||
slack = ["slack-bolt>=1.18.0,<2", "slack-sdk>=3.27.0,<4"]
|
slack = ["slack-bolt>=1.18.0,<2", "slack-sdk>=3.27.0,<4"]
|
||||||
|
|
|
||||||
|
|
@ -19,9 +19,6 @@ PyJWT[crypto]
|
||||||
# Web tools
|
# Web tools
|
||||||
firecrawl-py
|
firecrawl-py
|
||||||
parallel-web>=0.4.2
|
parallel-web>=0.4.2
|
||||||
browser-use>=0.12.5
|
|
||||||
playwright
|
|
||||||
playwright-stealth
|
|
||||||
|
|
||||||
# Image generation
|
# Image generation
|
||||||
fal-client
|
fal-client
|
||||||
|
|
@ -36,5 +33,3 @@ croniter
|
||||||
python-telegram-bot>=20.0
|
python-telegram-bot>=20.0
|
||||||
discord.py>=2.0
|
discord.py>=2.0
|
||||||
aiohttp>=3.9.0
|
aiohttp>=3.9.0
|
||||||
langchain-openai>=1.1.12,
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -64,27 +64,23 @@ from .skill_manager_tool import (
|
||||||
)
|
)
|
||||||
|
|
||||||
# Browser automation tools (agent-browser + Browserbase)
|
# Browser automation tools (agent-browser + Browserbase)
|
||||||
# from .browser_tool import (
|
from .browser_tool import (
|
||||||
# browser_navigate,
|
browser_navigate,
|
||||||
# browser_snapshot,
|
browser_snapshot,
|
||||||
# browser_click,
|
browser_click,
|
||||||
# browser_type,
|
browser_type,
|
||||||
# browser_scroll,
|
browser_scroll,
|
||||||
# browser_back,
|
browser_back,
|
||||||
# browser_press,
|
browser_press,
|
||||||
# browser_close,
|
browser_close,
|
||||||
# browser_get_images,
|
browser_get_images,
|
||||||
# browser_vision,
|
browser_vision,
|
||||||
# cleanup_browser,
|
cleanup_browser,
|
||||||
# cleanup_all_browsers,
|
cleanup_all_browsers,
|
||||||
# get_active_browser_sessions,
|
get_active_browser_sessions,
|
||||||
# check_browser_requirements,
|
check_browser_requirements,
|
||||||
# BROWSER_TOOL_SCHEMAS
|
BROWSER_TOOL_SCHEMAS
|
||||||
# )
|
)
|
||||||
|
|
||||||
from .browser_use_tool import run_browser_task
|
|
||||||
|
|
||||||
from .browser_tool import cleanup_browser, cleanup_all_browsers
|
|
||||||
|
|
||||||
# Cronjob management tools (CLI-only, hermes-cli toolset)
|
# Cronjob management tools (CLI-only, hermes-cli toolset)
|
||||||
from .cronjob_tools import (
|
from .cronjob_tools import (
|
||||||
|
|
|
||||||
|
|
@ -298,32 +298,30 @@ _cleanup_lock = threading.Lock()
|
||||||
|
|
||||||
|
|
||||||
def _emergency_cleanup_all_sessions():
|
def _emergency_cleanup_all_sessions():
|
||||||
# """
|
"""
|
||||||
# Emergency cleanup of all active browser sessions.
|
Emergency cleanup of all active browser sessions.
|
||||||
# Called on process exit or interrupt to prevent orphaned sessions.
|
Called on process exit or interrupt to prevent orphaned sessions.
|
||||||
# """
|
"""
|
||||||
# global _cleanup_done
|
global _cleanup_done
|
||||||
# if _cleanup_done:
|
if _cleanup_done:
|
||||||
# return
|
return
|
||||||
# _cleanup_done = True
|
_cleanup_done = True
|
||||||
|
|
||||||
# if not _active_sessions:
|
if not _active_sessions:
|
||||||
# return
|
return
|
||||||
|
|
||||||
# logger.info("Emergency cleanup: closing %s active session(s)...",
|
logger.info("Emergency cleanup: closing %s active session(s)...",
|
||||||
# len(_active_sessions))
|
len(_active_sessions))
|
||||||
|
|
||||||
# try:
|
try:
|
||||||
# cleanup_all_browsers()
|
cleanup_all_browsers()
|
||||||
# except Exception as e:
|
except Exception as e:
|
||||||
# logger.error("Emergency cleanup error: %s", e)
|
logger.error("Emergency cleanup error: %s", e)
|
||||||
# finally:
|
finally:
|
||||||
# with _cleanup_lock:
|
with _cleanup_lock:
|
||||||
# _active_sessions.clear()
|
_active_sessions.clear()
|
||||||
# _session_last_activity.clear()
|
_session_last_activity.clear()
|
||||||
# _recording_sessions.clear()
|
_recording_sessions.clear()
|
||||||
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
# Register cleanup via atexit only. Previous versions installed SIGINT/SIGTERM
|
# Register cleanup via atexit only. Previous versions installed SIGINT/SIGTERM
|
||||||
|
|
@ -1659,89 +1657,85 @@ def _cleanup_old_recordings(max_age_hours=72):
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
|
|
||||||
def cleanup_browser(task_id: Optional[str] = None) -> None:
|
def cleanup_browser(task_id: Optional[str] = None) -> None:
|
||||||
# """
|
"""
|
||||||
# Clean up browser session for a task.
|
Clean up browser session for a task.
|
||||||
|
|
||||||
# Called automatically when a task completes or when inactivity timeout is reached.
|
Called automatically when a task completes or when inactivity timeout is reached.
|
||||||
# Closes both the agent-browser session and the Browserbase session.
|
Closes both the agent-browser session and the Browserbase session.
|
||||||
|
|
||||||
# Args:
|
Args:
|
||||||
# task_id: Task identifier to clean up
|
task_id: Task identifier to clean up
|
||||||
# """
|
"""
|
||||||
# if task_id is None:
|
if task_id is None:
|
||||||
# task_id = "default"
|
task_id = "default"
|
||||||
|
|
||||||
# logger.debug("cleanup_browser called for task_id: %s", task_id)
|
logger.debug("cleanup_browser called for task_id: %s", task_id)
|
||||||
# logger.debug("Active sessions: %s", list(_active_sessions.keys()))
|
logger.debug("Active sessions: %s", list(_active_sessions.keys()))
|
||||||
|
|
||||||
# # Check if session exists (under lock), but don't remove yet -
|
# Check if session exists (under lock), but don't remove yet -
|
||||||
# # _run_browser_command needs it to build the close command.
|
# _run_browser_command needs it to build the close command.
|
||||||
# with _cleanup_lock:
|
with _cleanup_lock:
|
||||||
# session_info = _active_sessions.get(task_id)
|
session_info = _active_sessions.get(task_id)
|
||||||
|
|
||||||
# if session_info:
|
if session_info:
|
||||||
# bb_session_id = session_info.get("bb_session_id", "unknown")
|
bb_session_id = session_info.get("bb_session_id", "unknown")
|
||||||
# logger.debug("Found session for task %s: bb_session_id=%s", task_id, bb_session_id)
|
logger.debug("Found session for task %s: bb_session_id=%s", task_id, bb_session_id)
|
||||||
|
|
||||||
# # Stop auto-recording before closing (saves the file)
|
# Stop auto-recording before closing (saves the file)
|
||||||
# _maybe_stop_recording(task_id)
|
_maybe_stop_recording(task_id)
|
||||||
|
|
||||||
# # Try to close via agent-browser first (needs session in _active_sessions)
|
# Try to close via agent-browser first (needs session in _active_sessions)
|
||||||
# try:
|
try:
|
||||||
# _run_browser_command(task_id, "close", [], timeout=10)
|
_run_browser_command(task_id, "close", [], timeout=10)
|
||||||
# logger.debug("agent-browser close command completed for task %s", task_id)
|
logger.debug("agent-browser close command completed for task %s", task_id)
|
||||||
# except Exception as e:
|
except Exception as e:
|
||||||
# logger.warning("agent-browser close failed for task %s: %s", task_id, e)
|
logger.warning("agent-browser close failed for task %s: %s", task_id, e)
|
||||||
|
|
||||||
# # Now remove from tracking under lock
|
# Now remove from tracking under lock
|
||||||
# with _cleanup_lock:
|
with _cleanup_lock:
|
||||||
# _active_sessions.pop(task_id, None)
|
_active_sessions.pop(task_id, None)
|
||||||
# _session_last_activity.pop(task_id, None)
|
_session_last_activity.pop(task_id, None)
|
||||||
|
|
||||||
# # Cloud mode: close the cloud browser session via provider API
|
# Cloud mode: close the cloud browser session via provider API
|
||||||
# if bb_session_id:
|
if bb_session_id:
|
||||||
# provider = _get_cloud_provider()
|
provider = _get_cloud_provider()
|
||||||
# if provider is not None:
|
if provider is not None:
|
||||||
# try:
|
try:
|
||||||
# provider.close_session(bb_session_id)
|
provider.close_session(bb_session_id)
|
||||||
# except Exception as e:
|
except Exception as e:
|
||||||
# logger.warning("Could not close cloud browser session: %s", e)
|
logger.warning("Could not close cloud browser session: %s", e)
|
||||||
|
|
||||||
# # Kill the daemon process and clean up socket directory
|
# Kill the daemon process and clean up socket directory
|
||||||
# session_name = session_info.get("session_name", "")
|
session_name = session_info.get("session_name", "")
|
||||||
# if session_name:
|
if session_name:
|
||||||
# socket_dir = os.path.join(_socket_safe_tmpdir(), f"agent-browser-{session_name}")
|
socket_dir = os.path.join(_socket_safe_tmpdir(), f"agent-browser-{session_name}")
|
||||||
# if os.path.exists(socket_dir):
|
if os.path.exists(socket_dir):
|
||||||
# # agent-browser writes {session}.pid in the socket dir
|
# agent-browser writes {session}.pid in the socket dir
|
||||||
# pid_file = os.path.join(socket_dir, f"{session_name}.pid")
|
pid_file = os.path.join(socket_dir, f"{session_name}.pid")
|
||||||
# if os.path.isfile(pid_file):
|
if os.path.isfile(pid_file):
|
||||||
# try:
|
try:
|
||||||
# daemon_pid = int(Path(pid_file).read_text().strip())
|
daemon_pid = int(Path(pid_file).read_text().strip())
|
||||||
# os.kill(daemon_pid, signal.SIGTERM)
|
os.kill(daemon_pid, signal.SIGTERM)
|
||||||
# logger.debug("Killed daemon pid %s for %s", daemon_pid, session_name)
|
logger.debug("Killed daemon pid %s for %s", daemon_pid, session_name)
|
||||||
# except (ProcessLookupError, ValueError, PermissionError, OSError):
|
except (ProcessLookupError, ValueError, PermissionError, OSError):
|
||||||
# logger.debug("Could not kill daemon pid for %s (already dead or inaccessible)", session_name)
|
logger.debug("Could not kill daemon pid for %s (already dead or inaccessible)", session_name)
|
||||||
# shutil.rmtree(socket_dir, ignore_errors=True)
|
shutil.rmtree(socket_dir, ignore_errors=True)
|
||||||
|
|
||||||
# logger.debug("Removed task %s from active sessions", task_id)
|
logger.debug("Removed task %s from active sessions", task_id)
|
||||||
# else:
|
else:
|
||||||
# logger.debug("No active session found for task_id: %s", task_id)
|
logger.debug("No active session found for task_id: %s", task_id)
|
||||||
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
def cleanup_all_browsers() -> None:
|
def cleanup_all_browsers() -> None:
|
||||||
# """
|
"""
|
||||||
# Clean up all active browser sessions.
|
Clean up all active browser sessions.
|
||||||
|
|
||||||
# Useful for cleanup on shutdown.
|
Useful for cleanup on shutdown.
|
||||||
# """
|
"""
|
||||||
# with _cleanup_lock:
|
with _cleanup_lock:
|
||||||
# task_ids = list(_active_sessions.keys())
|
task_ids = list(_active_sessions.keys())
|
||||||
# for task_id in task_ids:
|
for task_id in task_ids:
|
||||||
# cleanup_browser(task_id)
|
cleanup_browser(task_id)
|
||||||
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
def get_active_browser_sessions() -> Dict[str, Dict[str, str]]:
|
def get_active_browser_sessions() -> Dict[str, Dict[str, str]]:
|
||||||
|
|
@ -1828,96 +1822,96 @@ if __name__ == "__main__":
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# Registry
|
# Registry
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# from tools.registry import registry
|
from tools.registry import registry
|
||||||
|
|
||||||
# _BROWSER_SCHEMA_MAP = {s["name"]: s for s in BROWSER_TOOL_SCHEMAS}
|
_BROWSER_SCHEMA_MAP = {s["name"]: s for s in BROWSER_TOOL_SCHEMAS}
|
||||||
|
|
||||||
# registry.register(
|
registry.register(
|
||||||
# name="browser_navigate",
|
name="browser_navigate",
|
||||||
# toolset="browser",
|
toolset="browser",
|
||||||
# schema=_BROWSER_SCHEMA_MAP["browser_navigate"],
|
schema=_BROWSER_SCHEMA_MAP["browser_navigate"],
|
||||||
# handler=lambda args, **kw: browser_navigate(url=args.get("url", ""), task_id=kw.get("task_id")),
|
handler=lambda args, **kw: browser_navigate(url=args.get("url", ""), task_id=kw.get("task_id")),
|
||||||
# check_fn=check_browser_requirements,
|
check_fn=check_browser_requirements,
|
||||||
# emoji="🌐",
|
emoji="🌐",
|
||||||
# )
|
)
|
||||||
# registry.register(
|
registry.register(
|
||||||
# name="browser_snapshot",
|
name="browser_snapshot",
|
||||||
# toolset="browser",
|
toolset="browser",
|
||||||
# schema=_BROWSER_SCHEMA_MAP["browser_snapshot"],
|
schema=_BROWSER_SCHEMA_MAP["browser_snapshot"],
|
||||||
# handler=lambda args, **kw: browser_snapshot(
|
handler=lambda args, **kw: browser_snapshot(
|
||||||
# full=args.get("full", False), task_id=kw.get("task_id"), user_task=kw.get("user_task")),
|
full=args.get("full", False), task_id=kw.get("task_id"), user_task=kw.get("user_task")),
|
||||||
# check_fn=check_browser_requirements,
|
check_fn=check_browser_requirements,
|
||||||
# emoji="📸",
|
emoji="📸",
|
||||||
# )
|
)
|
||||||
# registry.register(
|
registry.register(
|
||||||
# name="browser_click",
|
name="browser_click",
|
||||||
# toolset="browser",
|
toolset="browser",
|
||||||
# schema=_BROWSER_SCHEMA_MAP["browser_click"],
|
schema=_BROWSER_SCHEMA_MAP["browser_click"],
|
||||||
# handler=lambda args, **kw: browser_click(ref=args.get("ref", ""), task_id=kw.get("task_id")),
|
handler=lambda args, **kw: browser_click(ref=args.get("ref", ""), task_id=kw.get("task_id")),
|
||||||
# check_fn=check_browser_requirements,
|
check_fn=check_browser_requirements,
|
||||||
# emoji="👆",
|
emoji="👆",
|
||||||
# )
|
)
|
||||||
# registry.register(
|
registry.register(
|
||||||
# name="browser_type",
|
name="browser_type",
|
||||||
# toolset="browser",
|
toolset="browser",
|
||||||
# schema=_BROWSER_SCHEMA_MAP["browser_type"],
|
schema=_BROWSER_SCHEMA_MAP["browser_type"],
|
||||||
# handler=lambda args, **kw: browser_type(ref=args.get("ref", ""), text=args.get("text", ""), task_id=kw.get("task_id")),
|
handler=lambda args, **kw: browser_type(ref=args.get("ref", ""), text=args.get("text", ""), task_id=kw.get("task_id")),
|
||||||
# check_fn=check_browser_requirements,
|
check_fn=check_browser_requirements,
|
||||||
# emoji="⌨️",
|
emoji="⌨️",
|
||||||
# )
|
)
|
||||||
# registry.register(
|
registry.register(
|
||||||
# name="browser_scroll",
|
name="browser_scroll",
|
||||||
# toolset="browser",
|
toolset="browser",
|
||||||
# schema=_BROWSER_SCHEMA_MAP["browser_scroll"],
|
schema=_BROWSER_SCHEMA_MAP["browser_scroll"],
|
||||||
# handler=lambda args, **kw: browser_scroll(direction=args.get("direction", "down"), task_id=kw.get("task_id")),
|
handler=lambda args, **kw: browser_scroll(direction=args.get("direction", "down"), task_id=kw.get("task_id")),
|
||||||
# check_fn=check_browser_requirements,
|
check_fn=check_browser_requirements,
|
||||||
# emoji="📜",
|
emoji="📜",
|
||||||
# )
|
)
|
||||||
# registry.register(
|
registry.register(
|
||||||
# name="browser_back",
|
name="browser_back",
|
||||||
# toolset="browser",
|
toolset="browser",
|
||||||
# schema=_BROWSER_SCHEMA_MAP["browser_back"],
|
schema=_BROWSER_SCHEMA_MAP["browser_back"],
|
||||||
# handler=lambda args, **kw: browser_back(task_id=kw.get("task_id")),
|
handler=lambda args, **kw: browser_back(task_id=kw.get("task_id")),
|
||||||
# check_fn=check_browser_requirements,
|
check_fn=check_browser_requirements,
|
||||||
# emoji="◀️",
|
emoji="◀️",
|
||||||
# )
|
)
|
||||||
# registry.register(
|
registry.register(
|
||||||
# name="browser_press",
|
name="browser_press",
|
||||||
# toolset="browser",
|
toolset="browser",
|
||||||
# schema=_BROWSER_SCHEMA_MAP["browser_press"],
|
schema=_BROWSER_SCHEMA_MAP["browser_press"],
|
||||||
# handler=lambda args, **kw: browser_press(key=args.get("key", ""), task_id=kw.get("task_id")),
|
handler=lambda args, **kw: browser_press(key=args.get("key", ""), task_id=kw.get("task_id")),
|
||||||
# check_fn=check_browser_requirements,
|
check_fn=check_browser_requirements,
|
||||||
# emoji="⌨️",
|
emoji="⌨️",
|
||||||
# )
|
)
|
||||||
# registry.register(
|
registry.register(
|
||||||
# name="browser_close",
|
name="browser_close",
|
||||||
# toolset="browser",
|
toolset="browser",
|
||||||
# schema=_BROWSER_SCHEMA_MAP["browser_close"],
|
schema=_BROWSER_SCHEMA_MAP["browser_close"],
|
||||||
# handler=lambda args, **kw: browser_close(task_id=kw.get("task_id")),
|
handler=lambda args, **kw: browser_close(task_id=kw.get("task_id")),
|
||||||
# check_fn=check_browser_requirements,
|
check_fn=check_browser_requirements,
|
||||||
# emoji="🚪",
|
emoji="🚪",
|
||||||
# )
|
)
|
||||||
# registry.register(
|
registry.register(
|
||||||
# name="browser_get_images",
|
name="browser_get_images",
|
||||||
# toolset="browser",
|
toolset="browser",
|
||||||
# schema=_BROWSER_SCHEMA_MAP["browser_get_images"],
|
schema=_BROWSER_SCHEMA_MAP["browser_get_images"],
|
||||||
# handler=lambda args, **kw: browser_get_images(task_id=kw.get("task_id")),
|
handler=lambda args, **kw: browser_get_images(task_id=kw.get("task_id")),
|
||||||
# check_fn=check_browser_requirements,
|
check_fn=check_browser_requirements,
|
||||||
# emoji="🖼️",
|
emoji="🖼️",
|
||||||
# )
|
)
|
||||||
# registry.register(
|
registry.register(
|
||||||
# name="browser_vision",
|
name="browser_vision",
|
||||||
# toolset="browser",
|
toolset="browser",
|
||||||
# schema=_BROWSER_SCHEMA_MAP["browser_vision"],
|
schema=_BROWSER_SCHEMA_MAP["browser_vision"],
|
||||||
# handler=lambda args, **kw: browser_vision(question=args.get("question", ""), annotate=args.get("annotate", False), task_id=kw.get("task_id")),
|
handler=lambda args, **kw: browser_vision(question=args.get("question", ""), annotate=args.get("annotate", False), task_id=kw.get("task_id")),
|
||||||
# check_fn=check_browser_requirements,
|
check_fn=check_browser_requirements,
|
||||||
# emoji="👁️",
|
emoji="👁️",
|
||||||
# )
|
)
|
||||||
# registry.register(
|
registry.register(
|
||||||
# name="browser_console",
|
name="browser_console",
|
||||||
# toolset="browser",
|
toolset="browser",
|
||||||
# schema=_BROWSER_SCHEMA_MAP["browser_console"],
|
schema=_BROWSER_SCHEMA_MAP["browser_console"],
|
||||||
# handler=lambda args, **kw: browser_console(clear=args.get("clear", False), task_id=kw.get("task_id")),
|
handler=lambda args, **kw: browser_console(clear=args.get("clear", False), task_id=kw.get("task_id")),
|
||||||
# check_fn=check_browser_requirements,
|
check_fn=check_browser_requirements,
|
||||||
# emoji="🖥️",
|
emoji="🖥️",
|
||||||
# )
|
)
|
||||||
|
|
|
||||||
|
|
@ -1,86 +0,0 @@
|
||||||
import json
|
|
||||||
import os
|
|
||||||
import asyncio
|
|
||||||
import socket
|
|
||||||
from browser_use import Agent, Browser, ChatOpenAI
|
|
||||||
from tools.registry import registry
|
|
||||||
|
|
||||||
|
|
||||||
async def run_browser_task(task):
|
|
||||||
browser_host = "browser"
|
|
||||||
browser_port = 9222
|
|
||||||
BROWSER_VIEW_URL = os.getenv("BROWSER_VIEW_URL", "")
|
|
||||||
|
|
||||||
try:
|
|
||||||
browser_ip = socket.gethostbyname(browser_host)
|
|
||||||
cdp_url = f"http://{browser_ip}:{browser_port}"
|
|
||||||
except Exception:
|
|
||||||
cdp_url = f"http://{browser_host}:{browser_port}"
|
|
||||||
|
|
||||||
browser = Browser(cdp_url=cdp_url)
|
|
||||||
|
|
||||||
llm = ChatOpenAI(
|
|
||||||
model=os.getenv("MODEL_DEFAULT", "qwen3.5-122b"),
|
|
||||||
api_key=os.getenv("OPENAI_API_KEY"),
|
|
||||||
base_url=os.getenv("OPENAI_BASE_URL"),
|
|
||||||
temperature=0.0,
|
|
||||||
)
|
|
||||||
|
|
||||||
agent = Agent(
|
|
||||||
task=task,
|
|
||||||
llm=llm,
|
|
||||||
browser=browser,
|
|
||||||
use_vision=False
|
|
||||||
)
|
|
||||||
|
|
||||||
try:
|
|
||||||
history = await agent.run()
|
|
||||||
final_result = history.final_result()
|
|
||||||
|
|
||||||
response = {
|
|
||||||
"success": True,
|
|
||||||
"result": final_result,
|
|
||||||
"browser_view": BROWSER_VIEW_URL
|
|
||||||
}
|
|
||||||
return json.dumps(response, ensure_ascii=False)
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
return json.dumps({
|
|
||||||
"success": False,
|
|
||||||
"error": f"Browser automation failed: {str(e)}"
|
|
||||||
}, ensure_ascii=False)
|
|
||||||
|
|
||||||
finally:
|
|
||||||
if browser:
|
|
||||||
try:
|
|
||||||
await browser.close()
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
registry.register(
|
|
||||||
name="internet_browser",
|
|
||||||
toolset="browse_cmd",
|
|
||||||
schema={
|
|
||||||
"name": "internet_browser",
|
|
||||||
"description": (
|
|
||||||
"ГЛАВНЫЙ ИНСТРУМЕНТ ДЛЯ ВЕБ-СЕРФИНГА. Вызывай этот инструмент НАПРЯМУЮ (через стандартный tool call/function call). "
|
|
||||||
"КАТЕГОРИЧЕСКИ ЗАПРЕЩЕНО использовать `execute_code` или `delegate_task` для работы с браузером. "
|
|
||||||
"Не пиши Python-скрипты! Просто передай в этот инструмент параметр `task`. "
|
|
||||||
"Используй для любых задач в интернете: поиск товаров (Wildberries, Ozon), чтение статей, клики, навигация."
|
|
||||||
),
|
|
||||||
"parameters": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"task": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "Подробная задача на естественном языке. Например: 'Зайди на wildberries.ru, найди черную футболку и верни цену'."
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": ["task"]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
handler=lambda args, **kw: asyncio.run(run_browser_task(args.get("task"))),
|
|
||||||
emoji="🌐",
|
|
||||||
)
|
|
||||||
26
toolsets.py
26
toolsets.py
|
|
@ -29,12 +29,6 @@ from typing import List, Dict, Any, Set, Optional
|
||||||
# Shared tool list for CLI and all messaging platform toolsets.
|
# Shared tool list for CLI and all messaging platform toolsets.
|
||||||
# Edit this once to update all platforms simultaneously.
|
# Edit this once to update all platforms simultaneously.
|
||||||
_HERMES_CORE_TOOLS = [
|
_HERMES_CORE_TOOLS = [
|
||||||
# Browser automation
|
|
||||||
# "browser_navigate", "browser_snapshot", "browser_click",
|
|
||||||
# "browser_type", "browser_scroll", "browser_back",
|
|
||||||
# "browser_press", "browser_close", "browser_get_images",
|
|
||||||
# "browser_vision", "browser_console",
|
|
||||||
"internet_browser",
|
|
||||||
# Web
|
# Web
|
||||||
"web_search", "web_extract",
|
"web_search", "web_extract",
|
||||||
# Terminal + process management
|
# Terminal + process management
|
||||||
|
|
@ -47,6 +41,11 @@ _HERMES_CORE_TOOLS = [
|
||||||
"mixture_of_agents",
|
"mixture_of_agents",
|
||||||
# Skills
|
# Skills
|
||||||
"skills_list", "skill_view", "skill_manage",
|
"skills_list", "skill_view", "skill_manage",
|
||||||
|
# Browser automation
|
||||||
|
"browser_navigate", "browser_snapshot", "browser_click",
|
||||||
|
"browser_type", "browser_scroll", "browser_back",
|
||||||
|
"browser_press", "browser_close", "browser_get_images",
|
||||||
|
"browser_vision", "browser_console",
|
||||||
# Text-to-speech
|
# Text-to-speech
|
||||||
"text_to_speech",
|
"text_to_speech",
|
||||||
# Planning & memory
|
# Planning & memory
|
||||||
|
|
@ -117,20 +116,13 @@ TOOLSETS = {
|
||||||
"browser": {
|
"browser": {
|
||||||
"description": "Browser automation for web interaction (navigate, click, type, scroll, iframes, hold-click) with web search for finding URLs",
|
"description": "Browser automation for web interaction (navigate, click, type, scroll, iframes, hold-click) with web search for finding URLs",
|
||||||
"tools": [
|
"tools": [
|
||||||
# "browser_navigate", "browser_snapshot", "browser_click",
|
"browser_navigate", "browser_snapshot", "browser_click",
|
||||||
# "browser_type", "browser_scroll", "browser_back",
|
"browser_type", "browser_scroll", "browser_back",
|
||||||
# "browser_press", "browser_close", "browser_get_images",
|
"browser_press", "browser_close", "browser_get_images",
|
||||||
# "browser_vision", "browser_console", "web_search"
|
"browser_vision", "browser_console", "web_search"
|
||||||
# "internet_browser"
|
|
||||||
],
|
],
|
||||||
"includes": []
|
"includes": []
|
||||||
},
|
},
|
||||||
|
|
||||||
"browse_cmd": {
|
|
||||||
"description": "Advanced browser automation via browser-use",
|
|
||||||
"tools": ["internet_browser"],
|
|
||||||
"includes": []
|
|
||||||
},
|
|
||||||
|
|
||||||
"cronjob": {
|
"cronjob": {
|
||||||
"description": "Cronjob management tool - create, list, update, pause, resume, remove, and trigger scheduled tasks",
|
"description": "Cronjob management tool - create, list, update, pause, resume, remove, and trigger scheduled tasks",
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue