Skip to content

Commit cc20466

Browse files
author
SentienceDEV
committed
initial commit sentience sdk for python
0 parents  commit cc20466

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+1529
-0
lines changed

README.md

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
# Sentience Python SDK
2+
3+
**Status**: ✅ Week 1 Complete
4+
5+
Python SDK for Sentience AI Agent Browser Automation.
6+
7+
## Installation
8+
9+
```bash
10+
cd sdk-python
11+
pip install -e .
12+
```
13+
14+
## Quick Start
15+
16+
```python
17+
from sentience import SentienceBrowser, snapshot, find, click
18+
19+
# Start browser with extension
20+
with SentienceBrowser(headless=False) as browser:
21+
browser.page.goto("https://example.com")
22+
browser.page.wait_for_load_state("networkidle")
23+
24+
# Take snapshot
25+
snap = snapshot(browser)
26+
print(f"Found {len(snap.elements)} elements")
27+
28+
# Find and click a link
29+
link = find(snap, "role=link")
30+
if link:
31+
result = click(browser, link.id)
32+
print(f"Click success: {result.success}")
33+
```
34+
35+
## Features
36+
37+
### Day 2: Browser Harness
38+
- `SentienceBrowser` - Launch Playwright with extension loaded
39+
- Automatic extension loading and verification
40+
41+
### Day 3: Snapshot
42+
- `snapshot(browser, options)` - Capture page state
43+
- Pydantic models for type safety
44+
- `snapshot.save(filepath)` - Save to JSON
45+
46+
### Day 4: Query Engine
47+
- `query(snapshot, selector)` - Find elements matching selector
48+
- `find(snapshot, selector)` - Find single best match
49+
- String DSL: `"role=button text~'Sign in'"`
50+
51+
### Day 5: Actions
52+
- `click(browser, element_id)` - Click element
53+
- `type_text(browser, element_id, text)` - Type into element
54+
- `press(browser, key)` - Press keyboard key
55+
56+
### Day 6: Wait & Assert
57+
- `wait_for(browser, selector, timeout)` - Wait for element
58+
- `expect(browser, selector)` - Assertion helper
59+
- `.to_exist()`
60+
- `.to_be_visible()`
61+
- `.to_have_text(text)`
62+
- `.to_have_count(n)`
63+
64+
## Examples
65+
66+
See `examples/` directory:
67+
- `hello.py` - Extension bridge verification
68+
- `basic_agent.py` - Basic snapshot
69+
- `query_demo.py` - Query engine
70+
- `wait_and_click.py` - Wait and actions
71+
72+
## Testing
73+
74+
```bash
75+
pytest tests/
76+
```
77+
78+
## Documentation
79+
80+
- API Contract: `../spec/SNAPSHOT_V1.md`
81+
- Type Definitions: `../spec/sdk-types.md`

examples/basic_agent.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
"""
2+
Day 3 Example: Basic snapshot functionality
3+
"""
4+
5+
from sentience import SentienceBrowser, snapshot
6+
7+
8+
def main():
9+
with SentienceBrowser(headless=False) as browser:
10+
# Navigate to a test page
11+
browser.page.goto("https://example.com")
12+
browser.page.wait_for_load_state("networkidle")
13+
14+
# Take snapshot
15+
snap = snapshot(browser)
16+
17+
print(f"Status: {snap.status}")
18+
print(f"URL: {snap.url}")
19+
print(f"Elements found: {len(snap.elements)}")
20+
21+
# Show top 5 elements
22+
print("\nTop 5 elements:")
23+
for i, el in enumerate(snap.elements[:5], 1):
24+
print(f"{i}. [{el.role}] {el.text or '(no text)'} (importance: {el.importance})")
25+
26+
# Save snapshot
27+
snap.save("snapshot_example.json")
28+
print("\n✅ Snapshot saved to snapshot_example.json")
29+
30+
31+
if __name__ == "__main__":
32+
main()
33+

examples/hello.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
"""
2+
Day 2 Example: Verify extension bridge is loaded
3+
"""
4+
5+
from sentience import SentienceBrowser
6+
7+
8+
def main():
9+
with SentienceBrowser(headless=False) as browser:
10+
# Check if extension API is available
11+
bridge_ok = browser.page.evaluate("typeof window.sentience !== 'undefined'")
12+
print(f"bridge_ok={bridge_ok}")
13+
14+
if bridge_ok:
15+
print("✅ Extension loaded successfully!")
16+
else:
17+
print("❌ Extension not loaded")
18+
19+
20+
if __name__ == "__main__":
21+
main()
22+

