Quickstart (Python)
How to use OpenAI Computer Use with Steel
This guide will walk you through how to use OpenAI's computer-use-preview
model with Steel's managed remote browsers to create AI agents that can navigate the web.
Weโll be implementing a simple CUA loop that functions as described below:
Prerequisites
-
Python 3.8+
-
A Steel API key (sign up here)
-
An OpenAI API key with access to the
computer-use-preview
model
Step 1: Setup and Helper Functions
import osimport timeimport base64import jsonimport refrom typing import List, Dictfrom urllib.parse import urlparseimport requestsfrom dotenv import load_dotenvfrom PIL import Imagefrom io import BytesIOload_dotenv(override=True)# Replace with your own API keysSTEEL_API_KEY = os.getenv("STEEL_API_KEY") or "your-steel-api-key-here"OPENAI_API_KEY = os.getenv("OPENAI_API_KEY") or "your-openai-api-key-here"# Replace with your own taskTASK = os.getenv("TASK") or "Go to Wikipedia and search for machine learning"SYSTEM_PROMPT = """You are an expert browser automation assistant operating in an iterative execution loop. Your goal is to efficiently complete tasks using a Chrome browser with full internet access.<CAPABILITIES>* You control a Chrome browser tab and can navigate to any website* You can click, type, scroll, take screenshots, and interact with web elements* You have full internet access and can visit any public website* You can read content, fill forms, search for information, and perform complex multi-step tasks* After each action, you receive a screenshot showing the current state* Use the goto(url) function to navigate directly to URLs - DO NOT try to click address bars or browser UI* Use the back() function to go back to the previous page<COORDINATE_SYSTEM>* The browser viewport has specific dimensions that you must respect* All coordinates (x, y) must be within the viewport bounds* X coordinates must be between 0 and the display width (inclusive)* Y coordinates must be between 0 and the display height (inclusive)* Always ensure your click, move, scroll, and drag coordinates are within these bounds* If you're unsure about element locations, take a screenshot first to see the current state<AUTONOMOUS_EXECUTION>* Work completely independently - make decisions and act immediately without asking questions* Never request clarification, present options, or ask for permission* Make intelligent assumptions based on task context* If something is ambiguous, choose the most logical interpretation and proceed* Take immediate action rather than explaining what you might do* When the task objective is achieved, immediately declare "TASK_COMPLETED:" - do not provide commentary or ask questions<REASONING_STRUCTURE>For each step, you must reason systematically:* Analyze your previous action's success/failure and current state* Identify what specific progress has been made toward the goal* Determine the next immediate objective and how to achieve it* Choose the most efficient action sequence to make progress<EFFICIENCY_PRINCIPLES>* Combine related actions when possible rather than single-step execution* Navigate directly to relevant websites without unnecessary exploration* Use screenshots strategically to understand page state before acting* Be persistent with alternative approaches if initial attempts fail* Focus on the specific information or outcome requested<COMPLETION_CRITERIA>* MANDATORY: When you complete the task, your final message MUST start with "TASK_COMPLETED: [brief summary]"* MANDATORY: If technical issues prevent completion, your final message MUST start with "TASK_FAILED: [reason]"* MANDATORY: If you abandon the task, your final message MUST start with "TASK_ABANDONED: [explanation]"* Do not write anything after completing the task except the required completion message* Do not ask questions, provide commentary, or offer additional help after task completion* The completion message is the end of the interaction - nothing else should follow<CRITICAL_REQUIREMENTS>* This is fully automated execution - work completely independently* Start by taking a screenshot to understand the current state* Use goto(url) function for navigation - never click on browser UI elements* Always respect coordinate boundaries - invalid coordinates will fail* Recognize when the stated objective has been achieved and declare completion immediately* Focus on the explicit task given, not implied or potential follow-up tasksRemember: Be thorough but focused. Complete the specific task requested efficiently and provide clear results."""BLOCKED_DOMAINS = ["maliciousbook.com","evilvideos.com","darkwebforum.com","shadytok.com","suspiciouspins.com","ilanbigio.com",]CUA_KEY_TO_PLAYWRIGHT_KEY = {"/": "Divide","\\": "Backslash","alt": "Alt","arrowdown": "ArrowDown","arrowleft": "ArrowLeft","arrowright": "ArrowRight","arrowup": "ArrowUp","backspace": "Backspace","capslock": "CapsLock","cmd": "Meta","ctrl": "Control","delete": "Delete","end": "End","enter": "Enter","esc": "Escape","home": "Home","insert": "Insert","option": "Alt","pagedown": "PageDown","pageup": "PageUp","shift": "Shift","space": " ","super": "Meta","tab": "Tab","win": "Meta",}def pp(obj):print(json.dumps(obj, indent=4))def show_image(base_64_image):image_data = base64.b64decode(base_64_image)image = Image.open(BytesIO(image_data))image.show()def sanitize_message(msg: dict) -> dict:"""Return a copy of the message with image_url omitted for computer_call_output messages."""if msg.get("type") == "computer_call_output":output = msg.get("output", {})if isinstance(output, dict):sanitized = msg.copy()sanitized["output"] = {**output, "image_url": "[omitted]"}return sanitizedreturn msgdef create_response(**kwargs):url = "https://api.openai.com/v1/responses"headers = {"Authorization": f"Bearer {os.getenv('OPENAI_API_KEY')}","Content-Type": "application/json"}openai_org = os.getenv("OPENAI_ORG")if openai_org:headers["Openai-Organization"] = openai_orgresponse = requests.post(url, headers=headers, json=kwargs)if response.status_code != 200:print(f"Error: {response.status_code} {response.text}")return response.json()def check_blocklisted_url(url: str) -> None:"""Raise ValueError if the given URL (including subdomains) is in the blocklist."""hostname = urlparse(url).hostname or ""if any(hostname == blocked or hostname.endswith(f".{blocked}")for blocked in BLOCKED_DOMAINS):raise ValueError(f"Blocked URL: {url}")
Step 2: Create Steel Browser Integration
1class SteelBrowser:2def __init__(3self,4width: int = 1024,5height: int = 768,6proxy: bool = False,7solve_captcha: bool = False,8virtual_mouse: bool = True,9session_timeout: int = 900000, # 15 minutes10ad_blocker: bool = True,11start_url: str = "https://www.google.com",12):13self.client = Steel(14steel_api_key=os.getenv("STEEL_API_KEY"),15)16self.dimensions = (width, height)17self.proxy = proxy18self.solve_captcha = solve_captcha19self.virtual_mouse = virtual_mouse20self.session_timeout = session_timeout21self.ad_blocker = ad_blocker22self.start_url = start_url23self.session = None24self._playwright = None25self._browser = None26self._page = None2728def get_environment(self):29return "browser"3031def get_dimensions(self):32return self.dimensions3334def get_current_url(self) -> str:35return self._page.url if self._page else ""3637def __enter__(self):38"""Enter context manager - create Steel session and connect browser."""39width, height = self.dimensions40session_params = {41"use_proxy": self.proxy,42"solve_captcha": self.solve_captcha,43"api_timeout": self.session_timeout,44"block_ads": self.ad_blocker,45"dimensions": {"width": width, "height": height}46}47self.session = self.client.sessions.create(**session_params)4849print("Steel Session created successfully!")50print(f"View live session at: {self.session.session_viewer_url}")5152self._playwright = sync_playwright().start()53browser = self._playwright.chromium.connect_over_cdp(54f"{self.session.websocket_url}&apiKey={os.getenv('STEEL_API_KEY')}",55timeout=6000056)57self._browser = browser58context = browser.contexts[0]5960def handle_route(route, request):61url = request.url62try:63check_blocklisted_url(url)64route.continue_()65except ValueError:66print(f"Blocking URL: {url}")67route.abort()6869if self.virtual_mouse:70context.add_init_script("""71if (window.self === window.top) {72function initCursor() {73const CURSOR_ID = '__cursor__';74if (document.getElementById(CURSOR_ID)) return;7576const cursor = document.createElement('div');77cursor.id = CURSOR_ID;78Object.assign(cursor.style, {79position: 'fixed',80top: '0px',81left: '0px',82width: '20px',83height: '20px',84backgroundImage: 'url("data:image/svg+xml;utf8,<svg width=\\'16\\' height=\\'16\\' viewBox=\\'0 0 20 20\\' fill=\\'black\\' outline=\\'white\\' xmlns=\\'http://www.w3.org/2000/svg\\'><path d=\\'M15.8089 7.22221C15.9333 7.00888 15.9911 6.78221 15.9822 6.54221C15.9733 6.29333 15.8978 6.06667 15.7555 5.86221C15.6133 5.66667 15.4311 5.52445 15.2089 5.43555L1.70222 0.0888888C1.47111 0 1.23555 -0.0222222 0.995555 0.0222222C0.746667 0.0755555 0.537779 0.186667 0.368888 0.355555C0.191111 0.533333 0.0755555 0.746667 0.0222222 0.995555C-0.0222222 1.23555 0 1.47111 0.0888888 1.70222L5.43555 15.2222C5.52445 15.4445 5.66667 15.6267 5.86221 15.7689C6.06667 15.9111 6.28888 15.9867 6.52888 15.9955H6.58221C6.82221 15.9955 7.04445 15.9333 7.24888 15.8089C7.44445 15.6845 7.59555 15.52 7.70221 15.3155L10.2089 10.2222L15.3022 7.70221C15.5155 7.59555 15.6845 7.43555 15.8089 7.22221Z\\' ></path></svg>")',85backgroundSize: 'cover',86pointerEvents: 'none',87zIndex: '99999',88transform: 'translate(-2px, -2px)',89});9091document.body.appendChild(cursor);9293document.addEventListener("mousemove", (e) => {94cursor.style.top = e.clientY + "px";95cursor.style.left = e.clientX + "px";96});97}9899requestAnimationFrame(function checkBody() {100if (document.body) {101initCursor();102} else {103requestAnimationFrame(checkBody);104}105});106}107""")108109self._page = context.pages[0]110self._page.route("**/*", handle_route)111112self._page.set_viewport_size({"width": width, "height": height})113114self._page.goto(self.start_url)115116return self117118def __exit__(self, exc_type, exc_val, exc_tb):119if self._page:120self._page.close()121if self._browser:122self._browser.close()123if self._playwright:124self._playwright.stop()125126if self.session:127print("Releasing Steel session...")128self.client.sessions.release(self.session.id)129print(f"Session completed. View replay at {self.session.session_viewer_url}")130131def screenshot(self) -> str:132"""Take a screenshot using Playwright for consistent viewport sizing."""133try:134width, height = self.dimensions135png_bytes = self._page.screenshot(136full_page=False,137clip={"x": 0, "y": 0, "width": width, "height": height}138)139return base64.b64encode(png_bytes).decode("utf-8")140except PlaywrightError as error:141print(f"Screenshot failed, trying CDP fallback: {error}")142try:143cdp_session = self._page.context.new_cdp_session(self._page)144result = cdp_session.send(145"Page.captureScreenshot", {"format": "png", "fromSurface": False}146)147return result["data"]148except PlaywrightError as cdp_error:149print(f"CDP screenshot also failed: {cdp_error}")150raise error151152def click(self, x: int, y: int, button: str = "left") -> None:153if button == "back":154self.back()155elif button == "forward":156self.forward()157elif button == "wheel":158self._page.mouse.wheel(x, y)159else:160button_type = {"left": "left", "right": "right"}.get(button, "left")161self._page.mouse.click(x, y, button=button_type)162163def double_click(self, x: int, y: int) -> None:164self._page.mouse.dblclick(x, y)165166def scroll(self, x: int, y: int, scroll_x: int, scroll_y: int) -> None:167self._page.mouse.move(x, y)168self._page.evaluate(f"window.scrollBy({scroll_x}, {scroll_y})")169170def type(self, text: str) -> None:171self._page.keyboard.type(text)172173def wait(self, ms: int = 1000) -> None:174time.sleep(ms / 1000)175176def move(self, x: int, y: int) -> None:177self._page.mouse.move(x, y)178179def keypress(self, keys: List[str]) -> None:180"""Press keys (supports modifier combinations)."""181mapped_keys = [CUA_KEY_TO_PLAYWRIGHT_KEY.get(key.lower(), key) for key in keys]182for key in mapped_keys:183self._page.keyboard.down(key)184for key in reversed(mapped_keys):185self._page.keyboard.up(key)186187def drag(self, path: List[Dict[str, int]]) -> None:188if not path:189return190start_x, start_y = path[0]["x"], path[0]["y"]191self._page.mouse.move(start_x, start_y)192self._page.mouse.down()193for point in path[1:]:194scaled_x, scaled_y = point["x"], point["y"]195self._page.mouse.move(scaled_x, scaled_y)196self._page.mouse.up()197198def goto(self, url: str) -> None:199try:200self._page.goto(url)201except Exception as e:202print(f"Error navigating to {url}: {e}")203204def back(self) -> None:205self._page.go_back()206207def forward(self) -> None:208self._page.go_forward()
Step 3: Create the Agent Class
1class Agent:2def __init__(3self,4model: str = "computer-use-preview",5computer = None,6tools: List[dict] = None,7auto_acknowledge_safety: bool = True,8):9self.model = model10self.computer = computer11self.tools = tools or []12self.auto_acknowledge_safety = auto_acknowledge_safety13self.print_steps = True14self.debug = False15self.show_images = False1617if computer:18scaled_width, scaled_height = computer.get_dimensions()19self.viewport_width = scaled_width20self.viewport_height = scaled_height2122# Create dynamic system prompt with viewport dimensions23self.system_prompt = SYSTEM_PROMPT.replace(24'<COORDINATE_SYSTEM>',25f'<COORDINATE_SYSTEM>\n* The browser viewport dimensions are {scaled_width}x{scaled_height} pixels\n* The browser viewport has specific dimensions that you must respect'26)2728self.tools.append({29"type": "computer-preview",30"display_width": scaled_width,31"display_height": scaled_height,32"environment": computer.get_environment(),33})3435# Add goto function tool for direct URL navigation36self.tools.append({37"type": "function",38"name": "goto",39"description": "Navigate directly to a specific URL.",40"parameters": {41"type": "object",42"properties": {43"url": {44"type": "string",45"description": "Fully qualified URL to navigate to (e.g., https://example.com).",46},47},48"additionalProperties": False,49"required": ["url"],50},51})5253# Add back function tool for browser navigation54self.tools.append({55"type": "function",56"name": "back",57"description": "Go back to the previous page.",58"parameters": {},59})60else:61self.viewport_width = 102462self.viewport_height = 76863self.system_prompt = SYSTEM_PROMPT6465def debug_print(self, *args):66if self.debug:67pp(*args)6869def get_viewport_info(self) -> dict:70"""Get detailed viewport information for debugging."""71if not self.computer or not self.computer._page:72return {}7374try:75return self.computer._page.evaluate("""76() => ({77innerWidth: window.innerWidth,78innerHeight: window.innerHeight,79devicePixelRatio: window.devicePixelRatio,80screenWidth: window.screen.width,81screenHeight: window.screen.height,82scrollX: window.scrollX,83scrollY: window.scrollY84})85""")86except:87return {}8889def validate_screenshot_dimensions(self, screenshot_base64: str) -> dict:90"""Validate screenshot dimensions against viewport."""91try:92image_data = base64.b64decode(screenshot_base64)93image = Image.open(BytesIO(image_data))94screenshot_width, screenshot_height = image.size9596viewport_info = self.get_viewport_info()9798scaling_info = {99"screenshot_size": (screenshot_width, screenshot_height),100"viewport_size": (self.viewport_width, self.viewport_height),101"actual_viewport": (viewport_info.get('innerWidth', 0), viewport_info.get('innerHeight', 0)),102"device_pixel_ratio": viewport_info.get('devicePixelRatio', 1.0),103"width_scale": screenshot_width / self.viewport_width if self.viewport_width > 0 else 1.0,104"height_scale": screenshot_height / self.viewport_height if self.viewport_height > 0 else 1.0105}106107# Warn about scaling mismatches108if scaling_info["width_scale"] != 1.0 or scaling_info["height_scale"] != 1.0:109print(f"โ ๏ธ Screenshot scaling detected:")110print(f" Screenshot: {screenshot_width}x{screenshot_height}")111print(f" Expected viewport: {self.viewport_width}x{self.viewport_height}")112print(f" Actual viewport: {viewport_info.get('innerWidth', 'unknown')}x{viewport_info.get('innerHeight', 'unknown')}")113print(f" Scale factors: {scaling_info['width_scale']:.3f}x{scaling_info['height_scale']:.3f}")114115return scaling_info116except Exception as e:117print(f"โ ๏ธ Error validating screenshot dimensions: {e}")118return {}119120def validate_coordinates(self, action_args: dict) -> dict:121"""Validate coordinates without clamping."""122validated_args = action_args.copy()123124# Handle single coordinates (click, move, etc.)125if 'x' in action_args and 'y' in action_args:126validated_args['x'] = int(float(action_args['x']))127validated_args['y'] = int(float(action_args['y']))128129# Handle path arrays (drag)130if 'path' in action_args and isinstance(action_args['path'], list):131validated_path = []132for point in action_args['path']:133validated_path.append({134'x': int(float(point.get('x', 0))),135'y': int(float(point.get('y', 0)))136})137validated_args['path'] = validated_path138139return validated_args140141def handle_item(self, item):142"""Handle each item from OpenAI response."""143if item["type"] == "message":144if self.print_steps:145print(item["content"][0]["text"])146147elif item["type"] == "function_call":148name, args = item["name"], json.loads(item["arguments"])149if self.print_steps:150print(f"{name}({args})")151152if hasattr(self.computer, name):153method = getattr(self.computer, name)154method(**args)155156return [{157"type": "function_call_output",158"call_id": item["call_id"],159"output": "success",160}]161162elif item["type"] == "computer_call":163action = item["action"]164action_type = action["type"]165action_args = {k: v for k, v in action.items() if k != "type"}166167# Validate coordinates and log any issues168validated_args = self.validate_coordinates(action_args)169170if self.print_steps:171print(f"{action_type}({validated_args})")172173method = getattr(self.computer, action_type)174method(**validated_args)175176screenshot_base64 = self.computer.screenshot()177178# Validate screenshot dimensions for debugging179if action_type == "screenshot" or self.debug:180self.validate_screenshot_dimensions(screenshot_base64)181182if self.show_images:183show_image(screenshot_base64)184185pending_checks = item.get("pending_safety_checks", [])186for check in pending_checks:187message = check["message"]188if self.auto_acknowledge_safety:189print(f"โ ๏ธ Auto-acknowledging safety check: {message}")190else:191raise ValueError(f"Safety check failed: {message}")192193call_output = {194"type": "computer_call_output",195"call_id": item["call_id"],196"acknowledged_safety_checks": pending_checks,197"output": {198"type": "input_image",199"image_url": f"data:image/png;base64,{screenshot_base64}",200},201}202203if self.computer.get_environment() == "browser":204current_url = self.computer.get_current_url()205check_blocklisted_url(current_url)206call_output["output"]["current_url"] = current_url207208return [call_output]209210return []211212def execute_task(213self,214task: str,215print_steps: bool = True,216debug: bool = False,217max_iterations: int = 50218) -> str:219self.print_steps = print_steps220self.debug = debug221self.show_images = False222223input_items = [224{225"role": "system",226"content": self.system_prompt,227},228{229"role": "user",230"content": task,231},232]233234new_items = []235iterations = 0236consecutive_no_actions = 0237last_assistant_messages = []238239print(f"๐ฏ Executing task: {task}")240print("=" * 60)241242def is_task_complete(content: str) -> dict:243"""Check if the task is complete based on content patterns."""244245# Explicit completion markers246if "TASK_COMPLETED:" in content:247return {"completed": True, "reason": "explicit_completion"}248if "TASK_FAILED:" in content or "TASK_ABANDONED:" in content:249return {"completed": True, "reason": "explicit_failure"}250251# Natural completion patterns252completion_patterns = [253r'task\s+(completed|finished|done|accomplished)',254r'successfully\s+(completed|finished|found|gathered)',255r'here\s+(is|are)\s+the\s+(results?|information|summary)',256r'to\s+summarize',257r'in\s+conclusion',258r'final\s+(answer|result|summary)'259]260261# Failure/abandonment patterns262failure_patterns = [263r'cannot\s+(complete|proceed|access|continue)',264r'unable\s+to\s+(complete|access|find|proceed)',265r'blocked\s+by\s+(captcha|security|authentication)',266r'giving\s+up',267r'no\s+longer\s+able',268r'have\s+tried\s+multiple\s+approaches'269]270271for pattern in completion_patterns:272if re.search(pattern, content, re.IGNORECASE):273return {"completed": True, "reason": "natural_completion"}274275for pattern in failure_patterns:276if re.search(pattern, content, re.IGNORECASE):277return {"completed": True, "reason": "natural_failure"}278279return {"completed": False}280281def detect_repetition(new_message: str) -> bool:282"""Detect if the message is too similar to recent messages."""283if len(last_assistant_messages) < 2:284return False285286def similarity(str1: str, str2: str) -> float:287words1 = str1.lower().split()288words2 = str2.lower().split()289common_words = [word for word in words1 if word in words2]290return len(common_words) / max(len(words1), len(words2))291292return any(similarity(new_message, prev_message) > 0.8293for prev_message in last_assistant_messages)294295while iterations < max_iterations:296iterations += 1297has_actions = False298299if new_items and new_items[-1].get("role") == "assistant":300last_message = new_items[-1]301if last_message.get("content") and len(last_message["content"]) > 0:302content = last_message["content"][0].get("text", "")303304# Check for explicit completion305completion = is_task_complete(content)306if completion["completed"]:307print(f"โ Task completed ({completion['reason']})")308break309310# Check for repetition311if detect_repetition(content):312print("๐ Repetition detected - stopping execution")313last_assistant_messages.append(content)314break315316# Track assistant messages for repetition detection317last_assistant_messages.append(content)318if len(last_assistant_messages) > 3:319last_assistant_messages.pop(0) # Keep only last 3320321self.debug_print([sanitize_message(msg) for msg in input_items + new_items])322323try:324response = create_response(325model=self.model,326input=input_items + new_items,327tools=self.tools,328truncation="auto",329)330self.debug_print(response)331332if "output" not in response:333if self.debug:334print(response)335raise ValueError("No output from model")336337new_items += response["output"]338339# Check if this iteration had any actions340for item in response["output"]:341if item.get("type") in ["computer_call", "function_call"]:342has_actions = True343new_items += self.handle_item(item)344345# Track consecutive iterations without actions346if not has_actions:347consecutive_no_actions += 1348if consecutive_no_actions >= 3:349print("โ ๏ธ No actions for 3 consecutive iterations - stopping")350break351else:352consecutive_no_actions = 0353354except Exception as error:355print(f"โ Error during task execution: {error}")356raise error357358if iterations >= max_iterations:359print(f"โ ๏ธ Task execution stopped after {max_iterations} iterations")360361assistant_messages = [item for item in new_items if item.get("role") == "assistant"]362if assistant_messages:363final_message = assistant_messages[-1]364if final_message.get("content") and len(final_message["content"]) > 0:365return final_message["content"][0].get("text", "Task execution completed (no final message)")366367return "Task execution completed (no final message)"
Step 4: Create the Main Script
1def main():2print("๐ Steel + OpenAI Computer Use Assistant")3print("=" * 60)45if STEEL_API_KEY == "your-steel-api-key-here":6print("โ ๏ธ WARNING: Please replace 'your-steel-api-key-here' with your actual Steel API key")7print(" Get your API key at: https://app.steel.dev/settings/api-keys")8return910if OPENAI_API_KEY == "your-openai-api-key-here":11print("โ ๏ธ WARNING: Please replace 'your-openai-api-key-here' with your actual OpenAI API key")12print(" Get your API key at: https://platform.openai.com/")13return1415task = os.getenv("TASK") or TASK1617print("\nStarting Steel browser session...")1819try:20with SteelBrowser() as computer:21print("โ Steel browser session started!")2223agent = Agent(24computer=computer,25auto_acknowledge_safety=True,26)2728start_time = time.time()2930try:31result = agent.execute_task(32task,33print_steps=True,34debug=False,35max_iterations=50,36)3738duration = f"{(time.time() - start_time):.1f}"3940print("\n" + "=" * 60)41print("๐ TASK EXECUTION COMPLETED")42print("=" * 60)43print(f"โฑ๏ธ Duration: {duration} seconds")44print(f"๐ฏ Task: {task}")45print(f"๐ Result:\n{result}")46print("=" * 60)4748except Exception as error:49print(f"โ Task execution failed: {error}")50exit(1)5152except Exception as e:53print(f"โ Failed to start Steel browser: {e}")54print("Please check your STEEL_API_KEY and internet connection.")55exit(1)565758if __name__ == "__main__":59main()
Running Your Agent
Execute your script to start an interactive AI browser session:
You will see the session URL printed in the console. You can view the live browser session by opening this URL in your web browser.
The agent will execute the task defined in the TASK
environment variable or the default task. You can modify the task by setting the environment variable:
export TASK="Search for the latest news on artificial intelligence"python main.py
Next Steps
-
Explore the Steel API documentation for more advanced features
-
Check out the OpenAI documentation for more information about the computer-use-preview model
-
Add additional features like session recording or multi-session management