Skip to content

Commit 68ad42e

Browse files
committed
Phase 1.1-1.2 Producer
1 parent 5e5a783 commit 68ad42e

20 files changed

+1742
-24
lines changed

.python-version

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
3.11.5

MANIFEST.in

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1 @@
1-
include README.md
2-
include LICENSE
3-
recursive-include spec *
4-
recursive-include sentience *.py
1+
include sentience/schemas/*.json

sentience/__init__.py

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,16 @@
44

55
from .actions import click, click_rect, press, type_text
66
from .agent import SentienceAgent
7+
from .agent_config import AgentConfig
78

89
# Agent Layer (Phase 1 & 2)
910
from .base_agent import BaseAgent
1011
from .browser import SentienceBrowser
1112
from .conversational_agent import ConversationalAgent
1213
from .expect import expect
14+
15+
# Formatting (v0.12.0+)
16+
from .formatting import format_snapshot_for_llm
1317
from .generator import ScriptGenerator, generate
1418
from .inspector import Inspector, inspect
1519
from .llm_provider import (
@@ -39,9 +43,20 @@
3943
from .recorder import Recorder, Trace, TraceStep, record
4044
from .screenshot import screenshot
4145
from .snapshot import snapshot
46+
47+
# Tracing (v0.12.0+)
48+
from .tracing import JsonlTraceSink, TraceEvent, Tracer, TraceSink
49+
50+
# Utilities (v0.12.0+)
51+
from .utils import (
52+
canonical_snapshot_loose,
53+
canonical_snapshot_strict,
54+
compute_snapshot_digests,
55+
sha256_digest,
56+
)
4257
from .wait import wait_for
4358

44-
__version__ = "0.11.0"
59+
__version__ = "0.12.0"
4560

4661
__all__ = [
4762
# Core SDK
@@ -88,4 +103,18 @@
88103
"SnapshotOptions",
89104
"SnapshotFilter",
90105
"ScreenshotConfig",
106+
# Tracing (v0.12.0+)
107+
"Tracer",
108+
"TraceSink",
109+
"JsonlTraceSink",
110+
"TraceEvent",
111+
# Utilities (v0.12.0+)
112+
"canonical_snapshot_strict",
113+
"canonical_snapshot_loose",
114+
"compute_snapshot_digests",
115+
"sha256_digest",
116+
# Formatting (v0.12.0+)
117+
"format_snapshot_for_llm",
118+
# Agent Config (v0.12.0+)
119+
"AgentConfig",
91120
]

sentience/agent_config.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
"""
2+
Configuration classes for Sentience agents.
3+
"""
4+
5+
from dataclasses import dataclass
6+
7+
8+
@dataclass
9+
class AgentConfig:
10+
"""
11+
Configuration for Sentience Agent execution.
12+
13+
This dataclass provides centralized configuration for agent behavior,
14+
including snapshot limits, retry logic, verification, and screenshot capture.
15+
16+
Attributes:
17+
snapshot_limit: Maximum elements to include in LLM context (default: 50)
18+
temperature: LLM temperature 0.0-1.0 for response generation (default: 0.0)
19+
max_retries: Number of retries on action failure (default: 1)
20+
verify: Whether to run verification step after actions (default: True)
21+
capture_screenshots: Whether to capture screenshots during execution (default: True)
22+
screenshot_format: Screenshot format 'png' or 'jpeg' (default: 'jpeg')
23+
screenshot_quality: JPEG quality 1-100, ignored for PNG (default: 80)
24+
25+
Example:
26+
>>> from sentience import AgentConfig, SentienceAgent
27+
>>> config = AgentConfig(
28+
... snapshot_limit=100,
29+
... max_retries=2,
30+
... verify=True
31+
... )
32+
>>> agent = SentienceAgent(browser, llm, config=config)
33+
"""
34+
35+
snapshot_limit: int = 50
36+
temperature: float = 0.0
37+
max_retries: int = 1
38+
verify: bool = True
39+
40+
# Screenshot options
41+
capture_screenshots: bool = True
42+
screenshot_format: str = "jpeg" # "png" or "jpeg"
43+
screenshot_quality: int = 80 # 1-100 (for JPEG only)

sentience/browser.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,10 @@ def start(self) -> None:
7575

7676
if package_ext_path.exists() and (package_ext_path / "manifest.json").exists():
7777
extension_source = package_ext_path
78+
print(f"[SentienceBrowser] Loading SDK extension from: {package_ext_path}")
7879
elif dev_ext_path.exists() and (dev_ext_path / "manifest.json").exists():
7980
extension_source = dev_ext_path
81+
print(f"[SentienceBrowser] Loading SDK extension from (dev): {dev_ext_path}")
8082
else:
8183
raise FileNotFoundError(
8284
f"Extension not found. Checked:\n"
@@ -85,6 +87,18 @@ def start(self) -> None:
8587
"Make sure the extension is built and 'sentience/extension' directory exists."
8688
)
8789

90+
# Print extension version for debugging
91+
import json
92+
93+
try:
94+
with open(extension_source / "manifest.json") as f:
95+
manifest = json.load(f)
96+
print(
97+
f"[SentienceBrowser] SDK extension version: {manifest.get('version', 'unknown')}"
98+
)
99+
except Exception:
100+
pass
101+
88102
# Create temporary extension bundle
89103
# We copy it to a temp dir to avoid file locking issues and ensure clean state
90104
self._extension_path = tempfile.mkdtemp(prefix="sentience-ext-")

sentience/cli.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,11 +75,9 @@ def cmd_gen(args):
7575
generator = ScriptGenerator(trace)
7676

7777
if args.lang == "py":
78-
code = generator.generate_python()
7978
output = args.output or "generated.py"
8079
generator.save_python(output)
8180
elif args.lang == "ts":
82-
code = generator.generate_typescript()
8381
output = args.output or "generated.ts"
8482
generator.save_typescript(output)
8583
else:

sentience/formatting.py

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
"""
2+
Snapshot formatting utilities for LLM prompts.
3+
4+
Provides functions to convert Sentience snapshots into text format suitable
5+
for LLM consumption.
6+
"""
7+
8+
from typing import List
9+
10+
from .models import Snapshot
11+
12+
13+
def format_snapshot_for_llm(snap: Snapshot, limit: int = 50) -> str:
14+
"""
15+
Convert snapshot elements to text format for LLM consumption.
16+
17+
This is the canonical way Sentience formats DOM state for LLMs.
18+
The format includes element ID, role, text preview, visual cues,
19+
position, and importance score.
20+
21+
Args:
22+
snap: Snapshot object with elements
23+
limit: Maximum number of elements to include (default: 50)
24+
25+
Returns:
26+
Formatted string with one element per line
27+
28+
Example:
29+
>>> snap = snapshot(browser)
30+
>>> formatted = format_snapshot_for_llm(snap, limit=10)
31+
>>> print(formatted)
32+
[1] <button> "Sign In" {PRIMARY,CLICKABLE} @ (100,50) (Imp:10)
33+
[2] <input> "Email address" @ (100,100) (Imp:8)
34+
...
35+
"""
36+
lines: list[str] = []
37+
38+
for el in snap.elements[:limit]:
39+
# Build visual cues string
40+
cues = []
41+
if getattr(el.visual_cues, "is_primary", False):
42+
cues.append("PRIMARY")
43+
if getattr(el.visual_cues, "is_clickable", False):
44+
cues.append("CLICKABLE")
45+
46+
cues_str = f" {{{','.join(cues)}}}" if cues else ""
47+
48+
# Format text preview (truncate to 50 chars)
49+
text_preview = el.text or ""
50+
if len(text_preview) > 50:
51+
text_preview = text_preview[:50] + "..."
52+
53+
# Build element line: [ID] <role> "text" {cues} @ (x,y) (Imp:score)
54+
lines.append(
55+
f'[{el.id}] <{el.role}> "{text_preview}"{cues_str} '
56+
f"@ ({int(el.bbox.x)},{int(el.bbox.y)}) (Imp:{el.importance})"
57+
)
58+
59+
return "\n".join(lines)

sentience/inspector.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,7 @@
22
Inspector tool - helps developers see what the agent "sees"
33
"""
44

5-
from typing import Optional
6-
75
from .browser import SentienceBrowser
8-
from .query import find
9-
from .snapshot import snapshot
106

117

128
class Inspector:

0 commit comments

Comments
 (0)