sentienceapi 0.92.2__py3-none-any.whl → 0.98.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of sentienceapi might be problematic. Click here for more details.
- sentience/__init__.py +107 -2
- sentience/_extension_loader.py +156 -1
- sentience/action_executor.py +2 -0
- sentience/actions.py +354 -9
- sentience/agent.py +4 -0
- sentience/agent_runtime.py +840 -0
- sentience/asserts/__init__.py +70 -0
- sentience/asserts/expect.py +621 -0
- sentience/asserts/query.py +383 -0
- sentience/async_api.py +8 -1
- sentience/backends/__init__.py +137 -0
- sentience/backends/actions.py +372 -0
- sentience/backends/browser_use_adapter.py +241 -0
- sentience/backends/cdp_backend.py +393 -0
- sentience/backends/exceptions.py +211 -0
- sentience/backends/playwright_backend.py +194 -0
- sentience/backends/protocol.py +216 -0
- sentience/backends/sentience_context.py +469 -0
- sentience/backends/snapshot.py +483 -0
- sentience/browser.py +230 -74
- sentience/canonicalization.py +207 -0
- sentience/cloud_tracing.py +65 -24
- sentience/constants.py +6 -0
- sentience/cursor_policy.py +142 -0
- sentience/extension/content.js +35 -0
- sentience/extension/injected_api.js +310 -15
- sentience/extension/manifest.json +1 -1
- sentience/extension/pkg/sentience_core.d.ts +22 -22
- sentience/extension/pkg/sentience_core.js +192 -144
- sentience/extension/pkg/sentience_core_bg.wasm +0 -0
- sentience/extension/release.json +29 -29
- sentience/failure_artifacts.py +241 -0
- sentience/integrations/__init__.py +6 -0
- sentience/integrations/langchain/__init__.py +12 -0
- sentience/integrations/langchain/context.py +18 -0
- sentience/integrations/langchain/core.py +326 -0
- sentience/integrations/langchain/tools.py +180 -0
- sentience/integrations/models.py +46 -0
- sentience/integrations/pydanticai/__init__.py +15 -0
- sentience/integrations/pydanticai/deps.py +20 -0
- sentience/integrations/pydanticai/toolset.py +468 -0
- sentience/llm_provider.py +695 -18
- sentience/models.py +536 -3
- sentience/ordinal.py +280 -0
- sentience/query.py +66 -4
- sentience/schemas/trace_v1.json +27 -1
- sentience/snapshot.py +384 -93
- sentience/snapshot_diff.py +39 -54
- sentience/text_search.py +1 -0
- sentience/trace_event_builder.py +20 -1
- sentience/trace_indexing/indexer.py +3 -49
- sentience/tracer_factory.py +1 -3
- sentience/verification.py +618 -0
- sentience/visual_agent.py +3 -1
- {sentienceapi-0.92.2.dist-info → sentienceapi-0.98.0.dist-info}/METADATA +198 -40
- sentienceapi-0.98.0.dist-info/RECORD +92 -0
- sentience/utils.py +0 -296
- sentienceapi-0.92.2.dist-info/RECORD +0 -65
- {sentienceapi-0.92.2.dist-info → sentienceapi-0.98.0.dist-info}/WHEEL +0 -0
- {sentienceapi-0.92.2.dist-info → sentienceapi-0.98.0.dist-info}/entry_points.txt +0 -0
- {sentienceapi-0.92.2.dist-info → sentienceapi-0.98.0.dist-info}/licenses/LICENSE +0 -0
- {sentienceapi-0.92.2.dist-info → sentienceapi-0.98.0.dist-info}/licenses/LICENSE-APACHE +0 -0
- {sentienceapi-0.92.2.dist-info → sentienceapi-0.98.0.dist-info}/licenses/LICENSE-MIT +0 -0
- {sentienceapi-0.92.2.dist-info → sentienceapi-0.98.0.dist-info}/top_level.txt +0 -0
sentience/__init__.py
CHANGED
|
@@ -2,9 +2,39 @@
|
|
|
2
2
|
Sentience Python SDK - AI Agent Browser Automation
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
# Extension helpers (for browser-use integration)
|
|
6
|
+
from ._extension_loader import (
|
|
7
|
+
get_extension_dir,
|
|
8
|
+
get_extension_version,
|
|
9
|
+
verify_extension_injected,
|
|
10
|
+
verify_extension_injected_async,
|
|
11
|
+
verify_extension_version,
|
|
12
|
+
verify_extension_version_async,
|
|
13
|
+
)
|
|
14
|
+
from .actions import click, click_rect, press, scroll_to, type_text
|
|
6
15
|
from .agent import SentienceAgent, SentienceAgentAsync
|
|
7
16
|
from .agent_config import AgentConfig
|
|
17
|
+
from .agent_runtime import AgentRuntime, AssertionHandle
|
|
18
|
+
|
|
19
|
+
# Backend-agnostic actions (aliased to avoid conflict with existing actions)
|
|
20
|
+
# Browser backends (for browser-use integration)
|
|
21
|
+
from .backends import (
|
|
22
|
+
BrowserBackend,
|
|
23
|
+
BrowserUseAdapter,
|
|
24
|
+
BrowserUseCDPTransport,
|
|
25
|
+
CachedSnapshot,
|
|
26
|
+
CDPBackendV0,
|
|
27
|
+
CDPTransport,
|
|
28
|
+
LayoutMetrics,
|
|
29
|
+
PlaywrightBackend,
|
|
30
|
+
ViewportInfo,
|
|
31
|
+
)
|
|
32
|
+
from .backends import click as backend_click
|
|
33
|
+
from .backends import scroll as backend_scroll
|
|
34
|
+
from .backends import scroll_to_element as backend_scroll_to_element
|
|
35
|
+
from .backends import snapshot as backend_snapshot
|
|
36
|
+
from .backends import type_text as backend_type_text
|
|
37
|
+
from .backends import wait_for_stable as backend_wait_for_stable
|
|
8
38
|
|
|
9
39
|
# Agent Layer (Phase 1 & 2)
|
|
10
40
|
from .base_agent import BaseAgent
|
|
@@ -13,6 +43,7 @@ from .browser import SentienceBrowser
|
|
|
13
43
|
# Tracing (v0.12.0+)
|
|
14
44
|
from .cloud_tracing import CloudTraceSink, SentienceLogger
|
|
15
45
|
from .conversational_agent import ConversationalAgent
|
|
46
|
+
from .cursor_policy import CursorPolicy
|
|
16
47
|
from .expect import expect
|
|
17
48
|
from .generator import ScriptGenerator, generate
|
|
18
49
|
from .inspector import Inspector, inspect
|
|
@@ -21,6 +52,8 @@ from .llm_provider import (
|
|
|
21
52
|
LLMProvider,
|
|
22
53
|
LLMResponse,
|
|
23
54
|
LocalLLMProvider,
|
|
55
|
+
LocalVisionLLMProvider,
|
|
56
|
+
MLXVLMProvider,
|
|
24
57
|
OpenAIProvider,
|
|
25
58
|
)
|
|
26
59
|
from .models import ( # Agent Layer Models
|
|
@@ -47,6 +80,9 @@ from .models import ( # Agent Layer Models
|
|
|
47
80
|
ViewportRect,
|
|
48
81
|
WaitResult,
|
|
49
82
|
)
|
|
83
|
+
|
|
84
|
+
# Ordinal support (Phase 3)
|
|
85
|
+
from .ordinal import OrdinalIntent, boost_ordinal_elements, detect_ordinal_intent, select_by_ordinal
|
|
50
86
|
from .overlay import clear_overlay, show_overlay
|
|
51
87
|
from .query import find, query
|
|
52
88
|
from .read import read
|
|
@@ -70,12 +106,59 @@ from .utils import (
|
|
|
70
106
|
|
|
71
107
|
# Formatting (v0.12.0+)
|
|
72
108
|
from .utils.formatting import format_snapshot_for_llm
|
|
109
|
+
|
|
110
|
+
# Verification (agent assertion loop)
|
|
111
|
+
from .verification import (
|
|
112
|
+
AssertContext,
|
|
113
|
+
AssertOutcome,
|
|
114
|
+
Predicate,
|
|
115
|
+
all_of,
|
|
116
|
+
any_of,
|
|
117
|
+
custom,
|
|
118
|
+
element_count,
|
|
119
|
+
exists,
|
|
120
|
+
is_checked,
|
|
121
|
+
is_collapsed,
|
|
122
|
+
is_disabled,
|
|
123
|
+
is_enabled,
|
|
124
|
+
is_expanded,
|
|
125
|
+
is_unchecked,
|
|
126
|
+
not_exists,
|
|
127
|
+
url_contains,
|
|
128
|
+
url_matches,
|
|
129
|
+
value_contains,
|
|
130
|
+
value_equals,
|
|
131
|
+
)
|
|
73
132
|
from .visual_agent import SentienceVisualAgent, SentienceVisualAgentAsync
|
|
74
133
|
from .wait import wait_for
|
|
75
134
|
|
|
76
|
-
__version__ = "0.
|
|
135
|
+
__version__ = "0.98.0"
|
|
77
136
|
|
|
78
137
|
__all__ = [
|
|
138
|
+
# Extension helpers (for browser-use integration)
|
|
139
|
+
"get_extension_dir",
|
|
140
|
+
"get_extension_version",
|
|
141
|
+
"verify_extension_injected",
|
|
142
|
+
"verify_extension_injected_async",
|
|
143
|
+
"verify_extension_version",
|
|
144
|
+
"verify_extension_version_async",
|
|
145
|
+
# Browser backends (for browser-use integration)
|
|
146
|
+
"BrowserBackend",
|
|
147
|
+
"CDPTransport",
|
|
148
|
+
"CDPBackendV0",
|
|
149
|
+
"PlaywrightBackend",
|
|
150
|
+
"BrowserUseAdapter",
|
|
151
|
+
"BrowserUseCDPTransport",
|
|
152
|
+
"ViewportInfo",
|
|
153
|
+
"LayoutMetrics",
|
|
154
|
+
"backend_snapshot",
|
|
155
|
+
"CachedSnapshot",
|
|
156
|
+
# Backend-agnostic actions (prefixed to avoid conflicts)
|
|
157
|
+
"backend_click",
|
|
158
|
+
"backend_type_text",
|
|
159
|
+
"backend_scroll",
|
|
160
|
+
"backend_scroll_to_element",
|
|
161
|
+
"backend_wait_for_stable",
|
|
79
162
|
# Core SDK
|
|
80
163
|
"SentienceBrowser",
|
|
81
164
|
"Snapshot",
|
|
@@ -90,7 +173,9 @@ __all__ = [
|
|
|
90
173
|
"click",
|
|
91
174
|
"type_text",
|
|
92
175
|
"press",
|
|
176
|
+
"scroll_to",
|
|
93
177
|
"click_rect",
|
|
178
|
+
"CursorPolicy",
|
|
94
179
|
"wait_for",
|
|
95
180
|
"expect",
|
|
96
181
|
"Inspector",
|
|
@@ -119,6 +204,8 @@ __all__ = [
|
|
|
119
204
|
"OpenAIProvider",
|
|
120
205
|
"AnthropicProvider",
|
|
121
206
|
"LocalLLMProvider",
|
|
207
|
+
"LocalVisionLLMProvider",
|
|
208
|
+
"MLXVLMProvider",
|
|
122
209
|
"SentienceAgent",
|
|
123
210
|
"SentienceAgentAsync",
|
|
124
211
|
"SentienceVisualAgent",
|
|
@@ -159,4 +246,22 @@ __all__ = [
|
|
|
159
246
|
# Enums
|
|
160
247
|
"SentienceMethod",
|
|
161
248
|
"AgentAction",
|
|
249
|
+
# Verification (agent assertion loop)
|
|
250
|
+
"AgentRuntime",
|
|
251
|
+
"AssertContext",
|
|
252
|
+
"AssertOutcome",
|
|
253
|
+
"Predicate",
|
|
254
|
+
"url_matches",
|
|
255
|
+
"url_contains",
|
|
256
|
+
"exists",
|
|
257
|
+
"not_exists",
|
|
258
|
+
"element_count",
|
|
259
|
+
"all_of",
|
|
260
|
+
"any_of",
|
|
261
|
+
"custom",
|
|
262
|
+
# Ordinal support (Phase 3)
|
|
263
|
+
"OrdinalIntent",
|
|
264
|
+
"detect_ordinal_intent",
|
|
265
|
+
"select_by_ordinal",
|
|
266
|
+
"boost_ordinal_elements",
|
|
162
267
|
]
|
sentience/_extension_loader.py
CHANGED
|
@@ -1,8 +1,19 @@
|
|
|
1
1
|
"""
|
|
2
|
-
Shared extension loading logic for sync and async implementations
|
|
2
|
+
Shared extension loading logic for sync and async implementations.
|
|
3
|
+
|
|
4
|
+
Provides:
|
|
5
|
+
- get_extension_dir(): Returns path to bundled extension (for browser-use integration)
|
|
6
|
+
- verify_extension_injected(): Verifies window.sentience API is available
|
|
7
|
+
- get_extension_version(): Gets extension version from manifest
|
|
8
|
+
- verify_extension_version(): Checks SDK-extension version compatibility
|
|
3
9
|
"""
|
|
4
10
|
|
|
11
|
+
import json
|
|
5
12
|
from pathlib import Path
|
|
13
|
+
from typing import TYPE_CHECKING, Any
|
|
14
|
+
|
|
15
|
+
if TYPE_CHECKING:
|
|
16
|
+
from .protocols import AsyncPageProtocol, PageProtocol
|
|
6
17
|
|
|
7
18
|
|
|
8
19
|
def find_extension_path() -> Path:
|
|
@@ -38,3 +49,147 @@ def find_extension_path() -> Path:
|
|
|
38
49
|
f"2. {dev_ext_path}\n"
|
|
39
50
|
"Make sure the extension is built and 'sentience/extension' directory exists."
|
|
40
51
|
)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def get_extension_dir() -> str:
|
|
55
|
+
"""
|
|
56
|
+
Get path to the bundled Sentience extension directory.
|
|
57
|
+
|
|
58
|
+
Use this to load the extension into browser-use or other Chromium-based browsers:
|
|
59
|
+
|
|
60
|
+
from sentience import get_extension_dir
|
|
61
|
+
from browser_use import BrowserSession, BrowserProfile
|
|
62
|
+
|
|
63
|
+
profile = BrowserProfile(
|
|
64
|
+
args=[f"--load-extension={get_extension_dir()}"],
|
|
65
|
+
)
|
|
66
|
+
session = BrowserSession(browser_profile=profile)
|
|
67
|
+
|
|
68
|
+
Returns:
|
|
69
|
+
Absolute path to extension directory as string
|
|
70
|
+
|
|
71
|
+
Raises:
|
|
72
|
+
FileNotFoundError: If extension not found in package
|
|
73
|
+
"""
|
|
74
|
+
return str(find_extension_path())
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def get_extension_version() -> str:
|
|
78
|
+
"""
|
|
79
|
+
Get the version of the bundled extension from manifest.json.
|
|
80
|
+
|
|
81
|
+
Returns:
|
|
82
|
+
Version string (e.g., "2.2.0")
|
|
83
|
+
|
|
84
|
+
Raises:
|
|
85
|
+
FileNotFoundError: If extension or manifest not found
|
|
86
|
+
"""
|
|
87
|
+
ext_path = find_extension_path()
|
|
88
|
+
manifest_path = ext_path / "manifest.json"
|
|
89
|
+
with open(manifest_path) as f:
|
|
90
|
+
manifest = json.load(f)
|
|
91
|
+
return manifest.get("version", "unknown")
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def verify_extension_injected(page: "PageProtocol") -> bool:
|
|
95
|
+
"""
|
|
96
|
+
Verify the Sentience extension injected window.sentience API (sync).
|
|
97
|
+
|
|
98
|
+
Call this after navigating to a page to confirm the extension is working:
|
|
99
|
+
|
|
100
|
+
browser.goto("https://example.com")
|
|
101
|
+
if not verify_extension_injected(browser.page):
|
|
102
|
+
raise RuntimeError("Extension not injected")
|
|
103
|
+
|
|
104
|
+
Args:
|
|
105
|
+
page: Playwright Page object (sync)
|
|
106
|
+
|
|
107
|
+
Returns:
|
|
108
|
+
True if window.sentience.snapshot is available, False otherwise
|
|
109
|
+
"""
|
|
110
|
+
try:
|
|
111
|
+
result = page.evaluate(
|
|
112
|
+
"(() => !!(window.sentience && typeof window.sentience.snapshot === 'function'))()"
|
|
113
|
+
)
|
|
114
|
+
return bool(result)
|
|
115
|
+
except Exception:
|
|
116
|
+
return False
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
async def verify_extension_injected_async(page: "AsyncPageProtocol") -> bool:
|
|
120
|
+
"""
|
|
121
|
+
Verify the Sentience extension injected window.sentience API (async).
|
|
122
|
+
|
|
123
|
+
Call this after navigating to a page to confirm the extension is working:
|
|
124
|
+
|
|
125
|
+
await browser.goto("https://example.com")
|
|
126
|
+
if not await verify_extension_injected_async(browser.page):
|
|
127
|
+
raise RuntimeError("Extension not injected")
|
|
128
|
+
|
|
129
|
+
Args:
|
|
130
|
+
page: Playwright Page object (async)
|
|
131
|
+
|
|
132
|
+
Returns:
|
|
133
|
+
True if window.sentience.snapshot is available, False otherwise
|
|
134
|
+
"""
|
|
135
|
+
try:
|
|
136
|
+
result = await page.evaluate(
|
|
137
|
+
"(() => !!(window.sentience && typeof window.sentience.snapshot === 'function'))()"
|
|
138
|
+
)
|
|
139
|
+
return bool(result)
|
|
140
|
+
except Exception:
|
|
141
|
+
return False
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def verify_extension_version(page: "PageProtocol", expected: str | None = None) -> str | None:
|
|
145
|
+
"""
|
|
146
|
+
Check extension version exposed in page (sync).
|
|
147
|
+
|
|
148
|
+
The extension sets window.__SENTIENCE_EXTENSION_VERSION__ when injected.
|
|
149
|
+
|
|
150
|
+
Args:
|
|
151
|
+
page: Playwright Page object (sync)
|
|
152
|
+
expected: If provided, raises RuntimeError on mismatch
|
|
153
|
+
|
|
154
|
+
Returns:
|
|
155
|
+
Version string if found, None if not set (page may not have injected yet)
|
|
156
|
+
|
|
157
|
+
Raises:
|
|
158
|
+
RuntimeError: If expected version provided and doesn't match
|
|
159
|
+
"""
|
|
160
|
+
try:
|
|
161
|
+
got = page.evaluate("window.__SENTIENCE_EXTENSION_VERSION__ || null")
|
|
162
|
+
except Exception:
|
|
163
|
+
got = None
|
|
164
|
+
|
|
165
|
+
if expected and got and got != expected:
|
|
166
|
+
raise RuntimeError(f"Sentience extension version mismatch: expected {expected}, got {got}")
|
|
167
|
+
return got
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
async def verify_extension_version_async(
|
|
171
|
+
page: "AsyncPageProtocol", expected: str | None = None
|
|
172
|
+
) -> str | None:
|
|
173
|
+
"""
|
|
174
|
+
Check extension version exposed in page (async).
|
|
175
|
+
|
|
176
|
+
The extension sets window.__SENTIENCE_EXTENSION_VERSION__ when injected.
|
|
177
|
+
|
|
178
|
+
Args:
|
|
179
|
+
page: Playwright Page object (async)
|
|
180
|
+
expected: If provided, raises RuntimeError on mismatch
|
|
181
|
+
|
|
182
|
+
Returns:
|
|
183
|
+
Version string if found, None if not set (page may not have injected yet)
|
|
184
|
+
|
|
185
|
+
Raises:
|
|
186
|
+
RuntimeError: If expected version provided and doesn't match
|
|
187
|
+
"""
|
|
188
|
+
try:
|
|
189
|
+
got = await page.evaluate("window.__SENTIENCE_EXTENSION_VERSION__ || null")
|
|
190
|
+
except Exception:
|
|
191
|
+
got = None
|
|
192
|
+
|
|
193
|
+
if expected and got and got != expected:
|
|
194
|
+
raise RuntimeError(f"Sentience extension version mismatch: expected {expected}, got {got}")
|
|
195
|
+
return got
|
sentience/action_executor.py
CHANGED
|
@@ -96,6 +96,7 @@ class ActionExecutor:
|
|
|
96
96
|
"element_id": element_id,
|
|
97
97
|
"outcome": result.outcome,
|
|
98
98
|
"url_changed": result.url_changed,
|
|
99
|
+
"cursor": getattr(result, "cursor", None),
|
|
99
100
|
}
|
|
100
101
|
|
|
101
102
|
# Parse TYPE(42, "hello world")
|
|
@@ -170,6 +171,7 @@ class ActionExecutor:
|
|
|
170
171
|
"element_id": element_id,
|
|
171
172
|
"outcome": result.outcome,
|
|
172
173
|
"url_changed": result.url_changed,
|
|
174
|
+
"cursor": getattr(result, "cursor", None),
|
|
173
175
|
}
|
|
174
176
|
|
|
175
177
|
# Parse TYPE(42, "hello world")
|