examples/query_demo.py

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
"""
2+
Day 4 Example: Query engine demonstration
3+
"""
4+
5+
from sentience import SentienceBrowser, snapshot, query, find
6+
7+
8+
def main():
9+
with SentienceBrowser(headless=False) as browser:
10+
# Navigate to a page with links
11+
browser.page.goto("https://example.com")
12+
browser.page.wait_for_load_state("networkidle")
13+
14+
snap = snapshot(browser)
15+
16+
# Query examples
17+
print("=== Query Examples ===\n")
18+
19+
# Find all buttons
20+
buttons = query(snap, "role=button")
21+
print(f"Found {len(buttons)} buttons")
22+
23+
# Find all links
24+
links = query(snap, "role=link")
25+
print(f"Found {len(links)} links")
26+
27+
# Find clickable elements
28+
clickables = query(snap, "clickable=true")
29+
print(f"Found {len(clickables)} clickable elements")
30+
31+
# Find element with text containing "More"
32+
more_link = find(snap, "text~'More'")
33+
if more_link:
34+
print(f"\nFound 'More' link: {more_link.text} (id: {more_link.id})")
35+
else:
36+
print("\nNo 'More' link found")
37+
38+
# Complex query: clickable links
39+
clickable_links = query(snap, "role=link clickable=true")
40+
print(f"\nFound {len(clickable_links)} clickable links")
41+
42+
43+
if __name__ == "__main__":
44+
main()
45+

examples/wait_and_click.py

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
"""
2+
Day 5-6 Example: Wait for element and click
3+
"""
4+
5+
from sentience import SentienceBrowser, snapshot, find, wait_for, click, expect
6+
7+
8+
def main():
9+
with SentienceBrowser(headless=False) as browser:
10+
# Navigate to example.com
11+
browser.page.goto("https://example.com")
12+
browser.page.wait_for_load_state("networkidle")
13+
14+
# Take initial snapshot
15+
snap = snapshot(browser)
16+
17+
# Find a link
18+
link = find(snap, "role=link")
19+
20+
if link:
21+
print(f"Found link: {link.text} (id: {link.id})")
22+
23+
# Click it
24+
result = click(browser, link.id)
25+
print(f"Click result: success={result.success}, outcome={result.outcome}")
26+
27+
# Wait for navigation
28+
browser.page.wait_for_load_state("networkidle")
29+
print(f"New URL: {browser.page.url}")
30+
else:
31+
print("No link found")
32+
33+
# Example: Wait for element using wait_for
34+
print("\n=== Wait Example ===")
35+
browser.page.goto("https://example.com")
36+
browser.page.wait_for_load_state("networkidle")
37+
38+
wait_result = wait_for(browser, "role=link", timeout=5.0)
39+
if wait_result.found:
40+
print(f"✅ Found element after {wait_result.duration_ms}ms")
41+
else:
42+
print(f"❌ Element not found (timeout: {wait_result.timeout})")
43+
44+
# Example: Expect assertion
45+
print("\n=== Expect Example ===")
46+
try:
47+
element = expect(browser, "role=link").to_exist(timeout=5.0)
48+
print(f"✅ Element exists: {element.text}")
49+
except AssertionError as e:
50+
print(f"❌ Assertion failed: {e}")
51+
52+
53+
if __name__ == "__main__":
54+
main()
55+

package-lock.json

Lines changed: 6 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
[build-system]
2+
requires = ["setuptools>=61.0", "wheel"]
3+
build-backend = "setuptools.build_meta"
4+
5+
[project]
6+
name = "sentience-python"
7+
version = "0.1.0"
8+
description = "Python SDK for Sentience AI Agent Browser Automation"
9+
readme = "README.md"
10+
requires-python = ">=3.8"
11+
dependencies = [
12+
"playwright>=1.40.0",
13+
"pydantic>=2.0.0",
14+
"jsonschema>=4.0.0",
15+
]
16+
17+
[project.optional-dependencies]
18+
dev = [
19+
"pytest>=7.0.0",
20+
"pytest-asyncio>=0.21.0",
21+
]
22+
23+
[tool.setuptools.packages.find]
24+
where = ["."]
25+
include = ["sentience*"]
26+

pytest.ini

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
[pytest]
2+
testpaths = tests
3+
python_files = test_*.py
4+
python_classes = Test*
5+
python_functions = test_*
6+
asyncio_mode = auto
7+

sentience/__init__.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
"""
2+
Sentience Python SDK - AI Agent Browser Automation
3+
"""
4+
5+
from .browser import SentienceBrowser
6+
from .models import Snapshot, Element, BBox, Viewport, ActionResult, WaitResult
7+
from .query import query, find
8+
from .actions import click, type_text, press
9+
from .wait import wait_for
10+
from .expect import expect
11+
12+
__version__ = "0.1.0"
13+
14+
__all__ = [
15+
"SentienceBrowser",
16+
"Snapshot",
17+
"Element",
18+
"BBox",
19+
"Viewport",
20+
"ActionResult",
21+
"WaitResult",
22+
"query",
23+
"find",
24+
"click",
25+
"type_text",
26+
"press",
27+
"wait_for",
28+
"expect",
29+
]
30+
903 Bytes
Binary file not shown.

0 commit comments

Comments
 (0)