Keep the last in-template copy on main so history can restore it; product continues in jiangchang-ui-recorder. Co-authored-by: Cursor <cursoragent@cursor.com>
41 lines
1.4 KiB
Python
41 lines
1.4 KiB
Python
"""Structured locator candidate model."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from dataclasses import dataclass, field
|
|
from typing import Any, Optional
|
|
|
|
|
|
SCHEMA_LOCATOR = "openclaw.ui_inspector.locator_candidate.v1"
|
|
|
|
|
|
@dataclass
|
|
class LocatorCandidate:
|
|
schema: str = SCHEMA_LOCATOR
|
|
strategy: str = ""
|
|
scope: dict[str, Any] = field(default_factory=dict)
|
|
properties: dict[str, Any] = field(default_factory=dict)
|
|
expression: str = ""
|
|
expected_count: int = 1
|
|
actual_count: Optional[int] = None
|
|
unique: Optional[bool] = None
|
|
stability_score: float = 0.0
|
|
uses_index: bool = False
|
|
uses_coordinates: bool = False
|
|
validation: str = "none" # snapshot_only | live_tree | cross_state | cross_session | none
|
|
reasons: list[str] = field(default_factory=list)
|
|
ambiguous: bool = False
|
|
production_eligible: bool = False
|
|
|
|
def mark_counts(self, actual: int, *, expected: int = 1) -> None:
|
|
self.expected_count = int(expected)
|
|
self.actual_count = int(actual)
|
|
self.unique = self.actual_count == self.expected_count
|
|
self.ambiguous = not bool(self.unique)
|
|
# Coordinates / index never become production primary automatically.
|
|
self.production_eligible = bool(
|
|
self.unique
|
|
and not self.uses_coordinates
|
|
and not (self.uses_index and self.stability_score < 0.7)
|
|
)
|