opus_ctrl+v enable
This commit is contained in:
1 parent
f6f4ba4067
commit
d379c19b8c
1 file changed
+108
-9
@@ -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)
|
||||||
|
|||||||
Reference in new issue
Block a user