|
| 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) |
0 commit comments