Skip to content

Commit 23a6e96

Browse files
Working computer use implementation!
1 parent 78aa88f commit 23a6e96

2 files changed

Lines changed: 73 additions & 3 deletions

File tree

tests/test_computer_use.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,44 @@ async def test_execute_drag(self, session: BrowserSession):
217217
result = await session.execute(action)
218218
assert len(result) > 0
219219

220+
@pytest.mark.anyio
221+
async def test_landing_page_has_url_input(self, session: BrowserSession):
222+
page = await session._ensure_page()
223+
url_input = await page.query_selector("#url")
224+
assert url_input is not None
225+
226+
@pytest.mark.anyio
227+
async def test_landing_page_has_go_button(self, session: BrowserSession):
228+
page = await session._ensure_page()
229+
button = await page.query_selector("button[type=submit]")
230+
assert button is not None
231+
text = await button.text_content()
232+
assert text is not None
233+
assert "Go" in text
234+
235+
@pytest.mark.anyio
236+
async def test_landing_page_navigation(self, session: BrowserSession):
237+
page = await session._ensure_page()
238+
# Intercept the navigation request and serve a fake page
239+
await page.route("https://example.com/", lambda route: route.fulfill(
240+
status=200,
241+
content_type="text/html",
242+
body="<h1>Hello</h1>",
243+
))
244+
await page.fill("#url", "https://example.com/")
245+
await page.click("button[type=submit]")
246+
await page.wait_for_load_state("load")
247+
content = await page.content()
248+
assert "Hello" in content
249+
250+
@pytest.mark.anyio
251+
async def test_landing_page_not_blank(self, session: BrowserSession):
252+
result = await session.screenshot()
253+
img = Image.open(io.BytesIO(base64.b64decode(result)))
254+
colors = img.getcolors(maxcolors=10)
255+
# A blank white page would have exactly 1 color; landing page has more
256+
assert colors is None or len(colors) > 1
257+
220258
@pytest.mark.anyio
221259
async def test_close_is_idempotent(self, session: BrowserSession):
222260
await session.close()

utils/computer_use.py

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,32 @@
1919

2020
logger = logging.getLogger("uvicorn.error")
2121

22+
_LANDING_PAGE_HTML = """\
23+
<!DOCTYPE html>
24+
<html>
25+
<head><title>Browser</title></head>
26+
<body style="margin:0;display:flex;justify-content:center;align-items:center;
27+
height:100vh;background:#f0f0f0;font-family:sans-serif;">
28+
<div style="text-align:center;">
29+
<h1 style="font-size:28px;margin-bottom:24px;">Browser &mdash; Enter a URL to get started</h1>
30+
<form onsubmit="window.location=document.getElementById('url').value;return false;"
31+
style="display:flex;gap:8px;justify-content:center;">
32+
<input id="url" type="text"
33+
placeholder="Enter a URL, e.g. https://example.com"
34+
style="width:500px;padding:12px 16px;font-size:18px;border:2px solid #ccc;
35+
border-radius:6px;"
36+
autofocus />
37+
<button type="submit"
38+
style="padding:12px 24px;font-size:18px;background:#2563eb;color:white;
39+
border:none;border-radius:6px;cursor:pointer;">
40+
Go
41+
</button>
42+
</form>
43+
</div>
44+
</body>
45+
</html>
46+
"""
47+
2248
Action = Union[
2349
Click, DoubleClick, Drag, Keypress,
2450
Move, Screenshot, Scroll, Type, Wait,
@@ -100,7 +126,7 @@ async def _ensure_page(self) -> Page:
100126
device_scale_factor=1,
101127
)
102128
self._page = await context.new_page()
103-
await self._page.goto("about:blank")
129+
await self._page.set_content(_LANDING_PAGE_HTML, wait_until="load")
104130
return self._page
105131

106132
async def screenshot(self) -> str:
@@ -157,11 +183,17 @@ async def execute(self, action: Action) -> str:
157183
async def close(self) -> None:
158184
"""Close the browser and clean up resources."""
159185
if self._browser:
160-
await self._browser.close()
186+
try:
187+
await self._browser.close()
188+
except Exception:
189+
logger.debug("Browser already disconnected during close")
161190
self._browser = None
162191
self._page = None
163192
if self._playwright:
164-
await self._playwright.stop()
193+
try:
194+
await self._playwright.stop()
195+
except Exception:
196+
logger.debug("Playwright already stopped during close")
165197
self._playwright = None
166198

167199

0 commit comments

Comments
 (0)