Skip to content

Commit d51319f

Browse files
author
SentienceDEV
committed
fix llm data structure
1 parent 3d870c2 commit d51319f

File tree

3 files changed

+88
-1
lines changed

3 files changed

+88
-1
lines changed

sentience/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,8 @@
8585
BBox,
8686
Cookie,
8787
Element,
88+
LLMStepData,
89+
LLMUsage,
8890
LocalStorageItem,
8991
OriginStorage,
9092
ScreenshotConfig,
@@ -255,6 +257,8 @@
255257
"TokenStats",
256258
"ActionHistory",
257259
"ActionTokenUsage",
260+
"LLMStepData",
261+
"LLMUsage",
258262
"SnapshotOptions",
259263
"SnapshotFilter",
260264
"ScreenshotConfig",

sentience/agent_runtime.py

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@
8181
from .models import (
8282
EvaluateJsRequest,
8383
EvaluateJsResult,
84+
LLMStepData,
8485
Snapshot,
8586
SnapshotOptions,
8687
TabInfo,
@@ -792,9 +793,26 @@ async def emit_step_end(
792793
verify_signals: dict[str, Any] | None = None,
793794
post_url: str | None = None,
794795
post_snapshot_digest: str | None = None,
796+
llm_data: dict[str, Any] | LLMStepData | None = None,
795797
) -> dict[str, Any]:
796798
"""
797799
Emit a step_end event using TraceEventBuilder.
800+
801+
Args:
802+
action: Action name/type executed in this step
803+
success: Whether the action execution succeeded
804+
error: Error message if action failed
805+
outcome: Outcome description of the action
806+
duration_ms: Duration of action execution in milliseconds
807+
attempt: Attempt number (0-based)
808+
verify_passed: Whether verification passed
809+
verify_signals: Additional verification signals
810+
post_url: URL after action execution
811+
post_snapshot_digest: Digest of post-action snapshot
812+
llm_data: LLM interaction data for this step. Can be:
813+
- LLMStepData: Structured model with response_text, response_hash, usage, model
814+
- dict: Raw dict with response_text, response_hash, usage keys
815+
- None: No LLM data (defaults to empty dict)
798816
"""
799817
goal = self._step_goal or ""
800818
pre_snap = self._step_pre_snapshot or self.last_snapshot
@@ -850,6 +868,15 @@ async def emit_step_end(
850868
"signals": signals,
851869
}
852870

871+
# Convert LLMStepData to dict if needed
872+
llm_data_dict: dict[str, Any]
873+
if llm_data is None:
874+
llm_data_dict = {}
875+
elif isinstance(llm_data, LLMStepData):
876+
llm_data_dict = llm_data.to_trace_dict()
877+
else:
878+
llm_data_dict = llm_data
879+
853880
step_end_data = TraceEventBuilder.build_step_end_event(
854881
step_id=self.step_id or "",
855882
step_index=int(self.step_index),
@@ -858,7 +885,7 @@ async def emit_step_end(
858885
pre_url=str(pre_url or ""),
859886
post_url=str(post_url or ""),
860887
snapshot_digest=pre_digest,
861-
llm_data={},
888+
llm_data=llm_data_dict,
862889
exec_data=exec_data,
863890
verify_data=verify_data,
864891
pre_elements=None,

sentience/models.py

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -836,6 +836,62 @@ class ActionTokenUsage(BaseModel):
836836
model: str
837837

838838

839+
class LLMUsage(BaseModel):
840+
"""Token usage for a single LLM call"""
841+
842+
prompt_tokens: int = 0
843+
completion_tokens: int = 0
844+
total_tokens: int = 0
845+
846+
847+
class LLMStepData(BaseModel):
848+
"""
849+
LLM interaction data for a single step in agent traces.
850+
851+
This structure is used in step_end trace events to capture LLM response
852+
details for debugging and analysis in Sentience Studio.
853+
"""
854+
855+
response_text: str | None = Field(
856+
None,
857+
description="The LLM's response text for this step",
858+
)
859+
response_hash: str | None = Field(
860+
None,
861+
description="SHA256 hash of response_text for deduplication/indexing",
862+
)
863+
usage: LLMUsage | None = Field(
864+
None,
865+
description="Token usage statistics for this LLM call",
866+
)
867+
model: str | None = Field(
868+
None,
869+
description="Model identifier used for this call (e.g., 'gpt-4o', 'claude-3-5-sonnet')",
870+
)
871+
872+
def to_trace_dict(self) -> dict[str, Any]:
873+
"""
874+
Convert to dictionary format expected by TraceEventBuilder.
875+
876+
Returns:
877+
Dict with response_text, response_hash, and usage fields
878+
"""
879+
result: dict[str, Any] = {}
880+
if self.response_text is not None:
881+
result["response_text"] = self.response_text
882+
if self.response_hash is not None:
883+
result["response_hash"] = self.response_hash
884+
if self.usage is not None:
885+
result["usage"] = {
886+
"prompt_tokens": self.usage.prompt_tokens,
887+
"completion_tokens": self.usage.completion_tokens,
888+
"total_tokens": self.usage.total_tokens,
889+
}
890+
if self.model is not None:
891+
result["model"] = self.model
892+
return result
893+
894+
839895
class TokenStats(BaseModel):
840896
"""Token usage statistics for an agent session"""
841897

0 commit comments

Comments
 (0)