Skip to content

Commit 2667db5

Browse files
authored
Merge pull request #126 from SentienceAPI/agent_runtime
agent runtime verification loop + examples
2 parents 214bd1a + 8fad902 commit 2667db5

File tree

8 files changed

+1223
-2
lines changed

8 files changed

+1223
-2
lines changed

README.md

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -830,6 +830,40 @@ with browser:
830830

831831
</details>
832832

833+
<details>
834+
<summary><h3>🔍 Agent Runtime Verification</h3></summary>
835+
836+
`AgentRuntime` provides assertion predicates for runtime verification in agent loops, enabling programmatic verification of browser state during execution.
837+
838+
```python
839+
from sentience import (
840+
AgentRuntime, SentienceBrowser,
841+
url_contains, exists, all_of
842+
)
843+
from sentience.tracer_factory import create_tracer
844+
845+
browser = SentienceBrowser()
846+
browser.start()
847+
tracer = create_tracer(run_id="my-run", upload_trace=False)
848+
runtime = AgentRuntime(browser, browser.page, tracer)
849+
850+
# Navigate and take snapshot
851+
browser.page.goto("https://example.com")
852+
runtime.begin_step("Verify page")
853+
runtime.snapshot()
854+
855+
# Run assertions
856+
runtime.assert_(url_contains("example.com"), "on_correct_domain")
857+
runtime.assert_(exists("role=heading"), "has_heading")
858+
runtime.assert_done(exists("text~'Example'"), "task_complete")
859+
860+
print(f"Task done: {runtime.is_task_done}")
861+
```
862+
863+
**See example:** [`examples/agent_runtime_verification.py`](examples/agent_runtime_verification.py)
864+
865+
</details>
866+
833867
<details>
834868
<summary><h3>🧰 Snapshot Utilities</h3></summary>
835869

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
"""
2+
Example: Agent Runtime with Verification Loop
3+
4+
Demonstrates how to use AgentRuntime for runtime verification in agent loops.
5+
The AgentRuntime provides assertion predicates to verify browser state during execution.
6+
7+
Key features:
8+
- Predicate helpers: url_matches, url_contains, exists, not_exists, element_count
9+
- Combinators: all_of, any_of for complex conditions
10+
- Task completion: assert_done() for goal verification
11+
- Trace integration: Assertions emitted to trace for Studio timeline
12+
13+
Requirements:
14+
- SENTIENCE_API_KEY (Pro or Enterprise tier)
15+
16+
Usage:
17+
python examples/agent_runtime_verification.py
18+
"""
19+
20+
import os
21+
22+
from sentience import (
23+
AgentRuntime,
24+
SentienceBrowser,
25+
all_of,
26+
exists,
27+
not_exists,
28+
url_contains,
29+
url_matches,
30+
)
31+
from sentience.tracer_factory import create_tracer
32+
33+
34+
def main():
35+
# Get API key from environment
36+
sentience_key = os.environ.get("SENTIENCE_API_KEY")
37+
38+
if not sentience_key:
39+
print("Error: SENTIENCE_API_KEY not set")
40+
return
41+
42+
print("Starting Agent Runtime Verification Demo\n")
43+
44+
# 1. Create tracer for verification event emission
45+
run_id = "verification-demo"
46+
tracer = create_tracer(api_key=sentience_key, run_id=run_id, upload_trace=False)
47+
print(f"Run ID: {run_id}\n")
48+
49+
# 2. Create browser
50+
browser = SentienceBrowser(api_key=sentience_key, headless=False)
51+
browser.start()
52+
53+
try:
54+
# 3. Create AgentRuntime with browser, page, and tracer
55+
runtime = AgentRuntime(browser, browser.page, tracer)
56+
57+
# 4. Navigate to a page
58+
print("Navigating to example.com...\n")
59+
browser.page.goto("https://example.com")
60+
browser.page.wait_for_load_state("networkidle")
61+
62+
# 5. Begin a verification step
63+
runtime.begin_step("Verify page loaded correctly")
64+
65+
# 6. Take a snapshot (required for element assertions)
66+
snapshot = runtime.snapshot()
67+
print(f"Snapshot taken: {len(snapshot.elements)} elements found\n")
68+
69+
# 7. Run assertions against current state
70+
print("Running assertions:\n")
71+
72+
# URL assertions
73+
url_ok = runtime.assert_(url_contains("example.com"), "on_example_domain")
74+
print(f" [{'PASS' if url_ok else 'FAIL'}] on_example_domain")
75+
76+
url_match = runtime.assert_(url_matches(r"https://.*example\.com"), "url_is_https")
77+
print(f" [{'PASS' if url_match else 'FAIL'}] url_is_https")
78+
79+
# Element assertions
80+
has_heading = runtime.assert_(exists("role=heading"), "has_heading")
81+
print(f" [{'PASS' if has_heading else 'FAIL'}] has_heading")
82+
83+
no_error = runtime.assert_(not_exists("text~'Error'"), "no_error_message")
84+
print(f" [{'PASS' if no_error else 'FAIL'}] no_error_message")
85+
86+
# Combined assertion with all_of
87+
page_ready = runtime.assert_(
88+
all_of(url_contains("example"), exists("role=link")),
89+
"page_fully_ready",
90+
)
91+
print(f" [{'PASS' if page_ready else 'FAIL'}] page_fully_ready")
92+
93+
# 8. Check if task is done (required assertion)
94+
task_complete = runtime.assert_done(
95+
exists("text~'Example Domain'"),
96+
"reached_example_page",
97+
)
98+
print(f"\n [{'DONE' if task_complete else 'NOT DONE'}] reached_example_page")
99+
100+
# 9. Get accumulated assertions for step_end event
101+
assertions_data = runtime.get_assertions_for_step_end()
102+
print(f"\nTotal assertions: {len(assertions_data['assertions'])}")
103+
print(f"Task done: {assertions_data.get('task_done', False)}")
104+
105+
# 10. Check overall status
106+
print("\nVerification Summary:")
107+
print(f" All passed: {runtime.all_assertions_passed()}")
108+
print(f" Required passed: {runtime.required_assertions_passed()}")
109+
print(f" Task complete: {runtime.is_task_done}")
110+
111+
except Exception as e:
112+
print(f"\nError during execution: {e}")
113+
raise
114+
115+
finally:
116+
# Close tracer and browser
117+
print("\nClosing tracer...")
118+
tracer.close(blocking=True)
119+
print(f"Trace saved to: ~/.sentience/traces/{run_id}.jsonl")
120+
121+
browser.close()
122+
print("Done!")
123+
124+
125+
if __name__ == "__main__":
126+
main()

