Skip to content

Commit 0463173

Browse files
add notte computer
1 parent a842bec commit 0463173

File tree

6 files changed

+143
-2
lines changed

6 files changed

+143
-2
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ This sample app provides a set of implemented `Computer` examples, but feel free
9494
| `LocalPlaywright` | local-playwright | `browser` | Local browser window | [Playwright SDK](https://playwright.dev/) |
9595
| `Docker` | docker | `linux` | Docker container environment | [Docker](https://docs.docker.com/engine/install/) running |
9696
| `Browserbase` | browserbase | `browser` | Remote browser environment | [Browserbase](https://www.browserbase.com/) API key in `.env` |
97+
| `Notte Browser` | notte-browser | `browser` | Remote browser environment | [Notte](https://www.notte.cc/) API key in `.env` |
9798
| `ScrapybaraBrowser` | scrapybara-browser | `browser` | Remote browser environment | [Scrapybara](https://scrapybara.com/dashboard) API key in `.env` |
9899
| `ScrapybaraUbuntu` | scrapybara-ubuntu | `linux` | Remote Ubuntu desktop environment | [Scrapybara](https://scrapybara.com/dashboard) API key in `.env` |
99100

computers/config.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
"local-playwright": LocalPlaywrightBrowser,
66
"docker": DockerComputer,
77
"browserbase": BrowserbaseBrowser,
8+
"notte-browser": NotteBrowser,
89
"scrapybara-browser": ScrapybaraBrowser,
910
"scrapybara-ubuntu": ScrapybaraUbuntu,
1011
}

computers/contrib/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
from .notte import NotteBrowser

computers/contrib/notte.py

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
import os
2+
from typing import final
3+
from typing_extensions import override
4+
from playwright.sync_api import Browser, Page, Error as PlaywrightError
5+
6+
from dotenv import load_dotenv
7+
from computers.shared.base_playwright import BasePlaywrightComputer
8+
from notte_sdk import NotteClient
9+
10+
_ = load_dotenv()
11+
12+
@final
13+
class NotteBrowser(BasePlaywrightComputer):
14+
"""
15+
Notte is a headless browser platform that offers a remote browser API. You can use it to control thousands of browsers from anywhere.
16+
You can find more information about Notte at https://www.notte.cc/computer-use or view our OpenAI CUA Quickstart at https://docs.notte.cc/integrations/openai-cua/introduction.
17+
18+
IMPORTANT: This Notte computer requires the use of the `goto` tool defined in playwright_with_custom_functions.py.
19+
Make sure to include this tool in your configuration when using the Notte computer.
20+
"""
21+
22+
def __init__(
23+
self,
24+
width: int = 1024,
25+
height: int = 768,
26+
proxy: bool = False,
27+
) -> None:
28+
"""
29+
Initialize the Browserbase instance. Additional configuration options for features such as persistent cookies, ad blockers, file downloads and more can be found in the Browserbase API documentation: https://docs.browserbase.com/reference/api/create-a-session
30+
31+
Args:
32+
width (int): The width of the browser viewport. Default is 1024.
33+
height (int): The height of the browser viewport. Default is 768.
34+
region (str): The region for the Browserbase session. Default is "us-west-2". Pick a region close to you for better performance. https://docs.browserbase.com/guides/multi-region
35+
proxy (bool): Whether to use a proxy for the session. Default is False. Turn on proxies if you're browsing is frequently interrupted. https://docs.browserbase.com/features/proxies
36+
"""
37+
super().__init__()
38+
self.notte = NotteClient(api_key=os.getenv("NOTTE_API_KEY"))
39+
self.session = self.notte.Session(
40+
viewport_width=width,
41+
viewport_height=height,
42+
proxies=proxy,
43+
)
44+
self.width = width
45+
self.height = height
46+
47+
@override
48+
def get_dimensions(self) -> tuple[int, int]:
49+
return (self.width, self.height)
50+
51+
@override
52+
def _get_browser_and_page(self) -> tuple[Browser, Page]:
53+
"""
54+
Create a Browserbase session and connect to it.
55+
56+
Returns:
57+
Tuple[Browser, Page]: A tuple containing the connected browser and page objects.
58+
"""
59+
# Create a session on Browserbase with specified parameters
60+
self.session.start()
61+
# Connect to the remote session
62+
cdp_url = self.session.cdp_url()
63+
browser = self._playwright.chromium.connect_over_cdp(
64+
endpoint_url=cdp_url,
65+
timeout=60000
66+
)
67+
context = browser.contexts[0]
68+
69+
# Add event listeners for page creation and closure
70+
context.on("page", self._handle_new_page)
71+
72+
73+
page = context.pages[0]
74+
# page.on("close", self._handle_page_close)
75+
76+
page.goto("https://bing.com")
77+
78+
return browser, page
79+
80+
def _handle_new_page(self, page: Page):
81+
"""Handle the creation of a new page."""
82+
print("New page created")
83+
self._page = page
84+
# page.on("close", self._handle_page_close)
85+
86+
def _handle_page_close(self, page: Page):
87+
"""Handle the closure of a page."""
88+
print("Page closed")
89+
if self._page == page:
90+
if self._browser.contexts[0].pages:
91+
self._page = self._browser.contexts[0].pages[-1]
92+
else:
93+
print("Warning: All pages have been closed.")
94+
self._page = None
95+
96+
@override
97+
def __exit__(self, exc_type, exc_val, exc_tb):
98+
"""
99+
Clean up resources when exiting the context manager.
100+
101+
Args:
102+
exc_type: The type of the exception that caused the context to be exited.
103+
exc_val: The exception instance that caused the context to be exited.
104+
exc_tb: A traceback object encapsulating the call stack at the point where the exception occurred.
105+
"""
106+
super().__exit__(exc_type, exc_val, exc_tb)
107+
108+
if self.session:
109+
print(
110+
f"Session completed. Check our docs to learn more about session replays: https://docs.notte.cc"
111+
)
112+
self.session.stop()
113+
114+
@override
115+
def screenshot(self) -> str:
116+
"""
117+
Capture a screenshot of the current viewport using CDP.
118+
119+
Returns:
120+
str: A base64 encoded string of the screenshot.
121+
"""
122+
if self._page is None:
123+
raise ValueError("No page to screenshot")
124+
try:
125+
# Get CDP session from the page
126+
cdp_session = self._page.context.new_cdp_session(self._page)
127+
128+
# Capture screenshot using CDP
129+
result = cdp_session.send("Page.captureScreenshot", {
130+
"format": "png",
131+
"fromSurface": True
132+
})
133+
134+
return result['data']
135+
except PlaywrightError as error:
136+
print(f"CDP screenshot failed, falling back to standard screenshot: {error}")
137+
return super().screenshot()

computers/shared/base_playwright.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,10 +45,10 @@ class BasePlaywrightComputer:
4545
- We also have extra browser actions: `goto(url)` and `back()`.
4646
"""
4747

48-
def get_environment(self):
48+
def get_environment(self) -> str:
4949
return "browser"
5050

51-
def get_dimensions(self):
51+
def get_dimensions(self) -> tuple[int, int]:
5252
return (1024, 768)
5353

5454
def __init__(self):

requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,4 @@ scrapybara>=2.3.6
2121
sniffio==1.3.1
2222
typing_extensions==4.12.2
2323
urllib3==2.3.0
24+
notte-sdk==1.4.4

0 commit comments

Comments
 (0)