NEW in v0.90.17: Complete async API implementation with async versions of all SDK functions including core utilities, supporting utilities, agent layer, and developer tools. All async functions are now organized in their respective modules, with async_api serving as a convenient re-export point.
The async API enables you to build high-performance automation with Python's asyncio framework:
The AsyncSentienceBrowser class provides async context manager support and all the features of the sync SentienceBrowser.
from sentience.async_api import AsyncSentienceBrowser
# Async context manager (recommended)
async def main():
async with AsyncSentienceBrowser() as browser:
await browser.goto("https://example.com")
# Browser automatically closes when done
# Manual lifecycle
async def manual():
browser = AsyncSentienceBrowser()
await browser.start()
await browser.goto("https://example.com")
await browser.close()
from sentience.async_api import AsyncSentienceBrowser
async def main():
async with AsyncSentienceBrowser(api_key="sk_...") as browser:
await browser.goto("https://example.com")
# Use Pro/Enterprise features
from sentience.async_api import AsyncSentienceBrowser
async def main():
# Custom viewport size
async with AsyncSentienceBrowser(
viewport={"width": 1920, "height": 1080}
) as browser:
await browser.goto("https://example.com")
from sentience.async_api import AsyncSentienceBrowser
from playwright.async_api import async_playwright
async def main():
async with async_playwright() as p:
# Create Playwright context
context = await p.chromium.launch_persistent_context(
"./user_data",
headless=False
)
# Convert to AsyncSentienceBrowser
browser = AsyncSentienceBrowser.from_existing(context)
# Use all Sentience features
await browser.page.goto("https://example.com")
from sentience.async_api import AsyncSentienceBrowser
from playwright.async_api import async_playwright
async def main():
async with async_playwright() as p:
browser = await p.chromium.launch()
context = await browser.new_context()
page = await context.new_page()
# Navigate first
await page.goto("https://example.com")
# Convert to AsyncSentienceBrowser
sentience_browser = AsyncSentienceBrowser.from_page(page)
All core SDK functions have async versions with _async suffix for clarity.
Capture page snapshot asynchronously:
from sentience.async_api import AsyncSentienceBrowser, snapshot_async
async def main():
async with AsyncSentienceBrowser() as browser:
await browser.goto("https://example.com")
# Capture snapshot
snap = await snapshot_async(browser)
print(f"Found {len(snap.elements)} elements")
print(f"Page title: {snap.title}")
Parameters:
browser (AsyncSentienceBrowser): Browser instancescreenshot (bool, optional): Include screenshot. Defaults to Truelimit (int, optional): Max elements to returngoal (str, optional): ML reranking goalClick an element asynchronously:
from sentience.async_api import AsyncSentienceBrowser, snapshot_async, click_async, find
async def main():
async with AsyncSentienceBrowser() as browser:
await browser.goto("https://example.com")
# Find and click
snap = await snapshot_async(browser)
button = find(snap, "role=button text~'Submit'")
if button:
await click_async(browser, button.id)
Parameters:
browser (AsyncSentienceBrowser): Browser instanceelement_id (str): Element ID from snapshotType text into an input field asynchronously:
from sentience.async_api import AsyncSentienceBrowser, snapshot_async, type_text_async, find
async def main():
async with AsyncSentienceBrowser() as browser:
await browser.goto("https://example.com")
snap = await snapshot_async(browser)
email_input = find(snap, "role=textbox text~'email'")
if email_input:
await type_text_async(browser, email_input.id, "user@example.com")
Parameters:
browser (AsyncSentienceBrowser): Browser instanceelement_id (str): Element ID from snapshottext (str): Text to typePress keyboard keys asynchronously:
from sentience.async_api import AsyncSentienceBrowser, press_async
async def main():
async with AsyncSentienceBrowser() as browser:
await browser.goto("https://example.com")
# Press Enter
await press_async(browser, "Enter")
# Press Escape
await press_async(browser, "Escape")
# Keyboard shortcut
await press_async(browser, "Control+A")
Parameters:
browser (AsyncSentienceBrowser): Browser instancekey (str): Key name (e.g., "Enter", "Escape", "Control+A")Click at specific coordinates asynchronously:
from sentience.async_api import AsyncSentienceBrowser, click_rect_async
async def main():
async with AsyncSentienceBrowser() as browser:
await browser.goto("https://example.com")
# Click at coordinates
await click_rect_async(
browser,
x=100,
y=200,
width=50,
height=30
)
Parameters:
browser (AsyncSentienceBrowser): Browser instancex (int): X coordinatey (int): Y coordinatewidth (int): Click area widthheight (int): Click area heightNEW in v0.90.17: Async versions of core utility functions for semantic waiting, screenshots, and text search.
Wait for an element to appear using semantic queries:
from sentience.async_api import AsyncSentienceBrowser, wait_for_async
async def main():
async with AsyncSentienceBrowser() as browser:
await browser.goto("https://example.com")
# Wait for element with timeout
result = await wait_for_async(browser, "role=button", timeout=5.0)
if result:
print(f"Element found: {result.id}")
Parameters:
browser (AsyncSentienceBrowser): Browser instancequery (str): Semantic query stringtimeout (float, optional): Maximum wait time in seconds. Defaults to 10.0Capture screenshot asynchronously in PNG or JPEG format:
from sentience.async_api import AsyncSentienceBrowser, screenshot_async
async def main():
async with AsyncSentienceBrowser() as browser:
await browser.goto("https://example.com")
# Capture screenshot as JPEG
data_url = await screenshot_async(browser, format="jpeg", quality=80)
# Save to file
import base64
image_data = base64.b64decode(data_url.split(',')[1])
with open("screenshot.jpg", "wb") as f:
f.write(image_data)
Parameters:
browser (AsyncSentienceBrowser): Browser instanceformat (str, optional): Image format - "png" or "jpeg". Defaults to "png"quality (int, optional): JPEG quality (1-100). Only used when format is "jpeg". Defaults to 90Find text on the page and return pixel coordinates:
from sentience.async_api import AsyncSentienceBrowser, find_text_rect_async
async def main():
async with AsyncSentienceBrowser() as browser:
await browser.goto("https://example.com")
# Find text and get coordinates
text_result = await find_text_rect_async(browser, "Sign In")
if text_result:
print(f"Text found at: x={text_result.x}, y={text_result.y}")
print(f"Size: {text_result.width}x{text_result.height}")
Parameters:
browser (AsyncSentienceBrowser): Browser instancetext (str): Text to search forReturns: Object with x, y, width, height properties, or None if not found
NEW in v0.90.17: Async versions of supporting functions for content reading, visual overlays, and assertions.
Read page content in various formats:
from sentience.async_api import AsyncSentienceBrowser, read_async
async def main():
async with AsyncSentienceBrowser() as browser:
await browser.goto("https://example.com")
# Read as markdown
markdown = await read_async(browser, output_format="markdown")
print(markdown)
# Read as plain text
text = await read_async(browser, output_format="text")
# Read raw HTML
html = await read_async(browser, output_format="html")
Parameters:
browser (AsyncSentienceBrowser): Browser instanceoutput_format (str, optional): Output format - "html", "text", or "markdown". Defaults to "text"Manage visual overlays for debugging:
from sentience.async_api import AsyncSentienceBrowser, snapshot_async, show_overlay_async, clear_overlay_async
async def main():
async with AsyncSentienceBrowser() as browser:
await browser.goto("https://example.com")
# Take snapshot
snap = await snapshot_async(browser)
# Show overlay on specific element
await show_overlay_async(browser, snap, target_element_id=42)
# Clear overlay
await clear_overlay_async(browser)
Parameters:
browser (AsyncSentienceBrowser): Browser instancesnapshot (Snapshot): Snapshot object from snapshot_async()target_element_id (int): Element ID to highlightAsync assertion helpers with fluent API:
from sentience.async_api import AsyncSentienceBrowser, expect_async
async def main():
async with AsyncSentienceBrowser() as browser:
await browser.goto("https://example.com")
# Assert element is visible
element = await expect_async(browser, "role=button").to_be_visible()
# Assert element exists
await expect_async(browser, "role=link").to_exist()
# Assert element contains text
await expect_async(browser, "role=heading").to_have_text("Welcome")
# Assert query returns N elements
await expect_async(browser, "role=link").to_have_count(5)
Available Methods:
.to_be_visible() - Assert element is visible.to_exist() - Assert element exists.to_have_text(text) - Assert element contains text.to_have_count(count) - Assert query returns N elementsNEW in v0.90.17: Full async implementation of the agent layer for natural language automation.
Async agent with observe-think-act loop:
from sentience.async_api import AsyncSentienceBrowser, SentienceAgentAsync
from sentience.llm_provider import OpenAIProvider
async def main():
async with AsyncSentienceBrowser() as browser:
await browser.goto("https://example.com")
# Initialize LLM provider
llm = OpenAIProvider(api_key="your_key", model="gpt-4o")
# Create async agent
agent = SentienceAgentAsync(browser, llm)
# Natural language automation
result = await agent.act("Click the login button")
result = await agent.act("Type 'user@example.com' into the email field")
# Get token usage statistics
stats = agent.get_token_stats()
print(f"Tokens used: {stats['total_tokens']}")
Features:
Parameters:
browser (AsyncSentienceBrowser): Browser instancellm_provider: LLM provider instance (OpenAIProvider, etc.)NEW in v0.90.17: Async versions of developer tools for recording and inspection.
Record actions and generate traces:
from sentience.async_api import AsyncSentienceBrowser, RecorderAsync
async def main():
async with AsyncSentienceBrowser() as browser:
await browser.goto("https://example.com")
# Record actions
async with RecorderAsync(browser, capture_snapshots=True) as recorder:
await recorder.record_click(element_id)
await recorder.record_type(element_id, "text")
# Save trace
recorder.save("trace.json")
Parameters:
browser (AsyncSentienceBrowser): Browser instancecapture_snapshots (bool, optional): Whether to capture snapshots. Defaults to TrueInspect elements and debug interactively:
from sentience.async_api import AsyncSentienceBrowser, InspectorAsync
async def main():
async with AsyncSentienceBrowser() as browser:
await browser.goto("https://example.com")
# Interactive inspection
async with InspectorAsync(browser) as inspector:
# Hover elements to see info in console
# Click elements to see full details
pass
Parameters:
browser (AsyncSentienceBrowser): Browser instanceThese functions are pure (no I/O) and don't need async versions:
from sentience.async_api import find, query
# find() - Returns single element
button = find(snap, "role=button text~'Submit'")
# query() - Returns list of elements
links = query(snap, "role=link")
Here's a full example combining all async functions:
import asyncio
from sentience.async_api import (
AsyncSentienceBrowser,
snapshot_async,
find,
click_async,
type_text_async,
press_async
)
async def login_example():
"""Complete login automation example"""
async with AsyncSentienceBrowser() as browser:
# Navigate to login page
await browser.goto("https://example.com/login")
# Take snapshot
snap = await snapshot_async(browser)
# Find email input
email_input = find(snap, "role=textbox text~'email'")
if email_input:
await type_text_async(browser, email_input.id, "user@example.com")
# Find password input
snap = await snapshot_async(browser)
password_input = find(snap, "role=textbox text~'password'")
if password_input:
await type_text_async(browser, password_input.id, "mypassword")
# Click submit button
snap = await snapshot_async(browser)
submit_btn = find(snap, "role=button text~'log in'")
if submit_btn:
await click_async(browser, submit_btn.id)
# Wait for page load
await asyncio.sleep(2)
# Verify login success
snap = await snapshot_async(browser)
print(f"Page title after login: {snap.title}")
# Run the async function
if __name__ == "__main__":
asyncio.run(login_example())
NEW in v0.90.17: Here's a comprehensive example using all the new async features:
from sentience.async_api import (
AsyncSentienceBrowser,
wait_for_async,
screenshot_async,
find_text_rect_async,
read_async,
show_overlay_async,
expect_async,
SentienceAgentAsync
)
from sentience.llm_provider import OpenAIProvider
async def comprehensive_example():
"""Example using all Phase 2A-2D features"""
async with AsyncSentienceBrowser() as browser:
await browser.goto("https://example.com")
# Phase 2A: Core Utilities
# Wait for element
result = await wait_for_async(browser, "role=button", timeout=5.0)
# Capture screenshot
data_url = await screenshot_async(browser, format="jpeg", quality=80)
# Find text on page
text_result = await find_text_rect_async(browser, "Sign In")
# Phase 2B: Supporting Utilities
# Read page content
markdown = await read_async(browser, output_format="markdown")
# Show visual overlay
from sentience.async_api import snapshot_async
snap = await snapshot_async(browser)
await show_overlay_async(browser, snap, target_element_id=42)
# Assertions
element = await expect_async(browser, "role=button").to_be_visible()
await expect_async(browser, "role=link").to_have_count(5)
# Phase 2C: Agent Layer
llm = OpenAIProvider(api_key="your_key", model="gpt-4o")
agent = SentienceAgentAsync(browser, llm)
# Natural language automation
result = await agent.act("Click the login button")
result = await agent.act("Type 'user@example.com' into the email field")
# Token tracking
stats = agent.get_token_stats()
print(f"Tokens used: {stats['total_tokens']}")
if __name__ == "__main__":
import asyncio
asyncio.run(comprehensive_example())
Run multiple browser tasks in parallel:
import asyncio
from sentience.async_api import AsyncSentienceBrowser, snapshot_async
async def scrape_page(url: str):
"""Scrape a single page"""
async with AsyncSentienceBrowser() as browser:
await browser.goto(url)
snap = await snapshot_async(browser)
return {
"url": url,
"title": snap.title,
"element_count": len(snap.elements)
}
async def scrape_multiple_pages():
"""Scrape multiple pages concurrently"""
urls = [
"https://example.com",
"https://example.org",
"https://example.net"
]
# Run all scrapes concurrently
tasks = [scrape_page(url) for url in urls]
results = await asyncio.gather(*tasks)
for result in results:
print(f"{result['url']}: {result['title']} ({result['element_count']} elements)")
# Run
if __name__ == "__main__":
asyncio.run(scrape_multiple_pages())
from fastapi import FastAPI
from sentience.async_api import AsyncSentienceBrowser, snapshot_async, find
app = FastAPI()
@app.get("/scrape")
async def scrape_endpoint(url: str):
"""API endpoint that scrapes a URL"""
async with AsyncSentienceBrowser() as browser:
await browser.goto(url)
snap = await snapshot_async(browser)
return {
"url": url,
"title": snap.title,
"element_count": len(snap.elements)
}
import aiohttp
from aiohttp import web
from sentience.async_api import AsyncSentienceBrowser, snapshot_async
async def handle_scrape(request):
"""Handle scrape request"""
url = request.query.get('url')
async with AsyncSentienceBrowser() as browser:
await browser.goto(url)
snap = await snapshot_async(browser)
return web.json_response({
"url": url,
"title": snap.title,
"element_count": len(snap.elements)
})
app = web.Application()
app.router.add_get('/scrape', handle_scrape)
if __name__ == "__main__":
web.run_app(app)
v0.90.17 Refactoring:
AsyncSentienceBrowser → browser.pysnapshot_async() → snapshot.pyclick_async(), type_text_async(), press_async(), click_rect_async() → actions.pywait_for_async(), screenshot_async(), find_text_rect_async() → wait.py, screenshot.py, find_text_rect.pyread_async(), show_overlay_async(), expect_async() → read.py, overlay.py, expect.pySentienceAgentAsync → agent.pyRecorderAsync, InspectorAsync → recorder.py, inspector.pyasync_api.py serves as a convenient re-export module - all async APIs available from a single import pointComplete Coverage:
sentience.async_apiPerformance:
Code Quality:
Compatibility:
Testing:
sdk-python/examples/Migrating from sync to async is straightforward:
# Sync API (before)
from sentience import SentienceBrowser, snapshot, find, click
with SentienceBrowser() as browser:
browser.goto("https://example.com")
snap = snapshot(browser)
button = find(snap, "role=button")
if button:
click(browser, button.id)
# Async API (after)
from sentience.async_api import AsyncSentienceBrowser, snapshot_async, find, click_async
async def main():
async with AsyncSentienceBrowser() as browser:
await browser.goto("https://example.com")
snap = await snapshot_async(browser)
button = find(snap, "role=button")
if button:
await click_async(browser, button.id)
Key changes:
sentience.async_api instead of sentienceAsyncSentienceBrowser instead of SentienceBrowserawait before I/O operations (goto, snapshot_async, click_async, etc.)async keyword to function definition