""" Description: These Python modules are designed to capture detailed browser usage datafor analysis, with both server and client components working together to record and store the information. Author: Carlos A. Planchón https://github.com/carlosplanchon/ Adapt this code to your needs. Feedback is appreciated! """ ##################### # # # --- UTILS --- # # # ##################### import base64 def b64_to_png(b64_string: str, output_file): """ Convert a Base64-encoded string to a PNG file. :param b64_string: A string containing Base64-encoded data :param output_file: The path to the output PNG file """ with open(output_file, 'wb') as f: f.write(base64.b64decode(b64_string)) ################################################################### # # # --- FASTAPI API TO RECORD AND SAVE Browser-Use ACTIVITY --- # # # ################################################################### # Save to api.py and run with `python api.py` # ! pip install uvicorn # ! pip install fastapi # ! pip install prettyprinter import json from pathlib import Path import prettyprinter from fastapi import FastAPI, Request prettyprinter.install_extras() app = FastAPI() @app.post('/post_agent_history_step') async def post_agent_history_step(request: Request): data = await request.json() prettyprinter.cpprint(data) # Ensure the "recordings" folder exists using pathlib recordings_folder = Path('recordings') recordings_folder.mkdir(exist_ok=True) # Determine the next file number by examining existing .json files existing_numbers = [] for item in recordings_folder.iterdir(): if item.is_file() and item.suffix == '.json': try: file_num = int(item.stem) existing_numbers.append(file_num) except ValueError: # In case the file name isn't just a number ... if existing_numbers: next_number = max(existing_numbers) + 1 else: next_number = 1 # Construct the file path file_path = recordings_folder / f'{next_number}.json' # Save the JSON data to the file with file_path.open('w') as f: json.dump(data, f, indent=2) return {'status': 'ok', 'message': f'Saved to {file_path}'} if __name__ == '__main__': import uvicorn uvicorn.run(app, host='0.0.0.0', port=9000) ############################################################## # # # --- CLIENT TO RECORD AND SAVE Browser-Use ACTIVITY --- # # # ############################################################## """ pyobjtojson: A Python library to safely and recursively serialize any Python object (including Pydantic models and dataclasses) into JSON-ready structures, gracefully handling circular references. """ # ! pip install -U pyobjtojson # ! pip install -U prettyprinter import asyncio import os import sys sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))) from dotenv import load_dotenv load_dotenv() import requests from langchain_openai import ChatOpenAI from pyobjtojson import obj_to_json from browser_use import Agent # import prettyprinter # prettyprinter.install_extras() def send_agent_history_step(data): url = 'http://127.0.0.1:9000/post_agent_history_step' response = requests.post(url, json=data) return response.json() async def record_activity(agent_obj): website_html = None website_screenshot = None urls_json_last_elem = None model_thoughts_last_elem = None model_outputs_json_last_elem = None model_actions_json_last_elem = None extracted_content_json_last_elem = None print('--- ON_STEP_START HOOK ---') website_html: str = await agent_obj.browser_context.get_page_html() website_screenshot: str = await agent_obj.browser_context.take_screenshot() print('--> History:') if hasattr(agent_obj, 'state'): history = agent_obj.state.history else: history = None model_thoughts = obj_to_json(obj=history.model_thoughts(), check_circular=False) # print("--- MODEL THOUGHTS ---") if len(model_thoughts) > 0: model_thoughts_last_elem = model_thoughts[-1] # prettyprinter.cpprint(model_thoughts_last_elem) # print("--- MODEL OUTPUT ACTION ---") model_outputs = agent_obj.state.history.model_outputs() model_outputs_json = obj_to_json(obj=model_outputs, check_circular=False) if len(model_outputs_json) > 0: model_outputs_json_last_elem = model_outputs_json[-1] # prettyprinter.cpprint(model_outputs_json_last_elem) # print("--- MODEL INTERACTED ELEM ---") model_actions = agent_obj.state.history.model_actions() model_actions_json = obj_to_json(obj=model_actions, check_circular=False) if len(model_actions_json) > 0: model_actions_json_last_elem = model_actions_json[-1] # prettyprinter.cpprint(model_actions_json_last_elem) # print("--- EXTRACTED CONTENT ---") extracted_content = agent_obj.state.history.extracted_content() extracted_content_json = obj_to_json(obj=extracted_content, check_circular=False) if len(extracted_content_json) > 0: extracted_content_json_last_elem = extracted_content_json[-1] # prettyprinter.cpprint(extracted_content_json_last_elem) # print("--- URLS ---") urls = agent_obj.state.history.urls() # prettyprinter.cpprint(urls) urls_json = obj_to_json(obj=urls, check_circular=False) if len(urls_json) > 0: urls_json_last_elem = urls_json[-1] # prettyprinter.cpprint(urls_json_last_elem) model_step_summary = { 'website_html': website_html, 'website_screenshot': website_screenshot, 'url': urls_json_last_elem, 'model_thoughts': model_thoughts_last_elem, 'model_outputs': model_outputs_json_last_elem, 'model_actions': model_actions_json_last_elem, 'extracted_content': extracted_content_json_last_elem, } print('--- MODEL STEP SUMMARY ---') # prettyprinter.cpprint(model_step_summary) send_agent_history_step(data=model_step_summary) # response = send_agent_history_step(data=history) # print(response) # print("--> Website HTML:") # print(website_html[:200]) # print("--> Website Screenshot:") # print(website_screenshot[:200]) agent = Agent( task='Compare the price of gpt-4o and DeepSeek-V3', llm=ChatOpenAI(model='gpt-4o'), ) async def run_agent(): try: await agent.run(on_step_start=record_activity, max_steps=30) except Exception as e: print(e) asyncio.run(run_agent())