opus_ctrl+v enable

This commit is contained in:
nafer committed 2026-03-06 12:27:52 +03:00
1 parent f6f4ba4067
commit d379c19b8c
1 file changed
+108 -9
+108 -9
View File
@@ -7,7 +7,7 @@ import asyncio
from typing import Optional from typing import Optional
from fastapi import FastAPI, HTTPException from fastapi import FastAPI, HTTPException
from pydantic import BaseModel, Field from pydantic import BaseModel, Field
from browser_use import Agent, Browser, BrowserProfile, ChatOpenAI from browser_use import Agent, Browser, BrowserProfile, ChatOpenAI, ActionResult
from browser_use.agent.views import AgentHistoryList from browser_use.agent.views import AgentHistoryList
import uvicorn import uvicorn
from fastapi.responses import HTMLResponse from fastapi.responses import HTMLResponse
@@ -27,6 +27,99 @@ import logging
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
# Создаём Tools с кастомными действиями
try:
from browser_use import Tools, BrowserSession
custom_tools = Tools()
@custom_tools.action(description='Paste text from clipboard into the currently focused element using Ctrl+V. Use this instead of regular typing when a website requires a paste event to trigger special behavior (e.g., URL preview generation, link formatting). Parameters: text - the text to paste, index - optional index of element to click/focus first.')
async def paste_text(text: str, index: int = None, browser_session: BrowserSession = None):
"""Вставляет текст через Ctrl+V (эмуляция вставки из буфера обмена)"""
import json as json_util
try:
page = await browser_session.get_current_page()
# Если указан индекс элемента — кликаем на него для фокуса
if index is not None:
dom_element = await browser_session.get_dom_element_by_index(index)
if dom_element:
await browser_session.click_element_node(dom_element)
await asyncio.sleep(0.3)
# Экранируем текст для безопасной вставки в JS
safe_text = json_util.dumps(text)
# Записываем текст в буфер обмена через JavaScript
await page.evaluate(f'''() => {{
const textArea = document.createElement('textarea');
textArea.value = {safe_text};
textArea.style.position = 'fixed';
textArea.style.left = '-9999px';
document.body.appendChild(textArea);
textArea.select();
document.execCommand('copy');
document.body.removeChild(textArea);
}}''')
# Также пробуем через Clipboard API
try:
await page.evaluate(f'''async () => {{
await navigator.clipboard.writeText({safe_text});
}}''')
except Exception:
pass
# Нажимаем Ctrl+V для вставки
await page.keyboard.down('Control')
await page.keyboard.press('v')
await page.keyboard.up('Control')
await asyncio.sleep(0.5)
return ActionResult(
extracted_content=f"Text pasted successfully: {text[:100]}...",
include_in_memory=True
)
except Exception as e:
# Fallback: эмулируем paste event через JavaScript
try:
page = await browser_session.get_current_page()
safe_text = json_util.dumps(text)
await page.evaluate(f'''() => {{
const el = document.activeElement;
if (el) {{
const clipboardData = new DataTransfer();
clipboardData.setData('text/plain', {safe_text});
const pasteEvent = new ClipboardEvent('paste', {{
bubbles: true,
cancelable: true,
clipboardData: clipboardData
}});
el.dispatchEvent(pasteEvent);
if ('value' in el) {{
el.value = {safe_text};
el.dispatchEvent(new Event('input', {{ bubbles: true }}));
el.dispatchEvent(new Event('change', {{ bubbles: true }}));
}} else if (el.isContentEditable) {{
document.execCommand('insertText', false, {safe_text});
}}
}}
}}''')
return ActionResult(
extracted_content=f"Text pasted via fallback: {text[:100]}...",
include_in_memory=True
)
except Exception as e2:
return ActionResult(error=f"Failed to paste text: {e2}")
logger.info("Custom tools (paste_text) registered successfully")
except ImportError as e:
custom_tools = None
logger.warning(f"Could not import Tools/BrowserSession from browser_use: {e}. Custom tools disabled.")
def patch_chrome_binary(): def patch_chrome_binary():
"""Патчит бинарный файл Chrome для удаления строки cdc_, которую ищут антифроды""" """Патчит бинарный файл Chrome для удаления строки cdc_, которую ищут антифроды"""
possible_paths = [ possible_paths = [
@@ -224,9 +317,11 @@ async def run_browser_task(task_id: str, task: str, max_steps: int, model: str):
existing_prefs['safebrowsing'] = {} existing_prefs['safebrowsing'] = {}
existing_prefs['safebrowsing']['enabled'] = False existing_prefs['safebrowsing']['enabled'] = False
# Разрешаем cookies для всех сайтов # Разрешаем cookies и clipboard для всех сайтов
existing_prefs['profile']['default_content_setting_values'] = existing_prefs.get('profile', {}).get('default_content_setting_values', {}) if 'default_content_setting_values' not in existing_prefs['profile']:
existing_prefs['profile']['default_content_setting_values'] = {}
existing_prefs['profile']['default_content_setting_values']['cookies'] = 1 # 1 = Allow existing_prefs['profile']['default_content_setting_values']['cookies'] = 1 # 1 = Allow
existing_prefs['profile']['default_content_setting_values']['clipboard'] = 1 # 1 = Allow (для paste_text action)
# Отключаем Tracking Protection # Отключаем Tracking Protection
if 'tracking_protection' not in existing_prefs: if 'tracking_protection' not in existing_prefs:
@@ -413,12 +508,16 @@ async def run_browser_task(task_id: str, task: str, max_steps: int, model: str):
logger.info("[startup] Chrome started successfully and ready for connections.") logger.info("[startup] Chrome started successfully and ready for connections.")
return return
# Создание агента # Создание агента с кастомными действиями (paste_text и др.)
agent = Agent( agent_kwargs = {
task=task, "task": task,
llm=llm, "llm": llm,
browser=browser, "browser": browser,
) }
if custom_tools is not None:
agent_kwargs["tools"] = custom_tools
agent = Agent(**agent_kwargs)
# Выполнение задачи # Выполнение задачи
history: AgentHistoryList = await agent.run(max_steps=max_steps) history: AgentHistoryList = await agent.run(max_steps=max_steps)