Проблема с загрузкой файла через браузерPython

Программы на Python
Ответить
Anonymous
 Проблема с загрузкой файла через браузер

Сообщение Anonymous »

Я пишу веб-автоматизацию для загрузки некоторых резюме на веб-сайт с помощью инструмента веб-автоматизации, использующего браузер, который использует LLM в качестве агента искусственного интеллекта. Загрузка файлов не поддерживается браузером и является встроенной функцией. Вот почему я написал для этого следующий сложный код, однако иногда он работает, а иногда нет.
например, 5 раз сработало хорошо, а один раз нет. вот журнал, связанный со временем сбоя:

Код: Выделить всё

Received task execution request with payload: {
"prompt": "go to https://joblookup.com/uk/employer/login website, login with test@test.com username and 123123 password. click on the application, download two of the applications by clicking on one download button then terminate the task.",
"task_id": "2",
"is_dynamic": true,
"capsolver": false,
"is_download": true,
"data": {}
}
go to https://joblookup.com/uk/employer/login website, login with test@test.com username and 123123 password. click on the application, download two of the applications by clicking on one download button then terminate the task.
Found matching workflow ID: N/A
INFO:     127.0.0.1:44532 - "POST /api/v1/execute-task HTTP/1.1" 200 OK
Starting automation process...
contexts initial: 0
INFO     [agent] 🚀 Starting task:
Original Task: go to https://joblookup.com/uk/employer/login website, login with test@test.com username and 123123 password. click on the application, download two of the applications by clicking on one download button then terminate the task.

Data:

Please complete the task by following the step-by-step process outlined in the Previous Recording Context as a guide, but always prioritize and use the values provided in the 'Original Task' part.

NOTE: to populate the whole fields you need SCROLL DOWN to see all of them.
Note: if you see any Ad, just close it.
Note: there is no need to fill all fields that are unnecessary or in not into the above automation.

INFO     [agent]
📍 Step 1
contexts after 5 sec: 1
INFO     [agent] 🤷 Eval: Unknown - No previous actions to evaluate.
INFO     [agent] 🧠 Memory: Starting task to log in to joblookup.com with provided credentials.
INFO     [agent] 🎯 Next goal: Navigate to https://joblookup.com/uk/employer/login to begin login process.
INFO     [agent] 🛠️  Action 1/1: {"go_to_url":{"url":"https://joblookup.com/uk/employer/login"}}
INFO     [controller] 🔗  Navigated to https://joblookup.com/uk/employer/login
INFO     [agent]
📍 Step 2
INFO     [agent] 👍 Eval: Success - Navigated to the login page and identified email and password fields.
INFO     [agent] 🧠 Memory: Navigated to the login page. Next, input email and password to log in.
INFO     [agent] 🎯 Next goal: Input email and password, then click the Sign In button.
INFO     [agent] 🛠️  Action 1/3: {"input_text":{"index":14,"text":"test@test.com"}}
INFO     [agent] 🛠️  Action 2/3: {"input_text":{"index":16,"text":"123123"}}
INFO     [agent] 🛠️  Action 3/3: {"click_element":{"index":19}}
INFO     [controller] ⌨️  Input "test@test.com" into index 14
INFO     [controller] ⌨️  Input "123123" into index 16
INFO     [controller] 🖱️  Clicked button with index 19: Sign In
INFO     [agent]
📍 Step 3
INFO     [agent] 👍 Eval: Success - Successfully logged in and reached the dashboard.
INFO     [agent] 🧠 Memory: Logged in and on the Employer Dashboard. Next, navigate to Applications to download applications.
INFO     [agent] 🎯 Next goal: Click on the 'Applications' link to view applications.
INFO     [agent] 🛠️  Action 1/1: {"click_element":{"index":17}}
INFO     [controller] 🖱️  Clicked button with index 17: Applications
INFO     [agent]
📍 Step 4
INFO     [agent] 👍 Eval: Success - Reached the Applications page.
INFO     [agent] 🧠 Memory: On the Applications page, ready to download applications.
INFO     [agent] 🎯 Next goal: Download two applications by clicking on download buttons.
INFO     [agent] 🛠️  Action 1/2: {"click_element":{"index":20}}
INFO     [agent] 🛠️  Action 2/2: {"click_element":{"index":21}}
INFO     [controller] 🖱️  Clicked button with index 20:
INFO     [controller] Something new appeared after action 1 / 2
INFO     [agent]
📍 Step 5
INFO     [agent] 👍 Eval: Partial Success - First application downloaded.  Ready to download the second application.
INFO     [agent] 🧠 Memory: Downloaded one application. Ready to download the second application.
INFO     [agent] 🎯 Next goal: Download the second application by clicking the next download button.
INFO     [agent] 🛠️  Action 1/1: {"click_element":{"index":21}}
INFO     [controller] 🖱️  Clicked button with index 21:
INFO     [agent]
📍 Step 6
INFO     [agent] 👍 Eval: Success - Second application downloaded successfully.
INFO     [agent] 🧠 Memory: Downloaded two applications as required by the task.
INFO     [agent] 🎯 Next goal: Complete the task as the main objective has been achieved.
INFO     [agent] 🛠️  Action 1/1: {"done":{"text":"Successfully logged in to joblookup.com, navigated to the Applications page, and downloaded two applications as required."}}
INFO     [agent] 📄 Result: Successfully logged in to joblookup.com, navigated to the Applications page, and downloaded two applications as required.
INFO     [agent] ✅ Task completed successfully
INFO     [agent] Created GIF at agent_history.gif
is_done=True extracted_content='Successfully logged in to joblookup.com, navigated to the Applications page, and downloaded two applications as required.' error=None include_in_memory=False

