Spaces:
Build error
Build error
| import argparse | |
| import os | |
| import helium | |
| from time import sleep | |
| from datetime import datetime | |
| from dotenv import load_dotenv | |
| from PIL import Image | |
| from selenium import webdriver | |
| from selenium.webdriver.common.by import By | |
| from selenium.webdriver.common.keys import Keys | |
| from smolagents import tool, CodeAgent, DuckDuckGoSearchTool, Model, InferenceClientModel | |
| from smolagents.agents import ActionStep | |
| from smolagents.cli import load_model | |
| from io import BytesIO | |
| agent_request = """ | |
| I am alfred, the butler of wayne manor, responsible for verifying the identity of guests at party. A superhero has arrived at the entrance claiming to be Wonderwomen, but I need to confirm if she is who she says she is. | |
| Please search for images of Wonder Women and generate a detailed visual description based on those images. Additionally, navigate to Wikipedia to gather key details about her appearance. With this information, I can determine whether to grant her access to the event. | |
| """; | |
| def parse_arguments(): | |
| parser = argparse.ArgumentParser(description="Run a web browser automation script with a specified model.") | |
| parser.add_argument( | |
| "prompt", | |
| type=str, | |
| nargs="?", # Makes it optional | |
| default=agent_request, | |
| help="The prompt to run with the agent", | |
| ) | |
| parser.add_argument( | |
| "--model-type", | |
| type=str, | |
| default="LiteLLMModel", | |
| help="The model type to use (e.g., OpenAIServerModel, LiteLLMModel, TransformersModel, InferenceClientModel)", | |
| ) | |
| parser.add_argument( | |
| "--model-id", | |
| type=str, | |
| default="gpt-4o", | |
| help="The model ID to use for the specified model type", | |
| ) | |
| return parser.parse_args() | |
| def search_item_ctrl_f(text: str, nth_result: int = 1) -> str: | |
| """ | |
| Searches for text on the current page via Ctrl + F and jumps to the nth occurence. | |
| Args: | |
| text: The text to search for. | |
| nth_result: Which ocurrence to jump to (default: 1) | |
| """ | |
| elements = driver.find_elements(By.XPATH, f"//*[contains(text(), '{text}')]") | |
| if nth_result > len(elements): | |
| raise Exception(f"Match n {nth_result} not found (only {len(elements)} matches found)") | |
| result = f"Found {len(elements)} matches for '{text}'." | |
| elem = elements[nth_result - 1] | |
| driver.execute_script("arguments[0].scrollIntoView();", elem) | |
| result += f"Focused on element {nth_result} of {len(elements)}" | |
| def go_back() -> None: | |
| """Goes back to previous page""" | |
| driver.back() | |
| def close_popups() -> str: | |
| """ | |
| Closes any visible modal or pop-up on the page. Use this to dismiss pop-up windows! This does not work on cookies consent banners. | |
| """ | |
| webdriver.ActionChains(driver).send_keys(Keys.ESCAPE).perform() | |
| def save_screenshot(step): | |
| """Save screenshot of the agent's current state""" | |
| try: | |
| # Check if this is an action step with code output | |
| if hasattr(step, 'action') and step.action is not None: | |
| if hasattr(step.action, 'output'): | |
| # Create screenshots directory if it doesn't exist | |
| os.makedirs('screenshots', exist_ok=True) | |
| # Generate filename with timestamp | |
| timestamp = datetime.now().strftime("%Y%m%d_%H%M%S_%f") | |
| filename = f"screenshots/agent_step_{timestamp}.txt" | |
| # Save the step information | |
| with open(filename, 'w', encoding='utf-8') as f: | |
| f.write(f"Step Type: {type(step).__name__}\n") | |
| f.write(f"Timestamp: {datetime.now().isoformat()}\n") | |
| f.write("=" * 50 + "\n") | |
| # Write action details | |
| if hasattr(step.action, 'tool_name'): | |
| f.write(f"Tool: {step.action.tool_name}\n") | |
| if hasattr(step.action, 'arguments'): | |
| f.write(f"Arguments: {step.action.arguments}\n") | |
| f.write("\nOutput:\n") | |
| f.write(str(step.action.output)) | |
| print(f"Saved screenshot: {filename}") | |
| except Exception as e: | |
| print(f"Error saving screenshot: {e}") | |
| def initialize_driver(): | |
| """Initialize the Selenium WebDriver.""" | |
| chrome_options = webdriver.ChromeOptions() | |
| chrome_options.add_argument("--force-device-scale-factor=1") | |
| chrome_options.add_argument("--window-size=1000,1350") | |
| chrome_options.add_argument("--disable-pdf-viewer") | |
| chrome_options.add_argument("--window-position=0,0") | |
| return helium.start_chrome(headless=False, options=chrome_options) | |
| helium_instructions = """ | |
| Use your web_search tool when you want to get Google search results. | |
| Then you can use helium to access websites. Don't use helium for Google search, only for navigating websites! | |
| Don't bother about the helium driver, it's already managed. | |
| We've already ran "from helium import *" | |
| Then you can go to pages! | |
| Code: | |
| ```py | |
| go_to('github.com/trending') | |
| ```<end_code> | |
| You can directly click clickable elements by inputting the text that appears on them. | |
| Code: | |
| ```py | |
| click("Top products") | |
| ```<end_code> | |
| If it's a link: | |
| Code: | |
| ```py | |
| click(Link("Top products")) | |
| ```<end_code> | |
| If you try to interact with an element and it's not found, you'll get a LookupError. | |
| In general stop your action after each button click to see what happens on your screenshot. | |
| Never try to login in a page. | |
| To scroll up or down, use scroll_down or scroll_up with as an argument the number of pixels to scroll from. | |
| Code: | |
| ```py | |
| scroll_down(num_pixels=1200) # This will scroll one viewport down | |
| ```<end_code> | |
| When you have pop-ups with a cross icon to close, don't try to click the close icon by finding its element or targeting an 'X' element (this most often fails). | |
| Just use your built-in tool `close_popups` to close them: | |
| Code: | |
| ```py | |
| close_popups() | |
| ```<end_code> | |
| You can use .exists() to check for the existence of an element. For example: | |
| Code: | |
| ```py | |
| if Text('Accept cookies?').exists(): | |
| click('I accept') | |
| ```<end_code> | |
| Proceed in several steps rather than trying to solve the task in one shot. | |
| And at the end, only when you have your answer, return your final answer. | |
| Code: | |
| ```py | |
| Make the final answer format is only a string | |
| final_answer("YOUR_ANSWER_HERE") | |
| ```<end_code> | |
| If pages seem stuck on loading, you might have to wait, for instance `import time` and run `time.sleep(5.0)`. But don't overuse this! | |
| To list elements on page, DO NOT try code-based element searches like 'contributors = find_all(S("ol > li"))': just look at the latest screenshot you have and read it visually, or use your tool search_item_ctrl_f. | |
| Of course, you can act on buttons like a user would do when navigating. | |
| After each code blob you write, you will be automatically provided with an updated screenshot of the browser and the current browser url. | |
| But beware that the screenshot will only be taken at the end of the whole action, it won't see intermediate states. | |
| Don't kill the browser. | |
| When you have modals or cookie banners on screen, you should get rid of them before you can click anything else. | |
| """ | |
| def initialize_agent(model): | |
| """Initialize the CodeAgent with the specified model.""" | |
| return CodeAgent( | |
| tools=[DuckDuckGoSearchTool(), go_back, close_popups, search_item_ctrl_f], | |
| model=model, | |
| additional_authorized_imports=["helium"], | |
| step_callbacks=[save_screenshot], | |
| max_steps=20, | |
| verbosity_level=2, | |
| ) | |
| def main(): | |
| # Parse command line arguments | |
| args = parse_arguments() | |
| # Initialize the model based on the provided arguments | |
| model = load_model(args.model_type, args.model_id) | |
| global driver | |
| driver = initialize_driver() | |
| agent = initialize_agent(model) | |
| # Run the agent with the provided prompt | |
| agent.python_executor("from helium import *") | |
| agent.run(args.prompt + helium_instructions) | |
| if __name__ == "__main__": | |
| main() |