From 68ad42eb53fc3120a12076881af01765ee5fb098 Mon Sep 17 00:00:00 2001 From: rcholic Date: Fri, 26 Dec 2025 07:43:57 -0800 Subject: [PATCH 1/5] Phase 1.1-1.2 Producer --- .python-version | 1 + MANIFEST.in | 5 +- sentience/__init__.py | 31 ++- sentience/agent_config.py | 43 ++++ sentience/browser.py | 14 ++ sentience/cli.py | 2 - sentience/formatting.py | 59 ++++++ sentience/inspector.py | 4 - sentience/schemas/trace_v1.json | 216 ++++++++++++++++++++ sentience/tracing.py | 257 ++++++++++++++++++++++++ sentience/utils.py | 257 ++++++++++++++++++++++++ sentience_python.egg-info/PKG-INFO | 2 +- tests/test_agent_config.py | 119 +++++++++++ tests/test_formatting.py | 219 ++++++++++++++++++++ tests/test_inspector.py | 2 - tests/test_recorder.py | 5 +- tests/test_smart_selector.py | 2 - tests/test_stealth.py | 8 +- tests/test_tracing.py | 209 +++++++++++++++++++ tests/test_utils.py | 311 +++++++++++++++++++++++++++++ 20 files changed, 1742 insertions(+), 24 deletions(-) create mode 100644 .python-version create mode 100644 sentience/agent_config.py create mode 100644 sentience/formatting.py create mode 100644 sentience/schemas/trace_v1.json create mode 100644 sentience/tracing.py create mode 100644 sentience/utils.py create mode 100644 tests/test_agent_config.py create mode 100644 tests/test_formatting.py create mode 100644 tests/test_tracing.py create mode 100644 tests/test_utils.py diff --git a/.python-version b/.python-version new file mode 100644 index 0000000..9ac3804 --- /dev/null +++ b/.python-version @@ -0,0 +1 @@ +3.11.5 diff --git a/MANIFEST.in b/MANIFEST.in index 921b4e2..1cc7b2a 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,4 +1 @@ -include README.md -include LICENSE -recursive-include spec * -recursive-include sentience *.py +include sentience/schemas/*.json diff --git a/sentience/__init__.py b/sentience/__init__.py index edec2ab..2ac9c18 100644 --- a/sentience/__init__.py +++ b/sentience/__init__.py @@ -4,12 +4,16 @@ from .actions import click, click_rect, press, type_text from .agent import SentienceAgent +from .agent_config import AgentConfig # Agent Layer (Phase 1 & 2) from .base_agent import BaseAgent from .browser import SentienceBrowser from .conversational_agent import ConversationalAgent from .expect import expect + +# Formatting (v0.12.0+) +from .formatting import format_snapshot_for_llm from .generator import ScriptGenerator, generate from .inspector import Inspector, inspect from .llm_provider import ( @@ -39,9 +43,20 @@ from .recorder import Recorder, Trace, TraceStep, record from .screenshot import screenshot from .snapshot import snapshot + +# Tracing (v0.12.0+) +from .tracing import JsonlTraceSink, TraceEvent, Tracer, TraceSink + +# Utilities (v0.12.0+) +from .utils import ( + canonical_snapshot_loose, + canonical_snapshot_strict, + compute_snapshot_digests, + sha256_digest, +) from .wait import wait_for -__version__ = "0.11.0" +__version__ = "0.12.0" __all__ = [ # Core SDK @@ -88,4 +103,18 @@ "SnapshotOptions", "SnapshotFilter", "ScreenshotConfig", + # Tracing (v0.12.0+) + "Tracer", + "TraceSink", + "JsonlTraceSink", + "TraceEvent", + # Utilities (v0.12.0+) + "canonical_snapshot_strict", + "canonical_snapshot_loose", + "compute_snapshot_digests", + "sha256_digest", + # Formatting (v0.12.0+) + "format_snapshot_for_llm", + # Agent Config (v0.12.0+) + "AgentConfig", ] diff --git a/sentience/agent_config.py b/sentience/agent_config.py new file mode 100644 index 0000000..dbddb41 --- /dev/null +++ b/sentience/agent_config.py @@ -0,0 +1,43 @@ +""" +Configuration classes for Sentience agents. +""" + +from dataclasses import dataclass + + +@dataclass +class AgentConfig: + """ + Configuration for Sentience Agent execution. + + This dataclass provides centralized configuration for agent behavior, + including snapshot limits, retry logic, verification, and screenshot capture. + + Attributes: + snapshot_limit: Maximum elements to include in LLM context (default: 50) + temperature: LLM temperature 0.0-1.0 for response generation (default: 0.0) + max_retries: Number of retries on action failure (default: 1) + verify: Whether to run verification step after actions (default: True) + capture_screenshots: Whether to capture screenshots during execution (default: True) + screenshot_format: Screenshot format 'png' or 'jpeg' (default: 'jpeg') + screenshot_quality: JPEG quality 1-100, ignored for PNG (default: 80) + + Example: + >>> from sentience import AgentConfig, SentienceAgent + >>> config = AgentConfig( + ... snapshot_limit=100, + ... max_retries=2, + ... verify=True + ... ) + >>> agent = SentienceAgent(browser, llm, config=config) + """ + + snapshot_limit: int = 50 + temperature: float = 0.0 + max_retries: int = 1 + verify: bool = True + + # Screenshot options + capture_screenshots: bool = True + screenshot_format: str = "jpeg" # "png" or "jpeg" + screenshot_quality: int = 80 # 1-100 (for JPEG only) diff --git a/sentience/browser.py b/sentience/browser.py index cbeff56..b83357c 100644 --- a/sentience/browser.py +++ b/sentience/browser.py @@ -75,8 +75,10 @@ def start(self) -> None: if package_ext_path.exists() and (package_ext_path / "manifest.json").exists(): extension_source = package_ext_path + print(f"[SentienceBrowser] Loading SDK extension from: {package_ext_path}") elif dev_ext_path.exists() and (dev_ext_path / "manifest.json").exists(): extension_source = dev_ext_path + print(f"[SentienceBrowser] Loading SDK extension from (dev): {dev_ext_path}") else: raise FileNotFoundError( f"Extension not found. Checked:\n" @@ -85,6 +87,18 @@ def start(self) -> None: "Make sure the extension is built and 'sentience/extension' directory exists." ) + # Print extension version for debugging + import json + + try: + with open(extension_source / "manifest.json") as f: + manifest = json.load(f) + print( + f"[SentienceBrowser] SDK extension version: {manifest.get('version', 'unknown')}" + ) + except Exception: + pass + # Create temporary extension bundle # We copy it to a temp dir to avoid file locking issues and ensure clean state self._extension_path = tempfile.mkdtemp(prefix="sentience-ext-") diff --git a/sentience/cli.py b/sentience/cli.py index a7f0ef4..64112e9 100644 --- a/sentience/cli.py +++ b/sentience/cli.py @@ -75,11 +75,9 @@ def cmd_gen(args): generator = ScriptGenerator(trace) if args.lang == "py": - code = generator.generate_python() output = args.output or "generated.py" generator.save_python(output) elif args.lang == "ts": - code = generator.generate_typescript() output = args.output or "generated.ts" generator.save_typescript(output) else: diff --git a/sentience/formatting.py b/sentience/formatting.py new file mode 100644 index 0000000..f8961c5 --- /dev/null +++ b/sentience/formatting.py @@ -0,0 +1,59 @@ +""" +Snapshot formatting utilities for LLM prompts. + +Provides functions to convert Sentience snapshots into text format suitable +for LLM consumption. +""" + +from typing import List + +from .models import Snapshot + + +def format_snapshot_for_llm(snap: Snapshot, limit: int = 50) -> str: + """ + Convert snapshot elements to text format for LLM consumption. + + This is the canonical way Sentience formats DOM state for LLMs. + The format includes element ID, role, text preview, visual cues, + position, and importance score. + + Args: + snap: Snapshot object with elements + limit: Maximum number of elements to include (default: 50) + + Returns: + Formatted string with one element per line + + Example: + >>> snap = snapshot(browser) + >>> formatted = format_snapshot_for_llm(snap, limit=10) + >>> print(formatted) + [1]