No files were downloaded during this session.
Automation result: {'result': 'Successfully logged in to joblookup.com, navigated to the Applications page, and downloaded two applications as required.', 'success': False, 'error': 'No files were downloaded during this session.'}
Sending webhook for valid result
Sending Webhook Payload:
{'data': {'candidate_update': True, 'task_id': '2'},
'message': 'No files were downloaded during this session.',
'success': False}
< /code>
и вот мои связанные коды: < /p>
main.py:
import httpx
import os
import json

from dotenv import load_dotenv
from fastapi import FastAPI, HTTPException, APIRouter, BackgroundTasks
from fastapi.middleware.cors import CORSMiddleware
from pydantic import BaseModel
from pprint import pprint
from colored_print import log

from .modules.agents.browser_use_capsolver import main as browser_capsolver
from .modules.agents.browser_use_service import main as browser_main
from .modules.agents.browser_use_download import main as browser_download
from .modules.rag.faiss_module import get_full_recording
from .modules.static_automation.playwright_automation import post_job_on_jobspider
from .  import utils

load_dotenv()

WEBHOOK_URL = "https://test.com/test"

app = FastAPI()
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],  # In production, replace with specific origins
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)

api_v1 = APIRouter(prefix="/api/v1")

class TaskRequest(BaseModel):
prompt: str
task_id: str
is_dynamic: bool = True
capsolver: bool | None = False
is_download: bool | None = False
data: dict | None = None

async def send_webhook(
task_id: str,
success: bool,
message: str,
webhook_url: str,
is_download: bool = False,
):
headers = {
"Content-Type": "application/json",
"Authorization": "Basic xxx"
}

data = {"task_id": task_id}

if is_download:
data["candidate_update"] = True

payload = {
"message": message,
"success": success,
"data": data
}

log.pink("Sending Webhook Payload:")
pprint(payload)

async with httpx.AsyncClient() as client:
await client.post(webhook_url, headers=headers, json=payload)

async def process_automation(
task: str,
task_id: str,
is_capsolver: bool,
webhook_url: str,
is_download: bool = False,
):
webhook_sent = False  # Flag to track if webhook has been sent

