Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
94 changes: 94 additions & 0 deletions examples/basic_agent_async.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
"""
Example: Basic agent usage (Async version)
Demonstrates SentienceAgentAsync for natural language automation
"""

import asyncio
import os

from sentience.async_api import AsyncSentienceBrowser, SentienceAgentAsync
from sentience.llm_provider import LLMProvider, LLMResponse


# Simple mock LLM provider for demonstration
# In production, use OpenAIProvider, AnthropicProvider, etc.
class MockLLMProvider(LLMProvider):
"""Mock LLM provider for testing"""

def generate(self, system_prompt: str, user_prompt: str, **kwargs) -> LLMResponse:
# Simple mock that returns CLICK action
return LLMResponse(
content="CLICK(1)",
model_name="mock-model",
prompt_tokens=100,
completion_tokens=10,
total_tokens=110,
)

def supports_json_mode(self) -> bool:
return True

@property
def model_name(self) -> str:
return "mock-model"


async def main():
# Get API key from environment variable (optional - uses free tier if not set)
api_key = os.environ.get("SENTIENCE_API_KEY")

async with AsyncSentienceBrowser(api_key=api_key, headless=False) as browser:
# Navigate to a page
await browser.goto("https://example.com", wait_until="domcontentloaded")

# Create LLM provider
# In production, use: llm = OpenAIProvider(api_key="your-key", model="gpt-4o")
llm = MockLLMProvider()

# Create agent
agent = SentienceAgentAsync(browser, llm, verbose=True)

print("=== Basic Agent Demo ===\n")

# Example 1: Simple action
print("1. Executing simple action...")
try:
result = await agent.act("Click the first link")
print(f" Result: success={result.success}, action={result.action}")
if result.element_id:
print(f" Clicked element ID: {result.element_id}")
except Exception as e:
print(f" Error: {e}")

print()

# Example 2: Check history
print("2. Agent execution history:")
history = agent.get_history()
print(f" Total actions: {len(history)}")
for i, entry in enumerate(history, 1):
print(f" {i}. {entry.goal} -> {entry.action} (success: {entry.success})")

print()

# Example 3: Token statistics
print("3. Token usage statistics:")
stats = agent.get_token_stats()
print(f" Total tokens: {stats.total_tokens}")
print(f" Prompt tokens: {stats.total_prompt_tokens}")
print(f" Completion tokens: {stats.total_completion_tokens}")

print()

# Example 4: Clear history
print("4. Clearing history...")
agent.clear_history()
print(f" History length after clear: {len(agent.get_history())}")

print("\n✅ Basic agent demo complete!")
print("\nNote: This example uses a mock LLM provider.")
print("In production, use a real LLM provider like OpenAIProvider or AnthropicProvider.")


if __name__ == "__main__":
asyncio.run(main())
65 changes: 65 additions & 0 deletions examples/hello_async.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
"""
Example: Verify extension bridge is loaded (Async version)
"""

import asyncio
import os

from sentience.async_api import AsyncSentienceBrowser


async def main():
# Get API key from environment variable (optional - uses free tier if not set)
api_key = os.environ.get("SENTIENCE_API_KEY")

try:
async with AsyncSentienceBrowser(api_key=api_key, headless=False) as browser:
# Navigate to a page to ensure extension is active
await browser.goto("https://example.com", wait_until="domcontentloaded")

# Check if extension API is available
bridge_ok = await browser.page.evaluate(
"""
() => {
return typeof window.sentience !== 'undefined' &&
typeof window.sentience.snapshot === 'function';
}
"""
)
print(f"bridge_ok={bridge_ok}")

if bridge_ok:
print("✅ Extension loaded successfully!")
# Try a quick snapshot to verify it works
try:
result = await browser.page.evaluate("window.sentience.snapshot({ limit: 1 })")
if result.get("status") == "success":
print(f"✅ Snapshot test: Found {len(result.get('elements', []))} elements")
else:
print(f"⚠️ Snapshot returned: {result.get('status')}")
except Exception as e:
print(f"⚠️ Snapshot test failed: {e}")
else:
print("❌ Extension not loaded")
# Debug info
debug_info = await browser.page.evaluate(
"""
() => {
return {
sentience_defined: typeof window.sentience !== 'undefined',
registry_defined: typeof window.sentience_registry !== 'undefined',
snapshot_defined: typeof window.sentience?.snapshot !== 'undefined'
};
}
"""
)
print(f"Debug info: {debug_info}")
except Exception as e:
print(f"❌ Error: {e}")
import traceback

