File size: 2,981 Bytes
b7fc81a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
"""Versioned mobile semantics; no model, device or frozen codec changes on import."""

import math
from dataclasses import asdict, dataclass

LEGACY_PROTOCOL = "gui-owl-unified133-desktop-android-historybins-v1"
PROTOCOL = "gui-owl-unified133-mobile-semantics-v2"
SCHEMA = "unified133-mobile-subgoal-sft-v2"
DIRECTIONS = ("up", "down", "left", "right")
ANDROID_WORLD_REVISION = "55ddaf5edc6f6c25673aed343907d5b9dca66f84"


@dataclass(frozen=True)
class MobileExecutionConfig:
    """Runtime policy, never a claim about missing demonstration durations.

    AndroidWorld uses 1 second for wait and long press. Its directional scroll
    leaves duration to Android's input command. Coordinate swipe stays on the
    frozen executor's explicit 800 ms path.
    """

    protocol: str = PROTOCOL
    wait_seconds: float = 1.0
    long_press_seconds: float = 1.0
    scroll_duration_ms: int | None = None
    scroll_geometry: str = "androidworld-screen-center-to-edge"
    reference_revision: str = ANDROID_WORLD_REVISION

    def __post_init__(self):
        if self.protocol != PROTOCOL:
            raise ValueError("execution_protocol_mismatch")
        for value in (self.wait_seconds, self.long_press_seconds):
            if (
                type(value) not in (int, float)
                or not math.isfinite(value)
                or not 0 < value <= 999.99
            ):
                raise ValueError("invalid_execution_default_duration")
        if self.scroll_duration_ms is not None and (
            type(self.scroll_duration_ms) is not int or not 1 <= self.scroll_duration_ms <= 999990
        ):
            raise ValueError("invalid_scroll_duration")
        if self.scroll_geometry != "androidworld-screen-center-to-edge":
            raise ValueError("unknown_scroll_geometry")
        if self.reference_revision != ANDROID_WORLD_REVISION:
            raise ValueError("execution_reference_mismatch")

    def as_dict(self):
        return asdict(self)


def direction_scroll_argv(direction, size, config):
    """Small argv-only adapter for pinned AndroidWorld execute_adb_action(scroll).

    Apache-2.0 reference: third_party/android_world_mobile_reference. The full
    executor imports AndroidEnv/ADB services and also changes text-entry semantics;
    only this GUI primitive is adapted. Endpoints are runtime geometry, not labels.
    """
    if direction not in DIRECTIONS:
        raise ValueError("invalid_scroll_direction")
    if len(size) != 2 or any(type(v) is not int or v <= 0 for v in size):
        raise ValueError("invalid_screen_size")
    width, height = size
    start = [width // 2, height // 2]
    ends = {
        "down": [width // 2, 0],
        "up": [width // 2, height],
        "right": [0, height // 2],
        "left": [width, height // 2],
    }
    argv = ["input", "swipe", *map(str, start + ends[direction])]
    if config.scroll_duration_ms is not None:
        argv.append(str(config.scroll_duration_ms))
    return argv