Spaces:
Runtime error
Runtime error
Upload folder using huggingface_hub
Browse filesThis view is limited to 50 files because it contains too many changes. See raw diff
- .env +11 -0
- .github/workflows/update_space.yml +28 -0
- .gradio/certificate.pem +31 -0
- BrowsingAgent/BrowsingAgent.py +166 -0
- BrowsingAgent/__init__.py +1 -0
- BrowsingAgent/__pycache__/BrowsingAgent.cpython-312.pyc +0 -0
- BrowsingAgent/__pycache__/__init__.cpython-312.pyc +0 -0
- BrowsingAgent/instructions.md +21 -0
- BrowsingAgent/requirements.txt +3 -0
- BrowsingAgent/tools/ClickElement.py +59 -0
- BrowsingAgent/tools/ExportFile.py +45 -0
- BrowsingAgent/tools/GoBack.py +22 -0
- BrowsingAgent/tools/ReadURL.py +44 -0
- BrowsingAgent/tools/Scroll.py +53 -0
- BrowsingAgent/tools/SelectDropdown.py +58 -0
- BrowsingAgent/tools/SendKeys.py +73 -0
- BrowsingAgent/tools/SolveCaptcha.py +238 -0
- BrowsingAgent/tools/WebPageSummarizer.py +39 -0
- BrowsingAgent/tools/__init__.py +9 -0
- BrowsingAgent/tools/__pycache__/ClickElement.cpython-312.pyc +0 -0
- BrowsingAgent/tools/__pycache__/ExportFile.cpython-312.pyc +0 -0
- BrowsingAgent/tools/__pycache__/GoBack.cpython-312.pyc +0 -0
- BrowsingAgent/tools/__pycache__/ReadURL.cpython-312.pyc +0 -0
- BrowsingAgent/tools/__pycache__/Scroll.cpython-312.pyc +0 -0
- BrowsingAgent/tools/__pycache__/SelectDropdown.cpython-312.pyc +0 -0
- BrowsingAgent/tools/__pycache__/SendKeys.cpython-312.pyc +0 -0
- BrowsingAgent/tools/__pycache__/SolveCaptcha.cpython-312.pyc +0 -0
- BrowsingAgent/tools/__pycache__/WebPageSummarizer.cpython-312.pyc +0 -0
- BrowsingAgent/tools/__pycache__/__init__.cpython-312.pyc +0 -0
- BrowsingAgent/tools/util/__init__.py +3 -0
- BrowsingAgent/tools/util/__pycache__/__init__.cpython-312.pyc +0 -0
- BrowsingAgent/tools/util/__pycache__/get_b64_screenshot.cpython-312.pyc +0 -0
- BrowsingAgent/tools/util/__pycache__/highlights.cpython-312.pyc +0 -0
- BrowsingAgent/tools/util/__pycache__/selenium.cpython-312.pyc +0 -0
- BrowsingAgent/tools/util/get_b64_screenshot.py +8 -0
- BrowsingAgent/tools/util/highlights.py +139 -0
- BrowsingAgent/tools/util/selenium.py +154 -0
- CompetitorTrackingAgent/CompetitorTrackingAgent.py +19 -0
- CompetitorTrackingAgent/__init__.py +1 -0
- CompetitorTrackingAgent/__pycache__/CompetitorTrackingAgent.cpython-312.pyc +0 -0
- CompetitorTrackingAgent/__pycache__/__init__.cpython-312.pyc +0 -0
- CompetitorTrackingAgent/instructions.md +11 -0
- CompetitorTrackingAgent/tools/WebScrapingTool.py +59 -0
- CompetitorTrackingAgent/tools/__pycache__/WebScrapingTool.cpython-312.pyc +0 -0
- DataAnalystAgent/DataAnalystAgent.py +19 -0
- DataAnalystAgent/__init__.py +1 -0
- DataAnalystAgent/__pycache__/DataAnalystAgent.cpython-312.pyc +0 -0
- DataAnalystAgent/__pycache__/__init__.cpython-312.pyc +0 -0
- DataAnalystAgent/instructions.md +11 -0
- DataAnalystAgent/tools/DataAnalysisTool.py +89 -0
.env
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
OPENAI_API_KEY=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
|
| 2 |
+
OPENAI_MODEL=gpt-4-1106-preview
|
| 3 |
+
OPENAI_ORGANIZATION=your_openai_org_id_here
|
| 4 |
+
GROQ_API_KEY=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
|
| 5 |
+
DEEPGRAM_API_KEY=your_deepgram_api_key_here
|
| 6 |
+
GROQ_API_KEY=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
|
| 7 |
+
MAX_TOKENS=4000
|
| 8 |
+
TEMPERATURE=0.3
|
| 9 |
+
|
| 10 |
+
PORT=5002
|
| 11 |
+
HOST=0.0.0.0
|
.github/workflows/update_space.yml
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
name: Run Python script
|
| 2 |
+
|
| 3 |
+
on:
|
| 4 |
+
push:
|
| 5 |
+
branches:
|
| 6 |
+
- y
|
| 7 |
+
|
| 8 |
+
jobs:
|
| 9 |
+
build:
|
| 10 |
+
runs-on: ubuntu-latest
|
| 11 |
+
|
| 12 |
+
steps:
|
| 13 |
+
- name: Checkout
|
| 14 |
+
uses: actions/checkout@v2
|
| 15 |
+
|
| 16 |
+
- name: Set up Python
|
| 17 |
+
uses: actions/setup-python@v2
|
| 18 |
+
with:
|
| 19 |
+
python-version: '3.9'
|
| 20 |
+
|
| 21 |
+
- name: Install Gradio
|
| 22 |
+
run: python -m pip install gradio
|
| 23 |
+
|
| 24 |
+
- name: Log in to Hugging Face
|
| 25 |
+
run: python -c 'import huggingface_hub; huggingface_hub.login(token="${{ secrets.hf_token }}")'
|
| 26 |
+
|
| 27 |
+
- name: Deploy to Spaces
|
| 28 |
+
run: gradio deploy
|
.gradio/certificate.pem
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
-----BEGIN CERTIFICATE-----
|
| 2 |
+
MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw
|
| 3 |
+
TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh
|
| 4 |
+
cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4
|
| 5 |
+
WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu
|
| 6 |
+
ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY
|
| 7 |
+
MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc
|
| 8 |
+
h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+
|
| 9 |
+
0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U
|
| 10 |
+
A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW
|
| 11 |
+
T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH
|
| 12 |
+
B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC
|
| 13 |
+
B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv
|
| 14 |
+
KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn
|
| 15 |
+
OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn
|
| 16 |
+
jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw
|
| 17 |
+
qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI
|
| 18 |
+
rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV
|
| 19 |
+
HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq
|
| 20 |
+
hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL
|
| 21 |
+
ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ
|
| 22 |
+
3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK
|
| 23 |
+
NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5
|
| 24 |
+
ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur
|
| 25 |
+
TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC
|
| 26 |
+
jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc
|
| 27 |
+
oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq
|
| 28 |
+
4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA
|
| 29 |
+
mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d
|
| 30 |
+
emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc=
|
| 31 |
+
-----END CERTIFICATE-----
|
BrowsingAgent/BrowsingAgent.py
ADDED
|
@@ -0,0 +1,166 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import json
|
| 2 |
+
import re
|
| 3 |
+
|
| 4 |
+
from agency_swarm.agents import Agent
|
| 5 |
+
from agency_swarm.tools.oai import FileSearch
|
| 6 |
+
from typing_extensions import override
|
| 7 |
+
import base64
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
class BrowsingAgent(Agent):
|
| 11 |
+
SCREENSHOT_FILE_NAME = "screenshot.jpg"
|
| 12 |
+
|
| 13 |
+
def __init__(self, selenium_config=None, **kwargs):
|
| 14 |
+
from .tools.util.selenium import set_selenium_config
|
| 15 |
+
super().__init__(
|
| 16 |
+
name="BrowsingAgent",
|
| 17 |
+
description="This agent is designed to navigate and search web effectively.",
|
| 18 |
+
instructions="./instructions.md",
|
| 19 |
+
files_folder="./files",
|
| 20 |
+
schemas_folder="./schemas",
|
| 21 |
+
tools=[],
|
| 22 |
+
tools_folder="./tools",
|
| 23 |
+
temperature=0,
|
| 24 |
+
max_prompt_tokens=16000,
|
| 25 |
+
model="gpt-4o",
|
| 26 |
+
validation_attempts=25,
|
| 27 |
+
**kwargs
|
| 28 |
+
)
|
| 29 |
+
if selenium_config is not None:
|
| 30 |
+
set_selenium_config(selenium_config)
|
| 31 |
+
|
| 32 |
+
self.prev_message = ""
|
| 33 |
+
|
| 34 |
+
@override
|
| 35 |
+
def response_validator(self, message):
|
| 36 |
+
from .tools.util.selenium import get_web_driver, set_web_driver
|
| 37 |
+
from .tools.util import highlight_elements_with_labels, remove_highlight_and_labels
|
| 38 |
+
from selenium.webdriver.common.by import By
|
| 39 |
+
from selenium.webdriver.support.select import Select
|
| 40 |
+
|
| 41 |
+
# Filter out everything in square brackets
|
| 42 |
+
filtered_message = re.sub(r'\[.*?\]', '', message).strip()
|
| 43 |
+
|
| 44 |
+
if filtered_message and self.prev_message == filtered_message:
|
| 45 |
+
raise ValueError("Do not repeat yourself. If you are stuck, try a different approach or search in google for the page you are looking for directly.")
|
| 46 |
+
|
| 47 |
+
self.prev_message = filtered_message
|
| 48 |
+
|
| 49 |
+
if "[send screenshot]" in message.lower():
|
| 50 |
+
wd = get_web_driver()
|
| 51 |
+
remove_highlight_and_labels(wd)
|
| 52 |
+
self.take_screenshot()
|
| 53 |
+
response_text = "Here is the screenshot of the current web page:"
|
| 54 |
+
|
| 55 |
+
elif '[highlight clickable elements]' in message.lower():
|
| 56 |
+
wd = get_web_driver()
|
| 57 |
+
highlight_elements_with_labels(wd, 'a, button, div[onclick], div[role="button"], div[tabindex], '
|
| 58 |
+
'span[onclick], span[role="button"], span[tabindex]')
|
| 59 |
+
self._shared_state.set("elements_highlighted", 'a, button, div[onclick], div[role="button"], div[tabindex], '
|
| 60 |
+
'span[onclick], span[role="button"], span[tabindex]')
|
| 61 |
+
|
| 62 |
+
self.take_screenshot()
|
| 63 |
+
|
| 64 |
+
all_elements = wd.find_elements(By.CSS_SELECTOR, '.highlighted-element')
|
| 65 |
+
|
| 66 |
+
all_element_texts = [element.text for element in all_elements]
|
| 67 |
+
|
| 68 |
+
element_texts_json = {}
|
| 69 |
+
for i, element_text in enumerate(all_element_texts):
|
| 70 |
+
element_texts_json[str(i + 1)] = self.remove_unicode(element_text)
|
| 71 |
+
|
| 72 |
+
element_texts_json = {k: v for k, v in element_texts_json.items() if v}
|
| 73 |
+
|
| 74 |
+
element_texts_formatted = ", ".join([f"{k}: {v}" for k, v in element_texts_json.items()])
|
| 75 |
+
|
| 76 |
+
response_text = ("Here is the screenshot of the current web page with highlighted clickable elements. \n\n"
|
| 77 |
+
"Texts of the elements are: " + element_texts_formatted + ".\n\n"
|
| 78 |
+
"Elements without text are not shown, but are available on screenshot. \n"
|
| 79 |
+
"Please make sure to analyze the screenshot to find the clickable element you need to click on.")
|
| 80 |
+
|
| 81 |
+
elif '[highlight text fields]' in message.lower():
|
| 82 |
+
wd = get_web_driver()
|
| 83 |
+
highlight_elements_with_labels(wd, 'input, textarea')
|
| 84 |
+
self._shared_state.set("elements_highlighted", "input, textarea")
|
| 85 |
+
|
| 86 |
+
self.take_screenshot()
|
| 87 |
+
|
| 88 |
+
all_elements = wd.find_elements(By.CSS_SELECTOR, '.highlighted-element')
|
| 89 |
+
|
| 90 |
+
all_element_texts = [element.text for element in all_elements]
|
| 91 |
+
|
| 92 |
+
element_texts_json = {}
|
| 93 |
+
for i, element_text in enumerate(all_element_texts):
|
| 94 |
+
element_texts_json[str(i + 1)] = self.remove_unicode(element_text)
|
| 95 |
+
|
| 96 |
+
element_texts_formatted = ", ".join([f"{k}: {v}" for k, v in element_texts_json.items()])
|
| 97 |
+
|
| 98 |
+
response_text = ("Here is the screenshot of the current web page with highlighted text fields: \n"
|
| 99 |
+
"Texts of the elements are: " + element_texts_formatted + ".\n"
|
| 100 |
+
"Please make sure to analyze the screenshot to find the text field you need to fill.")
|
| 101 |
+
|
| 102 |
+
elif '[highlight dropdowns]' in message.lower():
|
| 103 |
+
wd = get_web_driver()
|
| 104 |
+
highlight_elements_with_labels(wd, 'select')
|
| 105 |
+
self._shared_state.set("elements_highlighted", "select")
|
| 106 |
+
|
| 107 |
+
self.take_screenshot()
|
| 108 |
+
|
| 109 |
+
all_elements = wd.find_elements(By.CSS_SELECTOR, '.highlighted-element')
|
| 110 |
+
|
| 111 |
+
all_selector_values = {}
|
| 112 |
+
|
| 113 |
+
i = 0
|
| 114 |
+
for element in all_elements:
|
| 115 |
+
select = Select(element)
|
| 116 |
+
options = select.options
|
| 117 |
+
selector_values = {}
|
| 118 |
+
for j, option in enumerate(options):
|
| 119 |
+
selector_values[str(j)] = option.text
|
| 120 |
+
if j > 10:
|
| 121 |
+
break
|
| 122 |
+
all_selector_values[str(i + 1)] = selector_values
|
| 123 |
+
|
| 124 |
+
all_selector_values = {k: v for k, v in all_selector_values.items() if v}
|
| 125 |
+
all_selector_values_formatted = ", ".join([f"{k}: {v}" for k, v in all_selector_values.items()])
|
| 126 |
+
|
| 127 |
+
response_text = ("Here is the screenshot with highlighted dropdowns. \n"
|
| 128 |
+
"Selector values are: " + all_selector_values_formatted + ".\n"
|
| 129 |
+
"Please make sure to analyze the screenshot to find the dropdown you need to select.")
|
| 130 |
+
|
| 131 |
+
else:
|
| 132 |
+
return message
|
| 133 |
+
|
| 134 |
+
set_web_driver(wd)
|
| 135 |
+
content = self.create_response_content(response_text)
|
| 136 |
+
raise ValueError(content)
|
| 137 |
+
|
| 138 |
+
def take_screenshot(self):
|
| 139 |
+
from .tools.util.selenium import get_web_driver
|
| 140 |
+
from .tools.util import get_b64_screenshot
|
| 141 |
+
wd = get_web_driver()
|
| 142 |
+
screenshot = get_b64_screenshot(wd)
|
| 143 |
+
screenshot_data = base64.b64decode(screenshot)
|
| 144 |
+
with open(self.SCREENSHOT_FILE_NAME, "wb") as screenshot_file:
|
| 145 |
+
screenshot_file.write(screenshot_data)
|
| 146 |
+
|
| 147 |
+
def create_response_content(self, response_text):
|
| 148 |
+
with open(self.SCREENSHOT_FILE_NAME, "rb") as file:
|
| 149 |
+
file_id = self.client.files.create(
|
| 150 |
+
file=file,
|
| 151 |
+
purpose="vision",
|
| 152 |
+
).id
|
| 153 |
+
|
| 154 |
+
content = [
|
| 155 |
+
{"type": "text", "text": response_text},
|
| 156 |
+
{
|
| 157 |
+
"type": "image_file",
|
| 158 |
+
"image_file": {"file_id": file_id}
|
| 159 |
+
}
|
| 160 |
+
]
|
| 161 |
+
return content
|
| 162 |
+
|
| 163 |
+
# Function to check for Unicode escape sequences
|
| 164 |
+
def remove_unicode(self, data):
|
| 165 |
+
return re.sub(r'[^\x00-\x7F]+', '', data)
|
| 166 |
+
|
BrowsingAgent/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
from .BrowsingAgent import BrowsingAgent
|
BrowsingAgent/__pycache__/BrowsingAgent.cpython-312.pyc
ADDED
|
Binary file (8.19 kB). View file
|
|
|
BrowsingAgent/__pycache__/__init__.cpython-312.pyc
ADDED
|
Binary file (207 Bytes). View file
|
|
|
BrowsingAgent/instructions.md
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Browsing Agent Instructions
|
| 2 |
+
|
| 3 |
+
As an advanced browsing agent, you are equipped with specialized tools to navigate and search the web effectively. Your primary objective is to fulfill the user's requests by efficiently utilizing these tools.
|
| 4 |
+
|
| 5 |
+
### Primary Instructions:
|
| 6 |
+
|
| 7 |
+
1. **Avoid Guessing URLs**: Never attempt to guess the direct URL. Always perform a Google search if applicable, or return to your previous search results.
|
| 8 |
+
2. **Navigating to New Pages**: Always use the `ClickElement` tool to open links when navigating to a new web page from the current source. Do not guess the direct URL.
|
| 9 |
+
3. **Single Page Interaction**: You can only open and interact with one web page at a time. The previous web page will be closed when you open a new one. To navigate back, use the `GoBack` tool.
|
| 10 |
+
4. **Requesting Screenshots**: Before using tools that interact with the web page, ask the user to send you the appropriate screenshot using one of the commands below.
|
| 11 |
+
|
| 12 |
+
### Commands to Request Screenshots:
|
| 13 |
+
|
| 14 |
+
- **'[send screenshot]'**: Sends the current browsing window as an image. Use this command if the user asks what is on the page.
|
| 15 |
+
- **'[highlight clickable elements]'**: Highlights all clickable elements on the current web page. This must be done before using the `ClickElement` tool.
|
| 16 |
+
- **'[highlight text fields]'**: Highlights all text fields on the current web page. This must be done before using the `SendKeys` tool.
|
| 17 |
+
- **'[highlight dropdowns]'**: Highlights all dropdowns on the current web page. This must be done before using the `SelectDropdown` tool.
|
| 18 |
+
|
| 19 |
+
### Important Reminders:
|
| 20 |
+
|
| 21 |
+
- Only open and interact with one web page at a time. Do not attempt to read or click on multiple links simultaneously. Complete your interactions with the current web page before proceeding to a different source.
|
BrowsingAgent/requirements.txt
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
selenium
|
| 2 |
+
webdriver-manager
|
| 3 |
+
selenium_stealth
|
BrowsingAgent/tools/ClickElement.py
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import time
|
| 2 |
+
|
| 3 |
+
from pydantic import Field
|
| 4 |
+
from selenium.webdriver.common.by import By
|
| 5 |
+
|
| 6 |
+
from agency_swarm.tools import BaseTool
|
| 7 |
+
from .util import get_web_driver, set_web_driver
|
| 8 |
+
from .util.highlights import remove_highlight_and_labels
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
class ClickElement(BaseTool):
|
| 12 |
+
"""
|
| 13 |
+
This tool clicks on an element on the current web page based on its number.
|
| 14 |
+
|
| 15 |
+
Before using this tool make sure to highlight clickable elements on the page by outputting '[highlight clickable elements]' message.
|
| 16 |
+
"""
|
| 17 |
+
element_number: int = Field(
|
| 18 |
+
...,
|
| 19 |
+
description="The number of the element to click on. The element numbers are displayed on the page after highlighting elements.",
|
| 20 |
+
)
|
| 21 |
+
|
| 22 |
+
def run(self):
|
| 23 |
+
wd = get_web_driver()
|
| 24 |
+
|
| 25 |
+
if 'button' not in self._shared_state.get("elements_highlighted", ""):
|
| 26 |
+
raise ValueError("Please highlight clickable elements on the page first by outputting '[highlight clickable elements]' message. You must output just the message without calling the tool first, so the user can respond with the screenshot.")
|
| 27 |
+
|
| 28 |
+
all_elements = wd.find_elements(By.CSS_SELECTOR, '.highlighted-element')
|
| 29 |
+
|
| 30 |
+
# iterate through all elements with a number in the text
|
| 31 |
+
try:
|
| 32 |
+
element_text = all_elements[self.element_number - 1].text
|
| 33 |
+
element_text = element_text.strip() if element_text else ""
|
| 34 |
+
# Subtract 1 because sequence numbers start at 1, but list indices start at 0
|
| 35 |
+
try:
|
| 36 |
+
all_elements[self.element_number - 1].click()
|
| 37 |
+
except Exception as e:
|
| 38 |
+
if "element click intercepted" in str(e).lower():
|
| 39 |
+
wd.execute_script("arguments[0].click();", all_elements[self.element_number - 1])
|
| 40 |
+
else:
|
| 41 |
+
raise e
|
| 42 |
+
|
| 43 |
+
time.sleep(3)
|
| 44 |
+
|
| 45 |
+
result = f"Clicked on element {self.element_number}. Text on clicked element: '{element_text}'. Current URL is {wd.current_url} To further analyze the page, output '[send screenshot]' command."
|
| 46 |
+
except IndexError:
|
| 47 |
+
result = "Element number is invalid. Please try again with a valid element number."
|
| 48 |
+
except Exception as e:
|
| 49 |
+
result = str(e)
|
| 50 |
+
|
| 51 |
+
wd = remove_highlight_and_labels(wd)
|
| 52 |
+
|
| 53 |
+
wd.execute_script("document.body.style.zoom='1.5'")
|
| 54 |
+
|
| 55 |
+
set_web_driver(wd)
|
| 56 |
+
|
| 57 |
+
self._shared_state.set("elements_highlighted", "")
|
| 58 |
+
|
| 59 |
+
return result
|
BrowsingAgent/tools/ExportFile.py
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import base64
|
| 2 |
+
import os
|
| 3 |
+
|
| 4 |
+
from agency_swarm.tools import BaseTool
|
| 5 |
+
from .util import get_web_driver
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
class ExportFile(BaseTool):
|
| 9 |
+
"""This tool converts the current full web page into a file and returns its file_id. You can then send this file id back to the user for further processing."""
|
| 10 |
+
|
| 11 |
+
def run(self):
|
| 12 |
+
wd = get_web_driver()
|
| 13 |
+
from agency_swarm import get_openai_client
|
| 14 |
+
client = get_openai_client()
|
| 15 |
+
|
| 16 |
+
# Define the parameters for the PDF
|
| 17 |
+
params = {
|
| 18 |
+
'landscape': False,
|
| 19 |
+
'displayHeaderFooter': False,
|
| 20 |
+
'printBackground': True,
|
| 21 |
+
'preferCSSPageSize': True,
|
| 22 |
+
}
|
| 23 |
+
|
| 24 |
+
# Execute the command to print to PDF
|
| 25 |
+
result = wd.execute_cdp_cmd('Page.printToPDF', params)
|
| 26 |
+
pdf = result['data']
|
| 27 |
+
|
| 28 |
+
pdf_bytes = base64.b64decode(pdf)
|
| 29 |
+
|
| 30 |
+
# Save the PDF to a file
|
| 31 |
+
with open("exported_file.pdf", "wb") as f:
|
| 32 |
+
f.write(pdf_bytes)
|
| 33 |
+
|
| 34 |
+
file_id = client.files.create(file=open("exported_file.pdf", "rb"), purpose="assistants",).id
|
| 35 |
+
|
| 36 |
+
self._shared_state.set("file_id", file_id)
|
| 37 |
+
|
| 38 |
+
return "Success. File exported with id: `" + file_id + "` You can now send this file id back to the user."
|
| 39 |
+
|
| 40 |
+
|
| 41 |
+
if __name__ == "__main__":
|
| 42 |
+
wd = get_web_driver()
|
| 43 |
+
wd.get("https://www.google.com")
|
| 44 |
+
tool = ExportFile()
|
| 45 |
+
tool.run()
|
BrowsingAgent/tools/GoBack.py
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import time
|
| 2 |
+
|
| 3 |
+
from agency_swarm.tools import BaseTool
|
| 4 |
+
|
| 5 |
+
from .util.selenium import get_web_driver, set_web_driver
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
class GoBack(BaseTool):
|
| 9 |
+
"""W
|
| 10 |
+
This tool allows you to go back 1 page in the browser history. Use it in case of a mistake or if a page shows you unexpected content.
|
| 11 |
+
"""
|
| 12 |
+
|
| 13 |
+
def run(self):
|
| 14 |
+
wd = get_web_driver()
|
| 15 |
+
|
| 16 |
+
wd.back()
|
| 17 |
+
|
| 18 |
+
time.sleep(3)
|
| 19 |
+
|
| 20 |
+
set_web_driver(wd)
|
| 21 |
+
|
| 22 |
+
return "Success. Went back 1 page. Current URL is: " + wd.current_url
|
BrowsingAgent/tools/ReadURL.py
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import time
|
| 2 |
+
|
| 3 |
+
from pydantic import Field
|
| 4 |
+
|
| 5 |
+
from agency_swarm.tools import BaseTool
|
| 6 |
+
from .util.selenium import get_web_driver, set_web_driver
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
class ReadURL(BaseTool):
|
| 10 |
+
"""
|
| 11 |
+
This tool reads a single URL and opens it in your current browser window. For each new source, either navigate directly to a URL that you believe contains the answer to the user's question or perform a Google search (e.g., 'https://google.com/search?q=search') if necessary.
|
| 12 |
+
|
| 13 |
+
If you are unsure of the direct URL, do not guess. Instead, use the ClickElement tool to click on links that might contain the desired information on the current web page.
|
| 14 |
+
|
| 15 |
+
Note: This tool only supports opening one URL at a time. The previous URL will be closed when you open a new one.
|
| 16 |
+
"""
|
| 17 |
+
chain_of_thought: str = Field(
|
| 18 |
+
..., description="Think step-by-step about where you need to navigate next to find the necessary information.",
|
| 19 |
+
exclude=True
|
| 20 |
+
)
|
| 21 |
+
url: str = Field(
|
| 22 |
+
..., description="URL of the webpage.", examples=["https://google.com/search?q=search"]
|
| 23 |
+
)
|
| 24 |
+
|
| 25 |
+
class ToolConfig:
|
| 26 |
+
one_call_at_a_time: bool = True
|
| 27 |
+
|
| 28 |
+
def run(self):
|
| 29 |
+
wd = get_web_driver()
|
| 30 |
+
|
| 31 |
+
wd.get(self.url)
|
| 32 |
+
|
| 33 |
+
time.sleep(2)
|
| 34 |
+
|
| 35 |
+
set_web_driver(wd)
|
| 36 |
+
|
| 37 |
+
self._shared_state.set("elements_highlighted", "")
|
| 38 |
+
|
| 39 |
+
return "Current URL is: " + wd.current_url + "\n" + "Please output '[send screenshot]' next to analyze the current web page or '[highlight clickable elements]' for further navigation."
|
| 40 |
+
|
| 41 |
+
|
| 42 |
+
if __name__ == "__main__":
|
| 43 |
+
tool = ReadURL(url="https://google.com")
|
| 44 |
+
print(tool.run())
|
BrowsingAgent/tools/Scroll.py
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from typing import Literal
|
| 2 |
+
|
| 3 |
+
from pydantic import Field
|
| 4 |
+
|
| 5 |
+
from agency_swarm.tools import BaseTool
|
| 6 |
+
from .util.selenium import get_web_driver, set_web_driver
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
class Scroll(BaseTool):
|
| 10 |
+
"""
|
| 11 |
+
This tool allows you to scroll the current web page up or down by 1 screen height.
|
| 12 |
+
"""
|
| 13 |
+
direction: Literal["up", "down"] = Field(
|
| 14 |
+
..., description="Direction to scroll."
|
| 15 |
+
)
|
| 16 |
+
|
| 17 |
+
def run(self):
|
| 18 |
+
wd = get_web_driver()
|
| 19 |
+
|
| 20 |
+
height = wd.get_window_size()['height']
|
| 21 |
+
|
| 22 |
+
# Get the zoom level
|
| 23 |
+
zoom_level = wd.execute_script("return document.body.style.zoom || '1';")
|
| 24 |
+
zoom_level = float(zoom_level.strip('%')) / 100 if '%' in zoom_level else float(zoom_level)
|
| 25 |
+
|
| 26 |
+
# Adjust height by zoom level
|
| 27 |
+
adjusted_height = height / zoom_level
|
| 28 |
+
|
| 29 |
+
current_scroll_position = wd.execute_script("return window.pageYOffset;")
|
| 30 |
+
total_scroll_height = wd.execute_script("return document.body.scrollHeight;")
|
| 31 |
+
|
| 32 |
+
result = ""
|
| 33 |
+
|
| 34 |
+
if self.direction == "up":
|
| 35 |
+
if current_scroll_position == 0:
|
| 36 |
+
# Reached the top of the page
|
| 37 |
+
result = "Reached the top of the page. Cannot scroll up any further.\n"
|
| 38 |
+
else:
|
| 39 |
+
wd.execute_script(f"window.scrollBy(0, -{adjusted_height});")
|
| 40 |
+
result = "Scrolled up by 1 screen height. Make sure to output '[send screenshot]' command to analyze the page after scrolling."
|
| 41 |
+
|
| 42 |
+
elif self.direction == "down":
|
| 43 |
+
if current_scroll_position + adjusted_height >= total_scroll_height:
|
| 44 |
+
# Reached the bottom of the page
|
| 45 |
+
result = "Reached the bottom of the page. Cannot scroll down any further.\n"
|
| 46 |
+
else:
|
| 47 |
+
wd.execute_script(f"window.scrollBy(0, {adjusted_height});")
|
| 48 |
+
result = "Scrolled down by 1 screen height. Make sure to output '[send screenshot]' command to analyze the page after scrolling."
|
| 49 |
+
|
| 50 |
+
set_web_driver(wd)
|
| 51 |
+
|
| 52 |
+
return result
|
| 53 |
+
|
BrowsingAgent/tools/SelectDropdown.py
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from typing import Dict
|
| 2 |
+
from pydantic import Field, model_validator
|
| 3 |
+
from selenium.webdriver.common.by import By
|
| 4 |
+
from selenium.webdriver.support.select import Select
|
| 5 |
+
|
| 6 |
+
from agency_swarm.tools import BaseTool
|
| 7 |
+
from .util import get_web_driver, set_web_driver
|
| 8 |
+
from .util.highlights import remove_highlight_and_labels
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
class SelectDropdown(BaseTool):
|
| 12 |
+
"""
|
| 13 |
+
This tool selects an option in a dropdown on the current web page based on the description of that element and which option to select.
|
| 14 |
+
|
| 15 |
+
Before using this tool make sure to highlight dropdown elements on the page by outputting '[highlight dropdowns]' message.
|
| 16 |
+
"""
|
| 17 |
+
|
| 18 |
+
key_value_pairs: Dict[str, str] = Field(...,
|
| 19 |
+
description="A dictionary where the key is the sequence number of the dropdown element and the value is the index of the option to select.",
|
| 20 |
+
examples=[{"1": 0, "2": 1}, {"3": 2}]
|
| 21 |
+
)
|
| 22 |
+
|
| 23 |
+
@model_validator(mode='before')
|
| 24 |
+
@classmethod
|
| 25 |
+
def check_key_value_pairs(cls, data):
|
| 26 |
+
if not data.get('key_value_pairs'):
|
| 27 |
+
raise ValueError(
|
| 28 |
+
"key_value_pairs is required. Example format: "
|
| 29 |
+
"key_value_pairs={'1': 0, '2': 1}"
|
| 30 |
+
)
|
| 31 |
+
return data
|
| 32 |
+
|
| 33 |
+
def run(self):
|
| 34 |
+
wd = get_web_driver()
|
| 35 |
+
|
| 36 |
+
if 'select' not in self._shared_state.get("elements_highlighted", ""):
|
| 37 |
+
raise ValueError("Please highlight dropdown elements on the page first by outputting '[highlight dropdowns]' message. You must output just the message without calling the tool first, so the user can respond with the screenshot.")
|
| 38 |
+
|
| 39 |
+
all_elements = wd.find_elements(By.CSS_SELECTOR, '.highlighted-element')
|
| 40 |
+
|
| 41 |
+
try:
|
| 42 |
+
for key, value in self.key_value_pairs.items():
|
| 43 |
+
key = int(key)
|
| 44 |
+
element = all_elements[key - 1]
|
| 45 |
+
|
| 46 |
+
select = Select(element)
|
| 47 |
+
|
| 48 |
+
# Select the first option (index 0)
|
| 49 |
+
select.select_by_index(int(value))
|
| 50 |
+
result = f"Success. Option is selected in the dropdown. To further analyze the page, output '[send screenshot]' command."
|
| 51 |
+
except Exception as e:
|
| 52 |
+
result = str(e)
|
| 53 |
+
|
| 54 |
+
remove_highlight_and_labels(wd)
|
| 55 |
+
|
| 56 |
+
set_web_driver(wd)
|
| 57 |
+
|
| 58 |
+
return result
|
BrowsingAgent/tools/SendKeys.py
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import time
|
| 2 |
+
from typing import Dict
|
| 3 |
+
|
| 4 |
+
from pydantic import Field
|
| 5 |
+
from selenium.webdriver import Keys
|
| 6 |
+
from selenium.webdriver.common.by import By
|
| 7 |
+
|
| 8 |
+
from agency_swarm.tools import BaseTool
|
| 9 |
+
from .util import get_web_driver, set_web_driver
|
| 10 |
+
from .util.highlights import remove_highlight_and_labels
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
from pydantic import model_validator
|
| 14 |
+
|
| 15 |
+
class SendKeys(BaseTool):
|
| 16 |
+
"""
|
| 17 |
+
This tool sends keys into input fields on the current webpage based on the description of that element and what needs to be typed. It then clicks "Enter" on the last element to submit the form. You do not need to tell it to press "Enter"; it will do that automatically.
|
| 18 |
+
|
| 19 |
+
Before using this tool make sure to highlight the input elements on the page by outputting '[highlight text fields]' message.
|
| 20 |
+
"""
|
| 21 |
+
elements_and_texts: Dict[int, str] = Field(...,
|
| 22 |
+
description="A dictionary where the key is the element number and the value is the text to be typed.",
|
| 23 |
+
examples=[
|
| 24 |
+
{52: "johndoe@gmail.com", 53: "password123"},
|
| 25 |
+
{3: "John Doe", 4: "123 Main St"},
|
| 26 |
+
]
|
| 27 |
+
)
|
| 28 |
+
|
| 29 |
+
@model_validator(mode='before')
|
| 30 |
+
@classmethod
|
| 31 |
+
def check_elements_and_texts(cls, data):
|
| 32 |
+
if not data.get('elements_and_texts'):
|
| 33 |
+
raise ValueError(
|
| 34 |
+
"elements_and_texts is required. Example format: "
|
| 35 |
+
"elements_and_texts={1: 'John Doe', 2: '123 Main St'}"
|
| 36 |
+
)
|
| 37 |
+
return data
|
| 38 |
+
|
| 39 |
+
def run(self):
|
| 40 |
+
wd = get_web_driver()
|
| 41 |
+
if 'input' not in self._shared_state.get("elements_highlighted", ""):
|
| 42 |
+
raise ValueError("Please highlight input elements on the page first by outputting '[highlight text fields]' message. You must output just the message without calling the tool first, so the user can respond with the screenshot.")
|
| 43 |
+
|
| 44 |
+
all_elements = wd.find_elements(By.CSS_SELECTOR, '.highlighted-element')
|
| 45 |
+
|
| 46 |
+
i = 0
|
| 47 |
+
try:
|
| 48 |
+
for key, value in self.elements_and_texts.items():
|
| 49 |
+
key = int(key)
|
| 50 |
+
element = all_elements[key - 1]
|
| 51 |
+
|
| 52 |
+
try:
|
| 53 |
+
element.click()
|
| 54 |
+
element.send_keys(Keys.CONTROL + "a") # Select all text in input
|
| 55 |
+
element.send_keys(Keys.DELETE)
|
| 56 |
+
element.clear()
|
| 57 |
+
except Exception as e:
|
| 58 |
+
pass
|
| 59 |
+
element.send_keys(value)
|
| 60 |
+
# send enter key to the last element
|
| 61 |
+
if i == len(self.elements_and_texts) - 1:
|
| 62 |
+
element.send_keys(Keys.RETURN)
|
| 63 |
+
time.sleep(3)
|
| 64 |
+
i += 1
|
| 65 |
+
result = f"Sent input to element and pressed Enter. Current URL is {wd.current_url} To further analyze the page, output '[send screenshot]' command."
|
| 66 |
+
except Exception as e:
|
| 67 |
+
result = str(e)
|
| 68 |
+
|
| 69 |
+
remove_highlight_and_labels(wd)
|
| 70 |
+
|
| 71 |
+
set_web_driver(wd)
|
| 72 |
+
|
| 73 |
+
return result
|
BrowsingAgent/tools/SolveCaptcha.py
ADDED
|
@@ -0,0 +1,238 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import base64
|
| 2 |
+
import time
|
| 3 |
+
|
| 4 |
+
from selenium.webdriver.common.by import By
|
| 5 |
+
from selenium.webdriver.support.expected_conditions import presence_of_element_located, \
|
| 6 |
+
frame_to_be_available_and_switch_to_it
|
| 7 |
+
from selenium.webdriver.support.wait import WebDriverWait
|
| 8 |
+
|
| 9 |
+
from agency_swarm.tools import BaseTool
|
| 10 |
+
from .util import get_b64_screenshot, remove_highlight_and_labels
|
| 11 |
+
from .util.selenium import get_web_driver
|
| 12 |
+
from agency_swarm.util import get_openai_client
|
| 13 |
+
|
| 14 |
+
|
| 15 |
+
class SolveCaptcha(BaseTool):
|
| 16 |
+
"""
|
| 17 |
+
This tool asks a human to solve captcha on the current webpage. Make sure that captcha is visible before running it.
|
| 18 |
+
"""
|
| 19 |
+
|
| 20 |
+
def run(self):
|
| 21 |
+
wd = get_web_driver()
|
| 22 |
+
|
| 23 |
+
try:
|
| 24 |
+
WebDriverWait(wd, 10).until(
|
| 25 |
+
frame_to_be_available_and_switch_to_it((By.XPATH, "//iframe[@title='reCAPTCHA']"))
|
| 26 |
+
)
|
| 27 |
+
|
| 28 |
+
element = WebDriverWait(wd, 3).until(
|
| 29 |
+
presence_of_element_located((By.ID, "recaptcha-anchor"))
|
| 30 |
+
)
|
| 31 |
+
except Exception as e:
|
| 32 |
+
return "Could not find captcha checkbox"
|
| 33 |
+
|
| 34 |
+
try:
|
| 35 |
+
# Scroll the element into view
|
| 36 |
+
wd.execute_script("arguments[0].scrollIntoView(true);", element)
|
| 37 |
+
time.sleep(1) # Give some time for the scrolling to complete
|
| 38 |
+
|
| 39 |
+
# Click the element using JavaScript
|
| 40 |
+
wd.execute_script("arguments[0].click();", element)
|
| 41 |
+
except Exception as e:
|
| 42 |
+
return f"Could not click captcha checkbox: {str(e)}"
|
| 43 |
+
|
| 44 |
+
try:
|
| 45 |
+
# Now check if the reCAPTCHA is checked
|
| 46 |
+
WebDriverWait(wd, 3).until(
|
| 47 |
+
lambda d: d.find_element(By.CLASS_NAME, "recaptcha-checkbox").get_attribute(
|
| 48 |
+
"aria-checked") == "true"
|
| 49 |
+
)
|
| 50 |
+
|
| 51 |
+
return "Success"
|
| 52 |
+
except Exception as e:
|
| 53 |
+
pass
|
| 54 |
+
|
| 55 |
+
wd.switch_to.default_content()
|
| 56 |
+
|
| 57 |
+
client = get_openai_client()
|
| 58 |
+
|
| 59 |
+
WebDriverWait(wd, 10).until(
|
| 60 |
+
frame_to_be_available_and_switch_to_it(
|
| 61 |
+
(By.XPATH, "//iframe[@title='recaptcha challenge expires in two minutes']"))
|
| 62 |
+
)
|
| 63 |
+
|
| 64 |
+
time.sleep(2)
|
| 65 |
+
|
| 66 |
+
attempts = 0
|
| 67 |
+
while attempts < 5:
|
| 68 |
+
tiles = wd.find_elements(By.CLASS_NAME, "rc-imageselect-tile")
|
| 69 |
+
|
| 70 |
+
# filter out tiles with rc-imageselect-dynamic-selected class
|
| 71 |
+
tiles = [tile for tile in tiles if
|
| 72 |
+
not tile.get_attribute("class").endswith("rc-imageselect-dynamic-selected")]
|
| 73 |
+
|
| 74 |
+
image_content = []
|
| 75 |
+
i = 0
|
| 76 |
+
for tile in tiles:
|
| 77 |
+
i += 1
|
| 78 |
+
screenshot = get_b64_screenshot(wd, tile)
|
| 79 |
+
|
| 80 |
+
image_content.append(
|
| 81 |
+
{
|
| 82 |
+
"type": "text",
|
| 83 |
+
"text": f"Image {i}:",
|
| 84 |
+
}
|
| 85 |
+
)
|
| 86 |
+
image_content.append(
|
| 87 |
+
{
|
| 88 |
+
"type": "image_url",
|
| 89 |
+
"image_url":
|
| 90 |
+
{
|
| 91 |
+
"url": f"data:image/jpeg;base64,{screenshot}",
|
| 92 |
+
"detail": "high",
|
| 93 |
+
}
|
| 94 |
+
},
|
| 95 |
+
)
|
| 96 |
+
# highlight all titles with rc-imageselect-tile class but not with rc-imageselect-dynamic-selected
|
| 97 |
+
# wd = highlight_elements_with_labels(wd, 'td.rc-imageselect-tile:not(.rc-imageselect-dynamic-selected)')
|
| 98 |
+
|
| 99 |
+
# screenshot = get_b64_screenshot(wd, wd.find_element(By.ID, "rc-imageselect"))
|
| 100 |
+
|
| 101 |
+
task_text = wd.find_element(By.CLASS_NAME, "rc-imageselect-instructions").text.strip().replace("\n",
|
| 102 |
+
" ")
|
| 103 |
+
|
| 104 |
+
continuous_task = 'once there are none left' in task_text.lower()
|
| 105 |
+
|
| 106 |
+
task_text = task_text.replace("Click verify", "Output 0")
|
| 107 |
+
task_text = task_text.replace("click skip", "Output 0")
|
| 108 |
+
task_text = task_text.replace("once", "if")
|
| 109 |
+
task_text = task_text.replace("none left", "none")
|
| 110 |
+
task_text = task_text.replace("all", "only")
|
| 111 |
+
task_text = task_text.replace("squares", "images")
|
| 112 |
+
|
| 113 |
+
additional_info = ""
|
| 114 |
+
if len(tiles) > 9:
|
| 115 |
+
additional_info = ("Keep in mind that all images are a part of a bigger image "
|
| 116 |
+
"from left to right, and top to bottom. The grid is 4x4. ")
|
| 117 |
+
|
| 118 |
+
messages = [
|
| 119 |
+
{
|
| 120 |
+
"role": "system",
|
| 121 |
+
"content": f"""You are an advanced AI designed to support users with visual impairments.
|
| 122 |
+
User will provide you with {i} images numbered from 1 to {i}. Your task is to output
|
| 123 |
+
the numbers of the images that contain the requested object, or at least some part of the requested
|
| 124 |
+
object. {additional_info}If there are no individual images that satisfy this condition, output 0.
|
| 125 |
+
""".replace("\n", ""),
|
| 126 |
+
},
|
| 127 |
+
{
|
| 128 |
+
"role": "user",
|
| 129 |
+
"content": [
|
| 130 |
+
*image_content,
|
| 131 |
+
{
|
| 132 |
+
"type": "text",
|
| 133 |
+
"text": f"{task_text}. Only output numbers separated by commas and nothing else. "
|
| 134 |
+
f"Output 0 if there are none."
|
| 135 |
+
}
|
| 136 |
+
]
|
| 137 |
+
}]
|
| 138 |
+
|
| 139 |
+
response = client.chat.completions.create(
|
| 140 |
+
model="gpt-4o",
|
| 141 |
+
messages=messages,
|
| 142 |
+
max_tokens=1024,
|
| 143 |
+
temperature=0.0,
|
| 144 |
+
)
|
| 145 |
+
|
| 146 |
+
message = response.choices[0].message
|
| 147 |
+
message_text = message.content
|
| 148 |
+
|
| 149 |
+
# check if 0 is in the message
|
| 150 |
+
if "0" in message_text and "10" not in message_text:
|
| 151 |
+
# Find the button by its ID
|
| 152 |
+
verify_button = wd.find_element(By.ID, "recaptcha-verify-button")
|
| 153 |
+
|
| 154 |
+
verify_button_text = verify_button.text
|
| 155 |
+
|
| 156 |
+
# Click the button
|
| 157 |
+
wd.execute_script("arguments[0].click();", verify_button)
|
| 158 |
+
|
| 159 |
+
time.sleep(1)
|
| 160 |
+
|
| 161 |
+
try:
|
| 162 |
+
if self.verify_checkbox(wd):
|
| 163 |
+
return "Success. Captcha solved."
|
| 164 |
+
except Exception as e:
|
| 165 |
+
print('Not checked')
|
| 166 |
+
pass
|
| 167 |
+
|
| 168 |
+
else:
|
| 169 |
+
numbers = [int(s.strip()) for s in message_text.split(",") if s.strip().isdigit()]
|
| 170 |
+
|
| 171 |
+
# Click the tiles based on the provided numbers
|
| 172 |
+
for number in numbers:
|
| 173 |
+
wd.execute_script("arguments[0].click();", tiles[number - 1])
|
| 174 |
+
time.sleep(0.5)
|
| 175 |
+
|
| 176 |
+
time.sleep(3)
|
| 177 |
+
|
| 178 |
+
if not continuous_task:
|
| 179 |
+
# Find the button by its ID
|
| 180 |
+
verify_button = wd.find_element(By.ID, "recaptcha-verify-button")
|
| 181 |
+
|
| 182 |
+
verify_button_text = verify_button.text
|
| 183 |
+
|
| 184 |
+
# Click the button
|
| 185 |
+
wd.execute_script("arguments[0].click();", verify_button)
|
| 186 |
+
|
| 187 |
+
try:
|
| 188 |
+
if self.verify_checkbox(wd):
|
| 189 |
+
return "Success. Captcha solved."
|
| 190 |
+
except Exception as e:
|
| 191 |
+
pass
|
| 192 |
+
else:
|
| 193 |
+
continue
|
| 194 |
+
|
| 195 |
+
if "verify" in verify_button_text.lower():
|
| 196 |
+
attempts += 1
|
| 197 |
+
|
| 198 |
+
wd = remove_highlight_and_labels(wd)
|
| 199 |
+
|
| 200 |
+
wd.switch_to.default_content()
|
| 201 |
+
|
| 202 |
+
# close captcha
|
| 203 |
+
try:
|
| 204 |
+
element = WebDriverWait(wd, 3).until(
|
| 205 |
+
presence_of_element_located((By.XPATH, "//iframe[@title='reCAPTCHA']"))
|
| 206 |
+
)
|
| 207 |
+
|
| 208 |
+
wd.execute_script(f"document.elementFromPoint({element.location['x']}, {element.location['y']-10}).click();")
|
| 209 |
+
except Exception as e:
|
| 210 |
+
print(e)
|
| 211 |
+
pass
|
| 212 |
+
|
| 213 |
+
return "Could not solve captcha."
|
| 214 |
+
|
| 215 |
+
def verify_checkbox(self, wd):
|
| 216 |
+
wd.switch_to.default_content()
|
| 217 |
+
|
| 218 |
+
try:
|
| 219 |
+
WebDriverWait(wd, 10).until(
|
| 220 |
+
frame_to_be_available_and_switch_to_it((By.XPATH, "//iframe[@title='reCAPTCHA']"))
|
| 221 |
+
)
|
| 222 |
+
|
| 223 |
+
WebDriverWait(wd, 5).until(
|
| 224 |
+
lambda d: d.find_element(By.CLASS_NAME, "recaptcha-checkbox").get_attribute(
|
| 225 |
+
"aria-checked") == "true"
|
| 226 |
+
)
|
| 227 |
+
|
| 228 |
+
return True
|
| 229 |
+
except Exception as e:
|
| 230 |
+
wd.switch_to.default_content()
|
| 231 |
+
|
| 232 |
+
WebDriverWait(wd, 10).until(
|
| 233 |
+
frame_to_be_available_and_switch_to_it(
|
| 234 |
+
(By.XPATH, "//iframe[@title='recaptcha challenge expires in two minutes']"))
|
| 235 |
+
)
|
| 236 |
+
|
| 237 |
+
return False
|
| 238 |
+
|
BrowsingAgent/tools/WebPageSummarizer.py
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from selenium.webdriver.common.by import By
|
| 2 |
+
|
| 3 |
+
from agency_swarm.tools import BaseTool
|
| 4 |
+
from .util import get_web_driver, set_web_driver
|
| 5 |
+
|
| 6 |
+
|
| 7 |
+
class WebPageSummarizer(BaseTool):
|
| 8 |
+
"""
|
| 9 |
+
This tool summarizes the content of the current web page, extracting the main points and providing a concise summary.
|
| 10 |
+
"""
|
| 11 |
+
|
| 12 |
+
def run(self):
|
| 13 |
+
from agency_swarm import get_openai_client
|
| 14 |
+
|
| 15 |
+
wd = get_web_driver()
|
| 16 |
+
client = get_openai_client()
|
| 17 |
+
|
| 18 |
+
content = wd.find_element(By.TAG_NAME, "body").text
|
| 19 |
+
|
| 20 |
+
# only use the first 10000 characters
|
| 21 |
+
content = " ".join(content.split()[:10000])
|
| 22 |
+
|
| 23 |
+
completion = client.chat.completions.create(
|
| 24 |
+
model="gpt-3.5-turbo",
|
| 25 |
+
messages=[
|
| 26 |
+
{"role": "system", "content": "Your task is to summarize the content of the provided webpage. The summary should be concise and informative, capturing the main points and takeaways of the page."},
|
| 27 |
+
{"role": "user", "content": "Summarize the content of the following webpage:\n\n" + content},
|
| 28 |
+
],
|
| 29 |
+
temperature=0.0,
|
| 30 |
+
)
|
| 31 |
+
|
| 32 |
+
return completion.choices[0].message.content
|
| 33 |
+
|
| 34 |
+
if __name__ == "__main__":
|
| 35 |
+
wd = get_web_driver()
|
| 36 |
+
wd.get("https://en.wikipedia.org/wiki/Python_(programming_language)")
|
| 37 |
+
set_web_driver(wd)
|
| 38 |
+
tool = WebPageSummarizer()
|
| 39 |
+
print(tool.run())
|
BrowsingAgent/tools/__init__.py
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from .Scroll import Scroll
|
| 2 |
+
from .ReadURL import ReadURL
|
| 3 |
+
from .SendKeys import SendKeys
|
| 4 |
+
from .ClickElement import ClickElement
|
| 5 |
+
from .GoBack import GoBack
|
| 6 |
+
from .SelectDropdown import SelectDropdown
|
| 7 |
+
from .SolveCaptcha import SolveCaptcha
|
| 8 |
+
from .ExportFile import ExportFile
|
| 9 |
+
from .WebPageSummarizer import WebPageSummarizer
|
BrowsingAgent/tools/__pycache__/ClickElement.cpython-312.pyc
ADDED
|
Binary file (3.4 kB). View file
|
|
|
BrowsingAgent/tools/__pycache__/ExportFile.cpython-312.pyc
ADDED
|
Binary file (2.08 kB). View file
|
|
|
BrowsingAgent/tools/__pycache__/GoBack.cpython-312.pyc
ADDED
|
Binary file (1.06 kB). View file
|
|
|
BrowsingAgent/tools/__pycache__/ReadURL.cpython-312.pyc
ADDED
|
Binary file (2.61 kB). View file
|
|
|
BrowsingAgent/tools/__pycache__/Scroll.cpython-312.pyc
ADDED
|
Binary file (2.4 kB). View file
|
|
|
BrowsingAgent/tools/__pycache__/SelectDropdown.cpython-312.pyc
ADDED
|
Binary file (3.27 kB). View file
|
|
|
BrowsingAgent/tools/__pycache__/SendKeys.cpython-312.pyc
ADDED
|
Binary file (4.14 kB). View file
|
|
|
BrowsingAgent/tools/__pycache__/SolveCaptcha.cpython-312.pyc
ADDED
|
Binary file (9.36 kB). View file
|
|
|
BrowsingAgent/tools/__pycache__/WebPageSummarizer.cpython-312.pyc
ADDED
|
Binary file (2.17 kB). View file
|
|
|
BrowsingAgent/tools/__pycache__/__init__.cpython-312.pyc
ADDED
|
Binary file (486 Bytes). View file
|
|
|
BrowsingAgent/tools/util/__init__.py
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from .get_b64_screenshot import get_b64_screenshot
|
| 2 |
+
from .selenium import get_web_driver, set_web_driver
|
| 3 |
+
from .highlights import remove_highlight_and_labels, highlight_elements_with_labels
|
BrowsingAgent/tools/util/__pycache__/__init__.cpython-312.pyc
ADDED
|
Binary file (405 Bytes). View file
|
|
|
BrowsingAgent/tools/util/__pycache__/get_b64_screenshot.cpython-312.pyc
ADDED
|
Binary file (489 Bytes). View file
|
|
|
BrowsingAgent/tools/util/__pycache__/highlights.cpython-312.pyc
ADDED
|
Binary file (5.74 kB). View file
|
|
|
BrowsingAgent/tools/util/__pycache__/selenium.cpython-312.pyc
ADDED
|
Binary file (7.38 kB). View file
|
|
|
BrowsingAgent/tools/util/get_b64_screenshot.py
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
def get_b64_screenshot(wd, element=None):
|
| 3 |
+
if element:
|
| 4 |
+
screenshot_b64 = element.screenshot_as_base64
|
| 5 |
+
else:
|
| 6 |
+
screenshot_b64 = wd.get_screenshot_as_base64()
|
| 7 |
+
|
| 8 |
+
return screenshot_b64
|
BrowsingAgent/tools/util/highlights.py
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
def highlight_elements_with_labels(driver, selector):
|
| 2 |
+
"""
|
| 3 |
+
This function highlights clickable elements like buttons, links, and certain divs and spans
|
| 4 |
+
that match the given CSS selector on the webpage with a red border and ensures that labels are visible and positioned
|
| 5 |
+
correctly within the viewport.
|
| 6 |
+
|
| 7 |
+
:param driver: Instance of Selenium WebDriver.
|
| 8 |
+
:param selector: CSS selector for the elements to be highlighted.
|
| 9 |
+
"""
|
| 10 |
+
script = f"""
|
| 11 |
+
// Helper function to check if an element is visible
|
| 12 |
+
function isElementVisible(element) {{
|
| 13 |
+
var rect = element.getBoundingClientRect();
|
| 14 |
+
if (rect.width <= 0 || rect.height <= 0 ||
|
| 15 |
+
rect.top >= (window.innerHeight || document.documentElement.clientHeight) ||
|
| 16 |
+
rect.bottom <= 0 ||
|
| 17 |
+
rect.left >= (window.innerWidth || document.documentElement.clientWidth) ||
|
| 18 |
+
rect.right <= 0) {{
|
| 19 |
+
return false;
|
| 20 |
+
}}
|
| 21 |
+
// Check if any parent element is hidden, which would hide this element as well
|
| 22 |
+
var parent = element;
|
| 23 |
+
while (parent) {{
|
| 24 |
+
var style = window.getComputedStyle(parent);
|
| 25 |
+
if (style.display === 'none' || style.visibility === 'hidden') {{
|
| 26 |
+
return false;
|
| 27 |
+
}}
|
| 28 |
+
parent = parent.parentElement;
|
| 29 |
+
}}
|
| 30 |
+
return true;
|
| 31 |
+
}}
|
| 32 |
+
|
| 33 |
+
// Remove previous labels and styles if they exist
|
| 34 |
+
document.querySelectorAll('.highlight-label').forEach(function(label) {{
|
| 35 |
+
label.remove();
|
| 36 |
+
}});
|
| 37 |
+
document.querySelectorAll('.highlighted-element').forEach(function(element) {{
|
| 38 |
+
element.classList.remove('highlighted-element');
|
| 39 |
+
element.removeAttribute('data-highlighted');
|
| 40 |
+
}});
|
| 41 |
+
|
| 42 |
+
// Inject custom style for highlighting elements
|
| 43 |
+
var styleElement = document.getElementById('highlight-style');
|
| 44 |
+
if (!styleElement) {{
|
| 45 |
+
styleElement = document.createElement('style');
|
| 46 |
+
styleElement.id = 'highlight-style';
|
| 47 |
+
document.head.appendChild(styleElement);
|
| 48 |
+
}}
|
| 49 |
+
styleElement.textContent = `
|
| 50 |
+
.highlighted-element {{
|
| 51 |
+
border: 2px solid red !important;
|
| 52 |
+
position: relative;
|
| 53 |
+
box-sizing: border-box;
|
| 54 |
+
}}
|
| 55 |
+
.highlight-label {{
|
| 56 |
+
position: absolute;
|
| 57 |
+
z-index: 2147483647;
|
| 58 |
+
background: yellow;
|
| 59 |
+
color: black;
|
| 60 |
+
font-size: 25px;
|
| 61 |
+
padding: 3px 5px;
|
| 62 |
+
border: 1px solid black;
|
| 63 |
+
border-radius: 3px;
|
| 64 |
+
white-space: nowrap;
|
| 65 |
+
box-shadow: 0px 0px 2px #000;
|
| 66 |
+
top: -25px;
|
| 67 |
+
left: 0;
|
| 68 |
+
display: none;
|
| 69 |
+
}}
|
| 70 |
+
`;
|
| 71 |
+
|
| 72 |
+
// Function to create and append a label to the body
|
| 73 |
+
function createAndAdjustLabel(element, index) {{
|
| 74 |
+
if (!isElementVisible(element)) return;
|
| 75 |
+
|
| 76 |
+
element.classList.add('highlighted-element');
|
| 77 |
+
var label = document.createElement('div');
|
| 78 |
+
label.className = 'highlight-label';
|
| 79 |
+
label.textContent = index.toString();
|
| 80 |
+
label.style.display = 'block'; // Make the label visible
|
| 81 |
+
|
| 82 |
+
// Calculate label position
|
| 83 |
+
var rect = element.getBoundingClientRect();
|
| 84 |
+
var top = rect.top + window.scrollY - 25; // Position label above the element
|
| 85 |
+
var left = rect.left + window.scrollX;
|
| 86 |
+
|
| 87 |
+
label.style.top = top + 'px';
|
| 88 |
+
label.style.left = left + 'px';
|
| 89 |
+
|
| 90 |
+
document.body.appendChild(label); // Append the label to the body
|
| 91 |
+
}}
|
| 92 |
+
|
| 93 |
+
// Select all clickable elements and apply the styles
|
| 94 |
+
var allElements = document.querySelectorAll('{selector}');
|
| 95 |
+
var index = 1;
|
| 96 |
+
allElements.forEach(function(element) {{
|
| 97 |
+
// Check if the element is not already highlighted and is visible
|
| 98 |
+
if (!element.dataset.highlighted && isElementVisible(element)) {{
|
| 99 |
+
element.dataset.highlighted = 'true';
|
| 100 |
+
createAndAdjustLabel(element, index++);
|
| 101 |
+
}}
|
| 102 |
+
}});
|
| 103 |
+
"""
|
| 104 |
+
|
| 105 |
+
driver.execute_script(script)
|
| 106 |
+
|
| 107 |
+
return driver
|
| 108 |
+
|
| 109 |
+
|
| 110 |
+
def remove_highlight_and_labels(driver):
|
| 111 |
+
"""
|
| 112 |
+
This function removes all red borders and labels from the webpage elements,
|
| 113 |
+
reversing the changes made by the highlight functions using Selenium WebDriver.
|
| 114 |
+
|
| 115 |
+
:param driver: Instance of Selenium WebDriver.
|
| 116 |
+
"""
|
| 117 |
+
selector = ('a, button, input, textarea, div[onclick], div[role="button"], div[tabindex], span[onclick], '
|
| 118 |
+
'span[role="button"], span[tabindex]')
|
| 119 |
+
script = f"""
|
| 120 |
+
// Remove all labels
|
| 121 |
+
document.querySelectorAll('.highlight-label').forEach(function(label) {{
|
| 122 |
+
label.remove();
|
| 123 |
+
}});
|
| 124 |
+
|
| 125 |
+
// Remove the added style for red borders
|
| 126 |
+
var highlightStyle = document.getElementById('highlight-style');
|
| 127 |
+
if (highlightStyle) {{
|
| 128 |
+
highlightStyle.remove();
|
| 129 |
+
}}
|
| 130 |
+
|
| 131 |
+
// Remove inline styles added by highlighting function
|
| 132 |
+
document.querySelectorAll('{selector}').forEach(function(element) {{
|
| 133 |
+
element.style.border = '';
|
| 134 |
+
}});
|
| 135 |
+
"""
|
| 136 |
+
|
| 137 |
+
driver.execute_script(script)
|
| 138 |
+
|
| 139 |
+
return driver
|
BrowsingAgent/tools/util/selenium.py
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
|
| 3 |
+
wd = None
|
| 4 |
+
|
| 5 |
+
selenium_config = {
|
| 6 |
+
"chrome_profile_path": None,
|
| 7 |
+
"headless": True,
|
| 8 |
+
"full_page_screenshot": True,
|
| 9 |
+
}
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
def get_web_driver():
|
| 13 |
+
print("Initializing WebDriver...")
|
| 14 |
+
try:
|
| 15 |
+
from selenium import webdriver
|
| 16 |
+
from selenium.webdriver.chrome.service import Service as ChromeService
|
| 17 |
+
print("Selenium imported successfully.")
|
| 18 |
+
except ImportError:
|
| 19 |
+
print("Selenium not installed. Please install it with pip install selenium")
|
| 20 |
+
raise ImportError
|
| 21 |
+
|
| 22 |
+
try:
|
| 23 |
+
from webdriver_manager.chrome import ChromeDriverManager
|
| 24 |
+
print("webdriver_manager imported successfully.")
|
| 25 |
+
except ImportError:
|
| 26 |
+
print("webdriver_manager not installed. Please install it with pip install webdriver-manager")
|
| 27 |
+
raise ImportError
|
| 28 |
+
|
| 29 |
+
try:
|
| 30 |
+
from selenium_stealth import stealth
|
| 31 |
+
print("selenium_stealth imported successfully.")
|
| 32 |
+
except ImportError:
|
| 33 |
+
print("selenium_stealth not installed. Please install it with pip install selenium-stealth")
|
| 34 |
+
raise ImportError
|
| 35 |
+
|
| 36 |
+
global wd, selenium_config
|
| 37 |
+
|
| 38 |
+
if wd:
|
| 39 |
+
print("Returning existing WebDriver instance.")
|
| 40 |
+
return wd
|
| 41 |
+
|
| 42 |
+
chrome_profile_path = selenium_config.get("chrome_profile_path", None)
|
| 43 |
+
profile_directory = None
|
| 44 |
+
user_data_dir = None
|
| 45 |
+
if isinstance(chrome_profile_path, str) and os.path.exists(chrome_profile_path):
|
| 46 |
+
profile_directory = os.path.split(chrome_profile_path)[-1].strip("\\").rstrip("/")
|
| 47 |
+
user_data_dir = os.path.split(chrome_profile_path)[0].strip("\\").rstrip("/")
|
| 48 |
+
print(f"Using Chrome profile: {profile_directory}")
|
| 49 |
+
print(f"Using Chrome user data dir: {user_data_dir}")
|
| 50 |
+
print(f"Using Chrome profile path: {chrome_profile_path}")
|
| 51 |
+
|
| 52 |
+
chrome_options = webdriver.ChromeOptions()
|
| 53 |
+
print("ChromeOptions initialized.")
|
| 54 |
+
|
| 55 |
+
chrome_driver_path = "/usr/bin/chromedriver"
|
| 56 |
+
if not os.path.exists(chrome_driver_path):
|
| 57 |
+
print("ChromeDriver not found at /usr/bin/chromedriver. Installing using webdriver_manager.")
|
| 58 |
+
chrome_driver_path = ChromeDriverManager().install()
|
| 59 |
+
else:
|
| 60 |
+
print(f"ChromeDriver found at {chrome_driver_path}.")
|
| 61 |
+
|
| 62 |
+
if selenium_config.get("headless", False):
|
| 63 |
+
chrome_options.add_argument('--headless')
|
| 64 |
+
print("Headless mode enabled.")
|
| 65 |
+
if selenium_config.get("full_page_screenshot", False):
|
| 66 |
+
chrome_options.add_argument("--start-maximized")
|
| 67 |
+
print("Full page screenshot mode enabled.")
|
| 68 |
+
else:
|
| 69 |
+
chrome_options.add_argument("--window-size=1920,1080")
|
| 70 |
+
print("Window size set to 1920,1080.")
|
| 71 |
+
|
| 72 |
+
chrome_options.add_argument("--no-sandbox")
|
| 73 |
+
chrome_options.add_argument("--disable-gpu")
|
| 74 |
+
chrome_options.add_argument("--disable-dev-shm-usage")
|
| 75 |
+
chrome_options.add_argument("--remote-debugging-port=9222")
|
| 76 |
+
chrome_options.add_argument("--disable-extensions")
|
| 77 |
+
chrome_options.add_argument("--disable-popup-blocking")
|
| 78 |
+
chrome_options.add_argument("--ignore-certificate-errors")
|
| 79 |
+
chrome_options.add_argument("--disable-blink-features=AutomationControlled")
|
| 80 |
+
chrome_options.add_argument("--disable-web-security")
|
| 81 |
+
chrome_options.add_argument("--allow-running-insecure-content")
|
| 82 |
+
chrome_options.add_experimental_option("excludeSwitches", ["enable-automation"])
|
| 83 |
+
chrome_options.add_experimental_option("useAutomationExtension", False)
|
| 84 |
+
print("Chrome options configured.")
|
| 85 |
+
|
| 86 |
+
if user_data_dir and profile_directory:
|
| 87 |
+
chrome_options.add_argument(f"user-data-dir={user_data_dir}")
|
| 88 |
+
chrome_options.add_argument(f"profile-directory={profile_directory}")
|
| 89 |
+
print(f"Using user data dir: {user_data_dir} and profile directory: {profile_directory}")
|
| 90 |
+
|
| 91 |
+
try:
|
| 92 |
+
wd = webdriver.Chrome(service=ChromeService(chrome_driver_path), options=chrome_options)
|
| 93 |
+
print("WebDriver initialized successfully.")
|
| 94 |
+
if wd.capabilities['chrome']['userDataDir']:
|
| 95 |
+
print(f"Profile path in use: {wd.capabilities['chrome']['userDataDir']}")
|
| 96 |
+
except Exception as e:
|
| 97 |
+
print(f"Error initializing WebDriver: {e}")
|
| 98 |
+
raise e
|
| 99 |
+
|
| 100 |
+
if not selenium_config.get("chrome_profile_path", None):
|
| 101 |
+
stealth(
|
| 102 |
+
wd,
|
| 103 |
+
languages=["en-US", "en"],
|
| 104 |
+
vendor="Google Inc.",
|
| 105 |
+
platform="Win32",
|
| 106 |
+
webgl_vendor="Intel Inc.",
|
| 107 |
+
renderer="Intel Iris OpenGL Engine",
|
| 108 |
+
fix_hairline=True,
|
| 109 |
+
)
|
| 110 |
+
print("Stealth mode configured.")
|
| 111 |
+
|
| 112 |
+
wd.implicitly_wait(3)
|
| 113 |
+
print("Implicit wait set to 3 seconds.")
|
| 114 |
+
|
| 115 |
+
return wd
|
| 116 |
+
|
| 117 |
+
|
| 118 |
+
def set_web_driver(new_wd):
|
| 119 |
+
# remove all popups
|
| 120 |
+
js_script = """
|
| 121 |
+
var popUpSelectors = ['modal', 'popup', 'overlay', 'dialog']; // Add more selectors that are commonly used for pop-ups
|
| 122 |
+
popUpSelectors.forEach(function(selector) {
|
| 123 |
+
var elements = document.querySelectorAll(selector);
|
| 124 |
+
elements.forEach(function(element) {
|
| 125 |
+
// You can choose to hide or remove; here we're removing the element
|
| 126 |
+
element.parentNode.removeChild(element);
|
| 127 |
+
});
|
| 128 |
+
});
|
| 129 |
+
"""
|
| 130 |
+
|
| 131 |
+
new_wd.execute_script(js_script)
|
| 132 |
+
|
| 133 |
+
# Close LinkedIn specific popups
|
| 134 |
+
if "linkedin.com" in new_wd.current_url:
|
| 135 |
+
linkedin_js_script = """
|
| 136 |
+
var linkedinSelectors = ['div.msg-overlay-list-bubble', 'div.ml4.msg-overlay-list-bubble__tablet-height'];
|
| 137 |
+
linkedinSelectors.forEach(function(selector) {
|
| 138 |
+
var elements = document.querySelectorAll(selector);
|
| 139 |
+
elements.forEach(function(element) {
|
| 140 |
+
element.parentNode.removeChild(element);
|
| 141 |
+
});
|
| 142 |
+
});
|
| 143 |
+
"""
|
| 144 |
+
new_wd.execute_script(linkedin_js_script)
|
| 145 |
+
|
| 146 |
+
new_wd.execute_script("document.body.style.zoom='1.2'")
|
| 147 |
+
|
| 148 |
+
global wd
|
| 149 |
+
wd = new_wd
|
| 150 |
+
|
| 151 |
+
|
| 152 |
+
def set_selenium_config(config):
|
| 153 |
+
global selenium_config
|
| 154 |
+
selenium_config = config
|
CompetitorTrackingAgent/CompetitorTrackingAgent.py
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from agency_swarm.agents import Agent
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
class CompetitorTrackingAgent(Agent):
|
| 5 |
+
def __init__(self):
|
| 6 |
+
super().__init__(
|
| 7 |
+
name="CompetitorTrackingAgent",
|
| 8 |
+
description="This agent monitors competitors using web scraping tools like BeautifulSoup or Scrapy.",
|
| 9 |
+
instructions="./instructions.md",
|
| 10 |
+
files_folder="./files",
|
| 11 |
+
schemas_folder="./schemas",
|
| 12 |
+
tools=[],
|
| 13 |
+
tools_folder="./tools",
|
| 14 |
+
temperature=0.3,
|
| 15 |
+
max_prompt_tokens=25000,
|
| 16 |
+
)
|
| 17 |
+
|
| 18 |
+
def response_validator(self, message):
|
| 19 |
+
return message
|
CompetitorTrackingAgent/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
from .CompetitorTrackingAgent import CompetitorTrackingAgent
|
CompetitorTrackingAgent/__pycache__/CompetitorTrackingAgent.cpython-312.pyc
ADDED
|
Binary file (1.16 kB). View file
|
|
|
CompetitorTrackingAgent/__pycache__/__init__.cpython-312.pyc
ADDED
|
Binary file (227 Bytes). View file
|
|
|
CompetitorTrackingAgent/instructions.md
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# CompetitorTrackingAgent Instructions
|
| 2 |
+
|
| 3 |
+
You are an agent responsible for monitoring competitors by using web scraping tools like BeautifulSoup or Scrapy. You must ensure that the data collected is accurate and relevant to the agency's goals.
|
| 4 |
+
|
| 5 |
+
### Primary Instructions:
|
| 6 |
+
1. Identify competitor websites and online platforms that provide relevant information.
|
| 7 |
+
2. Use web scraping tools like BeautifulSoup or Scrapy to extract data from these sources.
|
| 8 |
+
3. Validate and clean the scraped data to ensure its accuracy and integrity.
|
| 9 |
+
4. Store the collected data in a structured format that is accessible to other agents within the agency.
|
| 10 |
+
5. Collaborate with other agents to provide them with the necessary data for their tasks.
|
| 11 |
+
6. Monitor the competitor websites for any changes or updates and adjust the data collection process accordingly.
|
CompetitorTrackingAgent/tools/WebScrapingTool.py
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from agency_swarm.tools import BaseTool
|
| 2 |
+
from pydantic import Field
|
| 3 |
+
import requests
|
| 4 |
+
from bs4 import BeautifulSoup
|
| 5 |
+
|
| 6 |
+
class WebScrapingTool(BaseTool):
|
| 7 |
+
"""
|
| 8 |
+
A tool for performing web scraping tasks using BeautifulSoup.
|
| 9 |
+
This tool fetches a web page, parses the HTML content, and extracts specific data based on given criteria or tags.
|
| 10 |
+
"""
|
| 11 |
+
|
| 12 |
+
url: str = Field(
|
| 13 |
+
..., description="The URL of the web page to scrape."
|
| 14 |
+
)
|
| 15 |
+
tag: str = Field(
|
| 16 |
+
..., description="The HTML tag to search for in the web page."
|
| 17 |
+
)
|
| 18 |
+
attribute: str = Field(
|
| 19 |
+
None, description="The attribute of the HTML tag to filter by, if any."
|
| 20 |
+
)
|
| 21 |
+
attribute_value: str = Field(
|
| 22 |
+
None, description="The value of the attribute to filter by, if any."
|
| 23 |
+
)
|
| 24 |
+
|
| 25 |
+
def run(self):
|
| 26 |
+
"""
|
| 27 |
+
Fetches the web page, parses the HTML content, and extracts data based on the specified tag and attribute.
|
| 28 |
+
"""
|
| 29 |
+
try:
|
| 30 |
+
# Make an HTTP request to fetch the web page
|
| 31 |
+
response = requests.get(self.url)
|
| 32 |
+
response.raise_for_status() # Raise an error for bad responses
|
| 33 |
+
|
| 34 |
+
# Parse the HTML content using BeautifulSoup
|
| 35 |
+
soup = BeautifulSoup(response.content, 'html.parser')
|
| 36 |
+
|
| 37 |
+
# Find all elements matching the specified tag and attribute
|
| 38 |
+
if self.attribute and self.attribute_value:
|
| 39 |
+
elements = soup.find_all(self.tag, {self.attribute: self.attribute_value})
|
| 40 |
+
else:
|
| 41 |
+
elements = soup.find_all(self.tag)
|
| 42 |
+
|
| 43 |
+
# Extract and return the text content of the found elements
|
| 44 |
+
extracted_data = [element.get_text(strip=True) for element in elements]
|
| 45 |
+
return extracted_data
|
| 46 |
+
|
| 47 |
+
except requests.RequestException as e:
|
| 48 |
+
return f"An error occurred while fetching the web page: {e}"
|
| 49 |
+
except Exception as e:
|
| 50 |
+
return f"An error occurred during parsing or extraction: {e}"
|
| 51 |
+
|
| 52 |
+
if __name__ == "__main__":
|
| 53 |
+
tool = WebScrapingTool(
|
| 54 |
+
url="https://example.com",
|
| 55 |
+
tag="p",
|
| 56 |
+
attribute="class",
|
| 57 |
+
attribute_value="content"
|
| 58 |
+
)
|
| 59 |
+
print(tool.run())
|
CompetitorTrackingAgent/tools/__pycache__/WebScrapingTool.cpython-312.pyc
ADDED
|
Binary file (2.85 kB). View file
|
|
|
DataAnalystAgent/DataAnalystAgent.py
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from agency_swarm.agents import Agent
|
| 2 |
+
from agency_swarm.tools import CodeInterpreter
|
| 3 |
+
|
| 4 |
+
class DataAnalystAgent(Agent):
|
| 5 |
+
def __init__(self):
|
| 6 |
+
super().__init__(
|
| 7 |
+
name="DataAnalystAgent",
|
| 8 |
+
description="This agent analyzes the collected data to identify trends, opportunities, and insights.",
|
| 9 |
+
instructions="./instructions.md",
|
| 10 |
+
files_folder="./files",
|
| 11 |
+
schemas_folder="./schemas",
|
| 12 |
+
tools=[CodeInterpreter],
|
| 13 |
+
tools_folder="./tools",
|
| 14 |
+
temperature=0.3,
|
| 15 |
+
max_prompt_tokens=25000,
|
| 16 |
+
)
|
| 17 |
+
|
| 18 |
+
def response_validator(self, message):
|
| 19 |
+
return message
|
DataAnalystAgent/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
from .DataAnalystAgent import DataAnalystAgent
|
DataAnalystAgent/__pycache__/DataAnalystAgent.cpython-312.pyc
ADDED
|
Binary file (1.2 kB). View file
|
|
|
DataAnalystAgent/__pycache__/__init__.cpython-312.pyc
ADDED
|
Binary file (213 Bytes). View file
|
|
|
DataAnalystAgent/instructions.md
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# DataAnalystAgent Instructions
|
| 2 |
+
|
| 3 |
+
You are an agent responsible for analyzing the collected market data to identify trends, opportunities, and insights. You must use data analysis tools and libraries like Pandas and NumPy to perform your tasks effectively.
|
| 4 |
+
|
| 5 |
+
### Primary Instructions:
|
| 6 |
+
1. Access the structured data collected by the DataCollectorAgent.
|
| 7 |
+
2. Use data analysis libraries such as Pandas and NumPy to process and analyze the data.
|
| 8 |
+
3. Identify trends, patterns, and anomalies in the data that could provide valuable insights.
|
| 9 |
+
4. Generate reports and visualizations to communicate your findings to other agents and clients.
|
| 10 |
+
5. Collaborate with other agents to ensure your analysis aligns with the agency's goals and objectives.
|
| 11 |
+
6. Continuously update your analysis based on new data and feedback from clients and stakeholders.
|
DataAnalystAgent/tools/DataAnalysisTool.py
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from agency_swarm.tools import BaseTool
|
| 2 |
+
from pydantic import Field
|
| 3 |
+
import pandas as pd
|
| 4 |
+
import numpy as np
|
| 5 |
+
import matplotlib.pyplot as plt
|
| 6 |
+
import seaborn as sns
|
| 7 |
+
import io
|
| 8 |
+
|
| 9 |
+
class DataAnalysisTool(BaseTool):
|
| 10 |
+
"""
|
| 11 |
+
This tool utilizes data analysis libraries such as Pandas and NumPy to process and analyze structured data.
|
| 12 |
+
It is capable of identifying trends, patterns, and anomalies in the data.
|
| 13 |
+
The tool also generates reports and visualizations to communicate findings.
|
| 14 |
+
"""
|
| 15 |
+
|
| 16 |
+
data: str = Field(
|
| 17 |
+
..., description="The structured data in CSV format to be analyzed."
|
| 18 |
+
)
|
| 19 |
+
|
| 20 |
+
def run(self):
|
| 21 |
+
"""
|
| 22 |
+
Processes and analyzes the provided structured data.
|
| 23 |
+
Identifies trends, patterns, and anomalies, and generates reports and visualizations.
|
| 24 |
+
"""
|
| 25 |
+
# Load data into a Pandas DataFrame
|
| 26 |
+
data_io = io.StringIO(self.data)
|
| 27 |
+
df = pd.read_csv(data_io)
|
| 28 |
+
|
| 29 |
+
# Perform basic data analysis
|
| 30 |
+
summary = self._generate_summary(df)
|
| 31 |
+
trends = self._identify_trends(df)
|
| 32 |
+
anomalies = self._detect_anomalies(df)
|
| 33 |
+
|
| 34 |
+
# Generate visualizations
|
| 35 |
+
visualizations = self._generate_visualizations(df)
|
| 36 |
+
|
| 37 |
+
# Compile the report
|
| 38 |
+
report = {
|
| 39 |
+
"summary": summary,
|
| 40 |
+
"trends": trends,
|
| 41 |
+
"anomalies": anomalies,
|
| 42 |
+
"visualizations": visualizations
|
| 43 |
+
}
|
| 44 |
+
|
| 45 |
+
return report
|
| 46 |
+
|
| 47 |
+
def _generate_summary(self, df):
|
| 48 |
+
"""
|
| 49 |
+
Generates a summary of the data including basic statistics.
|
| 50 |
+
"""
|
| 51 |
+
summary = df.describe().to_dict()
|
| 52 |
+
return summary
|
| 53 |
+
|
| 54 |
+
def _identify_trends(self, df):
|
| 55 |
+
"""
|
| 56 |
+
Identifies trends in the data using rolling averages.
|
| 57 |
+
"""
|
| 58 |
+
trends = {}
|
| 59 |
+
for column in df.select_dtypes(include=[np.number]).columns:
|
| 60 |
+
trends[column] = df[column].rolling(window=5).mean().dropna().tolist()
|
| 61 |
+
return trends
|
| 62 |
+
|
| 63 |
+
def _detect_anomalies(self, df):
|
| 64 |
+
"""
|
| 65 |
+
Detects anomalies in the data using z-score method.
|
| 66 |
+
"""
|
| 67 |
+
anomalies = {}
|
| 68 |
+
for column in df.select_dtypes(include=[np.number]).columns:
|
| 69 |
+
z_scores = np.abs((df[column] - df[column].mean()) / df[column].std())
|
| 70 |
+
anomalies[column] = df[column][z_scores > 3].tolist()
|
| 71 |
+
return anomalies
|
| 72 |
+
|
| 73 |
+
def _generate_visualizations(self, df):
|
| 74 |
+
"""
|
| 75 |
+
Generates visualizations for the data.
|
| 76 |
+
"""
|
| 77 |
+
visualizations = {}
|
| 78 |
+
for column in df.select_dtypes(include=[np.number]).columns:
|
| 79 |
+
plt.figure(figsize=(10, 6))
|
| 80 |
+
sns.lineplot(data=df, x=df.index, y=column)
|
| 81 |
+
plt.title(f'Trend for {column}')
|
| 82 |
+
plt.xlabel('Index')
|
| 83 |
+
plt.ylabel(column)
|
| 84 |
+
buf = io.BytesIO()
|
| 85 |
+
plt.savefig(buf, format='png')
|
| 86 |
+
buf.seek(0)
|
| 87 |
+
visualizations[column] = buf.getvalue()
|
| 88 |
+
plt.close()
|
| 89 |
+
return visualizations
|