traceback.print_exc()


if __name__ == "__main__":
asyncio.run(main())
49 changes: 49 additions & 0 deletions examples/query_demo_async.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
"""
Example: Query engine demonstration (Async version)
"""

import asyncio
import os

from sentience.async_api import AsyncSentienceBrowser, find, query, snapshot_async


async def main():
# Get API key from environment variable (optional - uses free tier if not set)
api_key = os.environ.get("SENTIENCE_API_KEY")

async with AsyncSentienceBrowser(api_key=api_key, headless=False) as browser:
# Navigate to a page with links
await browser.goto("https://example.com", wait_until="domcontentloaded")

snap = await snapshot_async(browser)

# Query examples
print("=== Query Examples ===\n")

# Find all buttons
buttons = query(snap, "role=button")
print(f"Found {len(buttons)} buttons")

# Find all links
links = query(snap, "role=link")
print(f"Found {len(links)} links")

# Find clickable elements
clickables = query(snap, "clickable=true")
print(f"Found {len(clickables)} clickable elements")

# Find element with text containing "More"
more_link = find(snap, "text~'More'")
if more_link:
print(f"\nFound 'More' link: {more_link.text} (id: {more_link.id})")
else:
print("\nNo 'More' link found")

# Complex query: clickable links
clickable_links = query(snap, "role=link clickable=true")
print(f"\nFound {len(clickable_links)} clickable links")


if __name__ == "__main__":
asyncio.run(main())
58 changes: 58 additions & 0 deletions examples/read_markdown_async.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
"""
Example: Reading page content and converting to markdown (Async version)

This example shows how to use the read_async() function to get page content
and convert it to high-quality markdown using markdownify.
"""

import asyncio
import os

from markdownify import markdownify

from sentience.async_api import AsyncSentienceBrowser, read_async


async def main():
# Get API key from environment variable (optional - uses free tier if not set)
api_key = os.environ.get("SENTIENCE_API_KEY")

# Initialize browser
async with AsyncSentienceBrowser(api_key=api_key, headless=True) as browser:
# Navigate to a page
await browser.goto("https://example.com", wait_until="domcontentloaded")

# Method 1: Get raw HTML (default) and convert with markdownify
print("=== Method 1: Raw HTML + markdownify (Recommended) ===")
result = await read_async(browser) # output_format="raw" is default
html_content = result["content"]

# Convert to markdown using markdownify (better quality)
markdown = markdownify(
html_content,
heading_style="ATX", # Use # for headings
bullets="-", # Use - for lists
strip=["script", "style", "nav", "footer", "header"], # Strip unwanted tags
)
print(f"Markdown length: {len(markdown)} characters")
print(markdown[:500]) # Print first 500 chars
print("\n")

# Method 2: Get high-quality markdown directly (uses markdownify internally)
print("=== Method 2: Direct markdown (High-quality via markdownify) ===")
result = await read_async(browser, output_format="markdown")
high_quality_markdown = result["content"]
print(f"Markdown length: {len(high_quality_markdown)} characters")
print(high_quality_markdown[:500]) # Print first 500 chars
print("\n")

# Method 3: Get plain text
print("=== Method 3: Plain text ===")
result = await read_async(browser, output_format="text")
text_content = result["content"]
print(f"Text length: {len(text_content)} characters")
print(text_content[:500]) # Print first 500 chars


if __name__ == "__main__":
asyncio.run(main())
119 changes: 119 additions & 0 deletions examples/semantic_wait_demo_async.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
"""
Example: Semantic wait_for using query DSL (Async version)
Demonstrates waiting for elements using semantic selectors
"""

import asyncio
import os

from sentience.async_api import AsyncSentienceBrowser, click_async, wait_for_async