sentience/__init__.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from .actions import click, click_rect, press, scroll_to, type_text
66
from .agent import SentienceAgent, SentienceAgentAsync
77
from .agent_config import AgentConfig
8+
from .agent_runtime import AgentRuntime
89

910
# Agent Layer (Phase 1 & 2)
1011
from .base_agent import BaseAgent
@@ -70,6 +71,21 @@
7071

7172
# Formatting (v0.12.0+)
7273
from .utils.formatting import format_snapshot_for_llm
74+
75+
# Verification (agent assertion loop)
76+
from .verification import (
77+
AssertContext,
78+
AssertOutcome,
79+
Predicate,
80+
all_of,
81+
any_of,
82+
custom,
83+
element_count,
84+
exists,
85+
not_exists,
86+
url_contains,
87+
url_matches,
88+
)
7389
from .visual_agent import SentienceVisualAgent, SentienceVisualAgentAsync
7490
from .wait import wait_for
7591

@@ -160,4 +176,17 @@
160176
# Enums
161177
"SentienceMethod",
162178
"AgentAction",
179+
# Verification (agent assertion loop)
180+
"AgentRuntime",
181+
"AssertContext",
182+
"AssertOutcome",
183+
"Predicate",
184+
"url_matches",
185+
"url_contains",
186+
"exists",
187+
"not_exists",
188+
"element_count",
189+
"all_of",
190+
"any_of",
191+
"custom",
163192
]

0 commit comments

Comments
 (0)