Skip to content

Commit 7fbc3c5

Browse files
authored
Merge pull request #79 from SentienceAPI/trace_indexing
Trace indexing
2 parents 0843f85 + 6d988f9 commit 7fbc3c5

File tree

9 files changed

+1075
-7
lines changed

9 files changed

+1075
-7
lines changed

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
44

55
[project]
66
name = "sentienceapi"
7-
version = "0.90.7"
7+
version = "0.90.8"
88
description = "Python SDK for Sentience AI Agent Browser Automation"
99
readme = "README.md"
1010
requires-python = ">=3.11"

sentience/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@
7070
)
7171
from .wait import wait_for
7272

73-
__version__ = "0.90.7"
73+
__version__ = "0.90.8"
7474

7575
__all__ = [
7676
# Core SDK

sentience/cloud_tracing.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,9 @@ def close(
145145
# Close file first
146146
self._trace_file.close()
147147

148+
# Generate index after closing file
149+
self._generate_index()
150+
148151
if not blocking:
149152
# Fire-and-forget background upload
150153
thread = threading.Thread(
@@ -231,6 +234,16 @@ def _do_upload(self, on_progress: Callable[[int, int], None] | None = None) -> N
231234
print(f" Local trace preserved at: {self._path}")
232235
# Don't raise - preserve trace locally even if upload fails
233236

237+
def _generate_index(self) -> None:
238+
"""Generate trace index file (automatic on close)."""
239+
try:
240+
from .trace_indexing import write_trace_index
241+
242+
write_trace_index(str(self._path))
243+
except Exception as e:
244+
# Non-fatal: log but don't crash
245+
print(f"⚠️ Failed to generate trace index: {e}")
246+
234247
def _complete_trace(self) -> None:
235248
"""
236249
Call /v1/traces/complete to report file sizes to gateway.
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
"""
2+
Trace indexing module for Sentience SDK.
3+
"""
4+
5+
from .indexer import build_trace_index, write_trace_index, read_step_events
6+
from .index_schema import (
7+
TraceIndex,
8+
StepIndex,
9+
TraceSummary,
10+
TraceFileInfo,
11+
SnapshotInfo,
12+
ActionInfo,
13+
StepCounters,
14+
)
15+
16+
__all__ = [
17+
"build_trace_index",
18+
"write_trace_index",
19+
"read_step_events",
20+
"TraceIndex",
21+
"StepIndex",
22+
"TraceSummary",
23+
"TraceFileInfo",
24+
"SnapshotInfo",
25+
"ActionInfo",
26+
"StepCounters",
27+
]
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
"""
2+
Type definitions for trace index schema using concrete classes.
3+
"""
4+
5+
from dataclasses import dataclass, field, asdict
6+
from typing import Optional, List, Literal
7+
8+
9+
@dataclass
10+
class TraceFileInfo:
11+
"""Metadata about the trace file."""
12+
13+
path: str
14+
size_bytes: int
15+
sha256: str
16+
17+
def to_dict(self) -> dict:
18+
return asdict(self)
19+
20+
21+
@dataclass
22+
class TraceSummary:
23+
"""High-level summary of the trace."""
24+
25+
first_ts: str
26+
last_ts: str
27+
event_count: int
28+
step_count: int
29+
error_count: int
30+
final_url: Optional[str]
31+
32+
def to_dict(self) -> dict:
33+
return asdict(self)
34+
35+
36+
@dataclass
37+
class SnapshotInfo:
38+
"""Snapshot metadata for index."""
39+
40+
snapshot_id: Optional[str] = None
41+
digest: Optional[str] = None
42+
url: Optional[str] = None
43+
44+
def to_dict(self) -> dict:
45+
return asdict(self)
46+
47+
48+
@dataclass
49+
class ActionInfo:
50+
"""Action metadata for index."""
51+
52+
type: Optional[str] = None
53+
target_element_id: Optional[int] = None
54+
args_digest: Optional[str] = None
55+
success: Optional[bool] = None
56+
57+
def to_dict(self) -> dict:
58+
return asdict(self)
59+
60+
61+
@dataclass
62+
class StepCounters:
63+
"""Event counters per step."""
64+
65+
events: int = 0
66+
snapshots: int = 0
67+
actions: int = 0
68+
llm_calls: int = 0
69+
70+
def to_dict(self) -> dict:
71+
return asdict(self)
72+
73+
74+
@dataclass
75+
class StepIndex:
76+
"""Index entry for a single step."""
77+
78+
step_index: int
79+
step_id: str
80+
goal: Optional[str]
81+
status: Literal["ok", "error", "partial"]
82+
ts_start: str
83+
ts_end: str
84+
offset_start: int
85+
offset_end: int
86+
url_before: Optional[str]
87+
url_after: Optional[str]
88+
snapshot_before: SnapshotInfo
89+
snapshot_after: SnapshotInfo
90+
action: ActionInfo
91+
counters: StepCounters
92+
93+
def to_dict(self) -> dict:
94+
result = asdict(self)
95+
return result
96+
97+
98+
@dataclass
99+
class TraceIndex:
100+
"""Complete trace index schema."""
101+
102+
version: int
103+
run_id: str
104+
created_at: str
105+
trace_file: TraceFileInfo
106+
summary: TraceSummary
107+
steps: List[StepIndex] = field(default_factory=list)
108+
109+
def to_dict(self) -> dict:
110+
"""Convert to dictionary for JSON serialization."""
111+
return asdict(self)

0 commit comments

Comments
 (0)