diff --git a/GramAddict/core/telepathic_engine.py b/GramAddict/core/telepathic_engine.py index 7220b8e..e5c3199 100644 --- a/GramAddict/core/telepathic_engine.py +++ b/GramAddict/core/telepathic_engine.py @@ -64,12 +64,6 @@ class TelepathicEngine: """ logger.debug(f"🧠 [SpatialEngine] Resolving intent: '{intent_description}'") - # 1.25 Structural Fast-Paths (Deterministically bypass VLM for fixed UI elements) - nodes_dicts = self._extract_semantic_nodes(xml_string) - fast_node = self._structural_fast_path(intent_description, nodes_dicts, kwargs.get("skip_positions"), xml_string) - if fast_node: - return fast_node - # 1. Parse into Spatial Topology root = self._parser.parse(xml_string) if not root: @@ -134,115 +128,6 @@ class TelepathicEngine: nodes = self._parser.get_clickable_nodes(root) return [self._translate_node(n) for n in nodes] - def _structural_fast_path(self, intent_description: str, nodes: list, skip_positions: set = None, xml_string: str = "") -> Optional[dict]: - if skip_positions is None: - skip_positions = set() - - intent_lower = intent_description.lower() - if "first image in explore grid" in intent_lower or "tap first grid item" in intent_lower: - grid_items = [ - n - for n in nodes - if n.get("y", 9999) < 2000 - and ( - "grid card layout container" in (n.get("semantic_string", "") or "").lower() - or "image button" in (n.get("semantic_string", "") or "").lower() - ) - and (n.get("x", -1), n.get("y", -1)) not in skip_positions - ] - if grid_items: - # Sort by y (row) then by x (col) - grid_items.sort(key=lambda n: (n.get("y", 9999), n.get("x", 9999))) - return grid_items[0] - - # --- Profile Structural Fast Paths --- - if "following list" in intent_lower or "followers list" in intent_lower: - target_id = "profile_header_following" if "following" in intent_lower else "profile_header_followers" - for n in nodes: - res_id = n.get("id", "") or n.get("resource_id", "") - if target_id in res_id: - return n - # Fallback to text matching if ID not found - for n in nodes: - sem = (n.get("semantic_string", "") or "").lower() - desc = (n.get("description", "") or "").lower() - text = (n.get("text", "") or "").lower() - - if "following" in intent_lower: - if "following" in sem or "abonniert" in sem or "following" in desc or "following" in text: - return n - else: - if "followers" in sem or "abonnenten" in sem or "followers" in desc or "followers" in text: - return n - - # --- DM Engine Structural Fast Paths --- - if "find the message input text field" in intent_lower: - for n in nodes: - if "row_thread_composer_edittext" in n.get("id", "") or "row_thread_composer_edittext" in n.get("resource_id", ""): - return n - - if "find the send message button" in intent_lower: - for n in nodes: - if "row_thread_composer_button_send" in n.get("id", "") or "row_thread_composer_button_send" in n.get("resource_id", ""): - return n - - if "find unread message threads" in intent_lower: - # We must be extremely strict here: It's only unread if it has the "unread" text or indicator dot - unread_candidates = [] - - # 1. Find all explicit unread dots in the UI - dot_nodes = [ - d for d in nodes - if "thread_indicator_status_dot" in (d.get("id", "") or d.get("resource_id", "")) - ] - - import re - - for n in nodes: - is_unread = False - res_id = n.get("id", "") or n.get("resource_id", "") - - if "row_inbox_container" in res_id and (n.get("x", -1), n.get("y", -1)) not in skip_positions: - content_desc = (n.get("description", "") or "").lower() - semantic = (n.get("semantic_string", "") or "").lower() - - # 1. Check for explicit 'unread' in description - if "unread" in content_desc or "unread" in semantic: - is_unread = True - - # 2. Check if an unread dot falls inside this container's bounds - if not is_unread and dot_nodes: - bounds_str = n.get("bounds", "") - m = re.match(r"\[\d+,(\d+)\]\[\d+,(\d+)\]", bounds_str) - if m: - y1, y2 = int(m.group(1)), int(m.group(2)) - for dot in dot_nodes: - dot_y = dot.get("y", -1) - if y1 <= dot_y <= y2: - is_unread = True - break - - if is_unread and n.get("y", 0) > 200: - unread_candidates.append(n) - - if unread_candidates: - unread_candidates.sort(key=lambda n: n.get("y", 9999)) - return unread_candidates[0] - - if "find the last received message text" in intent_lower: - msg_candidates = [] - for n in nodes: - res_id = n.get("id", "") or n.get("resource_id", "") - # The actual message text bubble - if "direct_text_message_text_view" in res_id or "message_content" in res_id: - msg_candidates.append(n) - - if msg_candidates: - # Sort by y descending (bottom-most message is the last one) - msg_candidates.sort(key=lambda n: n.get("y", 0), reverse=True) - return msg_candidates[0] - - return None # ────────────────────────────────────────────── # Action Memory Delegation