async def main():
# Get API key from environment variable (optional - uses free tier if not set)
api_key = os.environ.get("SENTIENCE_API_KEY")

async with AsyncSentienceBrowser(api_key=api_key, headless=False) as browser:
# Navigate to example.com
await browser.goto("https://example.com", wait_until="domcontentloaded")

print("=== Semantic wait_for_async Demo ===\n")

# Example 1: Wait for element by role
print("1. Waiting for link element (role=link)")
wait_result = await wait_for_async(browser, "role=link", timeout=5.0)
if wait_result.found:
print(f" ✅ Found after {wait_result.duration_ms}ms")
print(f" Element: '{wait_result.element.text}' (id: {wait_result.element.id})")
else:
print(f" ❌ Not found (timeout: {wait_result.timeout})")
print()

# Example 2: Wait for element by role and text
print("2. Waiting for link with specific text")
wait_result = await wait_for_async(browser, "role=link text~'Example'", timeout=5.0)
if wait_result.found:
print(f" ✅ Found after {wait_result.duration_ms}ms")
print(f" Element text: '{wait_result.element.text}'")
else:
print(" ❌ Not found")
print()

# Example 3: Wait for clickable element
print("3. Waiting for clickable element")
wait_result = await wait_for_async(browser, "clickable=true", timeout=5.0)
if wait_result.found:
print(f" ✅ Found clickable element after {wait_result.duration_ms}ms")
print(f" Role: {wait_result.element.role}")
print(f" Text: '{wait_result.element.text}'")
print(f" Is clickable: {wait_result.element.visual_cues.is_clickable}")
else:
print(" ❌ Not found")
print()

# Example 4: Wait for element with importance threshold
print("4. Waiting for important element (importance > 100)")
wait_result = await wait_for_async(browser, "importance>100", timeout=5.0)
if wait_result.found:
print(f" ✅ Found important element after {wait_result.duration_ms}ms")
print(f" Importance: {wait_result.element.importance}")
print(f" Role: {wait_result.element.role}")
else:
print(" ❌ Not found")
print()

# Example 5: Wait and then click
print("5. Wait for element, then click it")
wait_result = await wait_for_async(browser, "role=link", timeout=5.0)
if wait_result.found:
print(" ✅ Found element, clicking...")
click_result = await click_async(browser, wait_result.element.id)
print(
f" Click result: success={click_result.success}, outcome={click_result.outcome}"
)
if click_result.url_changed:
print(f" ✅ Navigation occurred: {browser.page.url}")
else:
print(" ❌ Element not found, cannot click")
print()

# Example 6: Using local extension (fast polling)
print("6. Using local extension with auto-optimized interval")
print(" When use_api=False, interval auto-adjusts to 0.25s (fast)")
wait_result = await wait_for_async(browser, "role=link", timeout=5.0, use_api=False)
if wait_result.found:
print(f" ✅ Found after {wait_result.duration_ms}ms")
print(" (Used local extension, polled every 0.25 seconds)")
print()

# Example 7: Using remote API (slower polling)
print("7. Using remote API with auto-optimized interval")
print(" When use_api=True, interval auto-adjusts to 1.5s (network-friendly)")
if api_key:
wait_result = await wait_for_async(browser, "role=link", timeout=5.0, use_api=True)
if wait_result.found:
print(f" ✅ Found after {wait_result.duration_ms}ms")
print(" (Used remote API, polled every 1.5 seconds)")
else:
print(" ⚠️ Skipped (no API key set)")
print()

# Example 8: Custom interval override
print("8. Custom interval override (manual control)")
print(" You can still specify custom interval if needed")
wait_result = await wait_for_async(
browser, "role=link", timeout=5.0, interval=0.5, use_api=False
)
if wait_result.found:
print(f" ✅ Found after {wait_result.duration_ms}ms")
print(" (Custom interval: 0.5 seconds)")
print()

print("✅ Semantic wait_for_async demo complete!")
print("\nNote: wait_for_async uses the semantic query DSL to find elements.")
print("This is more robust than CSS selectors because it understands")
print("the semantic meaning of elements (role, text, clickability, etc.).")


if __name__ == "__main__":
asyncio.run(main())
Loading