Skip to content

Commit 59fc5bd

Browse files
authored
Merge pull request #178 from SentienceAPI/bench_gaps
vision executor + enrich form actions
2 parents d4ed7e5 + 67729e4 commit 59fc5bd

28 files changed

+5709
-846
lines changed

.github/workflows/test.yml

Lines changed: 2 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -40,61 +40,8 @@ jobs:
4040
# Also clean .pyc files in sentience package specifically
4141
find sentience -type d -name __pycache__ -exec rm -rf {} + 2>/dev/null || python -c "import pathlib, shutil; [shutil.rmtree(p) for p in pathlib.Path('sentience').rglob('__pycache__') if p.is_dir()]" || true
4242
find sentience -name "*.pyc" -delete 2>/dev/null || python -c "import pathlib; [p.unlink() for p in pathlib.Path('sentience').rglob('*.pyc')]" || true
43-
# CRITICAL: Fix assertTrue bug if it exists in source (shouldn't happen, but safety check)
44-
python << 'PYEOF'
45-
import re
46-
import os
47-
import sys
48-
49-
# Set UTF-8 encoding for Windows compatibility
50-
if sys.platform == 'win32':
51-
import io
52-
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8', errors='replace')
53-
sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding='utf-8', errors='replace')
54-
55-
file_path = 'sentience/agent_runtime.py'
56-
print(f'=== Auto-fix check for {file_path} ===')
57-
try:
58-
if not os.path.exists(file_path):
59-
print(f'ERROR: {file_path} not found!')
60-
sys.exit(1)
61-
62-
with open(file_path, 'r', encoding='utf-8') as f:
63-
content = f.read()
64-
65-
if 'self.assertTrue(' in content:
66-
print('WARNING: Found self.assertTrue( in source file! Auto-fixing...')
67-
# Count occurrences
68-
count = len(re.findall(r'self\.assertTrue\s*\(', content))
69-
print(f'Found {count} occurrence(s) of self.assertTrue(')
70-
71-
# Replace all occurrences
72-
new_content = re.sub(r'self\.assertTrue\s*\(', 'self.assert_(', content)
73-
74-
# Write back
75-
with open(file_path, 'w', encoding='utf-8') as f:
76-
f.write(new_content)
77-
78-
# Verify the fix
79-
with open(file_path, 'r', encoding='utf-8') as f:
80-
verify_content = f.read()
81-
if 'self.assertTrue(' in verify_content:
82-
print('ERROR: Auto-fix failed! File still contains self.assertTrue(')
83-
sys.exit(1)
84-
else:
85-
print('OK: Auto-fixed: Replaced self.assertTrue( with self.assert_(')
86-
print('OK: Verified: File no longer contains self.assertTrue(')
87-
else:
88-
print('OK: Source file is correct (uses self.assert_())')
89-
except Exception as e:
90-
print(f'ERROR in auto-fix: {e}')
91-
import traceback
92-
traceback.print_exc()
93-
sys.exit(1)
94-
PYEOF
95-
# Verify source file is fixed before installation
96-
echo "=== Verifying source file after auto-fix ==="
97-
python -c "import sys; import io; sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8', errors='replace') if sys.platform == 'win32' else sys.stdout; content = open('sentience/agent_runtime.py', 'r', encoding='utf-8').read(); assert 'self.assertTrue(' not in content, 'Source file still has self.assertTrue( after auto-fix!'; print('OK: Source file verified: uses self.assert_()')"
43+
# Ensure source uses assert_ (no auto-rewrite).
44+
python -c "import sys; import io; sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8', errors='replace') if sys.platform == 'win32' else sys.stdout; content = open('sentience/agent_runtime.py', 'r', encoding='utf-8').read(); assert 'self.assertTrue(' not in content, 'Source file still has self.assertTrue('; print('OK: Source file verified: uses self.assert_()')"
9845
9946
# Force reinstall to ensure latest code
10047
pip install --no-cache-dir --force-reinstall -e ".[dev]"

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,3 +60,7 @@ Thumbs.db
6060
# Temporary directories from sync workflows
6161
extension-temp/
6262
playground/
63+
64+
# Allow bundled extension assets under sentience/extension/dist
65+
!sentience/extension/dist/
66+
!sentience/extension/dist/**

examples/runtime_agent_minimal.py

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,9 @@ async def main() -> None:
4747
await page.goto("https://example.com")
4848
await page.wait_for_load_state("networkidle")
4949

50-
runtime = await AgentRuntime.from_sentience_browser(browser=browser, page=page, tracer=tracer)
50+
runtime = await AgentRuntime.from_sentience_browser(
51+
browser=browser, page=page, tracer=tracer
52+
)
5153

5254
# Structured executor (for demo, we just return FINISH()).
5355
executor = FixedActionProvider("FINISH()")
@@ -63,15 +65,29 @@ async def main() -> None:
6365
def has_example_heading(ctx: AssertContext) -> AssertOutcome:
6466
# Demonstrates custom predicates (you can also use exists/url_contains helpers).
6567
snap = ctx.snapshot
66-
ok = bool(snap and any((el.role == "heading" and (el.text or "").startswith("Example")) for el in snap.elements))
68+
ok = bool(
69+
snap
70+
and any(
71+
(el.role == "heading" and (el.text or "").startswith("Example"))
72+
for el in snap.elements
73+
)
74+
)
6775
return AssertOutcome(passed=ok, reason="" if ok else "missing heading", details={})
6876

6977
step = RuntimeStep(
7078
goal="Confirm Example Domain page is loaded",
7179
verifications=[
72-
StepVerification(predicate=url_contains("example.com"), label="url_contains_example", required=True),
73-
StepVerification(predicate=exists("role=heading"), label="has_heading", required=True),
74-
StepVerification(predicate=has_example_heading, label="heading_text_matches", required=False),
80+
StepVerification(
81+
predicate=url_contains("example.com"),
82+
label="url_contains_example",
83+
required=True,
84+
),
85+
StepVerification(
86+
predicate=exists("role=heading"), label="has_heading", required=True
87+
),
88+
StepVerification(
89+
predicate=has_example_heading, label="heading_text_matches", required=False
90+
),
7591
],
7692
max_snapshot_attempts=2,
7793
snapshot_limit_base=60,
@@ -86,4 +102,3 @@ def has_example_heading(ctx: AssertContext) -> AssertOutcome:
86102

87103
if __name__ == "__main__":
88104
asyncio.run(main())
89-

scripts/sync_extension.sh

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
5+
REPO_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)"
6+
CHROME_DIR="${REPO_ROOT}/sentience-chrome"
7+
SDK_EXT_DIR="${REPO_ROOT}/sdk-python/sentience/extension"
8+
9+
if [[ ! -d "${CHROME_DIR}" ]]; then
10+
echo "[sync_extension] sentience-chrome not found at ${CHROME_DIR}"
11+
exit 1
12+
fi
13+
14+
if [[ ! -f "${CHROME_DIR}/package.json" ]]; then
15+
echo "[sync_extension] package.json missing in sentience-chrome"
16+
exit 1
17+
fi
18+
19+
echo "[sync_extension] Building sentience-chrome..."
20+
pushd "${CHROME_DIR}" >/dev/null
21+
npm run build
22+
popd >/dev/null
23+
24+
echo "[sync_extension] Syncing dist/ and pkg/ to sdk-python..."
25+
mkdir -p "${SDK_EXT_DIR}/dist" "${SDK_EXT_DIR}/pkg"
26+
cp "${CHROME_DIR}/dist/"* "${SDK_EXT_DIR}/dist/"
27+
cp "${CHROME_DIR}/pkg/"* "${SDK_EXT_DIR}/pkg/"
28+
29+
echo "[sync_extension] Done."

sentience/__init__.py

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,20 @@
1111
verify_extension_version,
1212
verify_extension_version_async,
1313
)
14-
from .actions import click, click_rect, press, scroll_to, type_text
14+
from .actions import (
15+
back,
16+
check,
17+
clear,
18+
click,
19+
click_rect,
20+
press,
21+
scroll_to,
22+
select_option,
23+
submit,
24+
type_text,
25+
uncheck,
26+
upload_file,
27+
)
1528
from .agent import SentienceAgent, SentienceAgentAsync
1629
from .agent_config import AgentConfig
1730
from .agent_runtime import AgentRuntime, AssertionHandle
@@ -118,6 +131,7 @@
118131
all_of,
119132
any_of,
120133
custom,
134+
download_completed,
121135
element_count,
122136
exists,
123137
is_checked,
@@ -132,6 +146,13 @@
132146
value_contains,
133147
value_equals,
134148
)
149+
150+
# Vision executor primitives (shared parsing/execution helpers)
151+
from .vision_executor import (
152+
VisionExecutorAction,
153+
execute_vision_executor_action,
154+
parse_vision_executor_action,
155+
)
135156
from .visual_agent import SentienceVisualAgent, SentienceVisualAgentAsync
136157
from .wait import wait_for
137158

0 commit comments

Comments
 (0)