try:
log.info("Starting automation process...")
if is_download:
result = await browser_download(task, task_id)

log.info(f"Automation result: {result}")

if isinstance(result, dict):
# Check if there's an error message that indicates failure
message = result.get("result") if result.get("success") else result.get("error", "Unknown error")

# Set success to False if message contains failure indicators
success = result.get("success", False) and "Failed" not in str(message) and "error"  not in str(message).lower()

log.info("Sending webhook for valid result")
await send_webhook(
task_id=task_id,
success=success,
message=message,
webhook_url=webhook_url,
is_download=is_download,
)
webhook_sent = True  # Set flag to true after successful webhook

else:
log.info("Sending webhook for invalid result format")
await send_webhook(
task_id=task_id,
success=False,
message="Invalid result format",
webhook_url=webhook_url,
is_download=is_download,
)
webhook_sent = True

except httpx.ConnectError as e:
log.error(f"Connection error occurred: {str(e)}")
# Handle connection-specific errors here if needed
except Exception as e:
log.error(f"Exception occurred: {str(e)}")
if not webhook_sent:  # Only send webhook if it hasn't been sent yet
await send_webhook(
task_id=task_id,
success=False,
message=str(e),
webhook_url=webhook_url,
is_download=is_download,
)

@api_v1.post("/execute-task")
async def execute_task(request: TaskRequest, background_tasks: BackgroundTasks):
log.info(
f"Received task execution request with payload: {json.dumps(request.model_dump(), indent=2)}"
).store()
try:
if not request.is_dynamic:
# Static Task Handling: Automate job posting on JobSpider
...

# Dynamic Task Handling
recording_context = get_full_recording("data/dummy.json", request.prompt)

data_string = utils.format_data_to_string(request.data)
task = f"""
Original Task: {request.prompt}

Data:
{data_string}

{f'Previous Recording Context (for guidance only, do not use exact values): {recording_context}' if recording_context != "No matching workflow found."  else ''}

Please complete the task by following the step-by-step process outlined in the Previous Recording Context as a guide, but always prioritize and use the values provided in the 'Original Task' part.

"""

background_tasks.add_task(
process_automation,
task,
request.task_id,
request.capsolver,
WEBHOOK_URL,
is_download=request.is_download,
)

return {
"status": "accepted",
"task_id": request.task_id,
"message": "Task is being processed asynchronously"
}

except Exception as e:
raise HTTPException(status_code=500, detail=str(e))

app.include_router(api_v1)

if __name__ == "__main__":
import uvicorn

uvicorn.run("main:app", host="0.0.0.0", port=8000, reload=True)
browser_use_download.py:

Код: Выделить всё

import os
import sys
from pathlib import Path

sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
import asyncio

from langchain_openai import ChatOpenAI
# from langchain.callbacks import get_openai_callback
from typing import Dict, List
from colored_print import log

from browser_use import Agent, Controller
from browser_use.browser.browser import Browser, BrowserConfig
from browser_use.browser.context import BrowserContext

from .browser_use_service import MAX_STEPS

# Initialize controller first
browser = Browser(config=BrowserConfig(headless=False))
controller = Controller()

async def handle_download(download, task_id, downloaded_files):
try:
# Create downloads directory if it doesn't exist
log.info(f"Handling download for task {task_id}")
downloads_dir = Path(f"./downloads/{task_id}")
downloads_dir.mkdir(parents=True, exist_ok=True)

# Wait for the download to complete and get the path
original_path = await download.path()
if not original_path:
raise Exception("Download path is None")

log.info(f"Original path: {original_path}")

# Check if the file exists before moving
if not os.path.exists(original_path):
log.error(f"Downloaded file not found at {original_path}")
return  # Early exit if file doesn't exist

# Create new path in downloads directory with .pdf postfix
new_path = downloads_dir / (os.path.basename(original_path) + '.pdf')

# Move the file to downloads directory
os.rename(original_path, new_path)

# Add the new path to downloaded files list
downloaded_files.append(str(new_path))
log.success(f"Downloaded and moved to: {new_path}")

except Exception as e:
log.error(f"Error handling download: {e}")

@controller.action(
'Upload file - the file name is inside the function - you only need to call this with the correct index',
requires_browser=True,
)
async def upload_file(index: int, browser: BrowserContext):
element = await browser.get_element_by_index(index)
my_file = Path.cwd() / 'examples/test_cv.txt'
if not element:
raise Exception(f'Element with index {index} not found')

await element.set_input_files(str(my_file.absolute()))
return f'Uploaded file to index {index}'

@controller.action('Close file dialog', requires_browser=True)
async def close_file_dialog(browser: BrowserContext):
page = await browser.get_current_page()
await page.keyboard.press('Escape')

def handle_page(new_page, task_id, downloaded_files):
log.info("New page created!")
new_page.on(
"download",
lambda download: asyncio.create_task(
handle_download(download, task_id, downloaded_files)
)
)

async def print_contexts_after_delay(
playwright_browser, task_id: str, downloaded_files: List[str]
):
await asyncio.sleep(5)
if len(playwright_browser.contexts) <  1:
raise Exception('No contexts found')

# Set up download handler at Playwright browser level
playwright_browser.contexts[0].on(
"page", lambda page: handle_page(page, task_id, downloaded_files)
)
log.info('contexts after 5 sec:', len(playwright_browser.contexts))

async def main(task: str, task_id: str):
model = ChatOpenAI(model='gpt-4o')
agent = Agent(
task=task,
llm=model,
controller=controller,
browser=browser,
)

# Initialize or clear the downloaded_files list for each task
downloaded_files = []

# Get the underlying Playwright browser instance
playwright_browser = await browser.get_playwright_browser()
log.info('contexts initial:', len(playwright_browser.contexts))

# Create task for delayed context printing
asyncio.create_task(
print_contexts_after_delay(playwright_browser, task_id, downloaded_files)
)

# Track token usage and cost
# with get_openai_callback() as cb:
# await agent.run()

# # Print usage statistics
# print("\nOpenAI API Usage Stats:")
# print(f"Total Tokens: {cb.total_tokens}")
# print(f"Prompt Tokens: {cb.prompt_tokens}")
# print(f"Completion Tokens: {cb.completion_tokens}")
# print(f"Total Cost (USD): ${cb.total_cost:.4f}")

# Run the agent
result = await agent.run(max_steps=MAX_STEPS)

history_file_path = 'AgentHistoryList.json'
agent.save_history(file_path=history_file_path)

is_complete: str | bool = result.history[-1].model_output.current_state.evaluation_previous_goal if result.history else False
max_steps_reached = len(result.history) >= MAX_STEPS
is_done = result.history[-1].result[-1].is_done
message = result.history[-1].result[-1].extracted_content
error = result.history[-1].result[-1].error
log.pink(result.history[-1].result[-1])

await browser.close()

# Print downloaded files
if downloaded_files:
log.info("\nDownloaded files:")
for file_path in downloaded_files:
log.info(file_path)
log.info(f"- {os.path.basename(file_path)}")
else:
log.error("\nNo files were downloaded during this session.")
is_done = False
error = "No files were downloaded during this session."

if max_steps_reached:
return {
"result": "Task exceeded maximum allowed steps",
"success": False,
"error": "Max steps exceeded without completion",
}

return {
"result": str(message),
"success": is_done,
"error": error,
}
< /code>
Полагаю, проблема связана с параллелизмом и асинхронными вещами. Или, может быть, связано с методом handle_download 
.
Я прошу прощения за длинный и сложный вопрос, я не мог сделать его проще, очень извините.

Подробнее здесь: https://stackoverflow.com/questions/793 ... rowser-use
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «Python»