openadapt-types 0.2.0__tar.gz → 0.3.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {openadapt_types-0.2.0 → openadapt_types-0.3.0}/CHANGELOG.md +20 -0
- {openadapt_types-0.2.0 → openadapt_types-0.3.0}/PKG-INFO +1 -1
- {openadapt_types-0.2.0 → openadapt_types-0.3.0}/openadapt_types/__init__.py +11 -0
- openadapt_types-0.3.0/openadapt_types/benchmark.py +202 -0
- {openadapt_types-0.2.0 → openadapt_types-0.3.0}/pyproject.toml +2 -1
- openadapt_types-0.3.0/tests/test_benchmark.py +86 -0
- {openadapt_types-0.2.0 → openadapt_types-0.3.0}/.github/workflows/publish.yml +0 -0
- {openadapt_types-0.2.0 → openadapt_types-0.3.0}/.github/workflows/test.yml +0 -0
- {openadapt_types-0.2.0 → openadapt_types-0.3.0}/.gitignore +0 -0
- {openadapt_types-0.2.0 → openadapt_types-0.3.0}/LICENSE +0 -0
- {openadapt_types-0.2.0 → openadapt_types-0.3.0}/README.md +0 -0
- {openadapt_types-0.2.0 → openadapt_types-0.3.0}/openadapt_types/_compat.py +0 -0
- {openadapt_types-0.2.0 → openadapt_types-0.3.0}/openadapt_types/action.py +0 -0
- {openadapt_types-0.2.0 → openadapt_types-0.3.0}/openadapt_types/computer_state.py +0 -0
- {openadapt_types-0.2.0 → openadapt_types-0.3.0}/openadapt_types/episode.py +0 -0
- {openadapt_types-0.2.0 → openadapt_types-0.3.0}/openadapt_types/failure.py +0 -0
- {openadapt_types-0.2.0 → openadapt_types-0.3.0}/openadapt_types/parsing.py +0 -0
- {openadapt_types-0.2.0 → openadapt_types-0.3.0}/openadapt_types/py.typed +0 -0
- {openadapt_types-0.2.0 → openadapt_types-0.3.0}/tests/__init__.py +0 -0
- {openadapt_types-0.2.0 → openadapt_types-0.3.0}/tests/test_compat.py +0 -0
- {openadapt_types-0.2.0 → openadapt_types-0.3.0}/tests/test_parsing.py +0 -0
- {openadapt_types-0.2.0 → openadapt_types-0.3.0}/tests/test_schemas.py +0 -0
- {openadapt_types-0.2.0 → openadapt_types-0.3.0}/uv.lock +0 -0
|
@@ -1,6 +1,26 @@
|
|
|
1
1
|
# CHANGELOG
|
|
2
2
|
|
|
3
3
|
|
|
4
|
+
## v0.3.0 (2026-07-13)
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
- Add canonical Benchmark* types (Task/Observation/Action/Agent)
|
|
9
|
+
([#5](https://github.com/OpenAdaptAI/openadapt-types/pull/5),
|
|
10
|
+
[`82ad485`](https://github.com/OpenAdaptAI/openadapt-types/commit/82ad48590e89e23c44cdf25ce636035195d6ea14))
|
|
11
|
+
|
|
12
|
+
Move the Benchmark* vocabulary into the canonical schema package so both openadapt-ml and
|
|
13
|
+
openadapt-evals can import it without depending on each other, breaking the historical ml<->evals
|
|
14
|
+
import cycle.
|
|
15
|
+
|
|
16
|
+
Definitions are dependency-free (dataclasses + abc) and faithfully match the previous
|
|
17
|
+
openadapt-evals definitions.
|
|
18
|
+
|
|
19
|
+
Claude-Session: https://claude.ai/code/session_01CKrVJJy5jWVCkXAqgUqtqZ
|
|
20
|
+
|
|
21
|
+
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
|
|
22
|
+
|
|
23
|
+
|
|
4
24
|
## v0.2.0 (2026-03-29)
|
|
5
25
|
|
|
6
26
|
### Continuous Integration
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: openadapt-types
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: Canonical Pydantic schemas for computer-use agents: ComputerState, Action, ActionResult, UINode
|
|
5
5
|
Project-URL: Homepage, https://github.com/OpenAdaptAI/openadapt-types
|
|
6
6
|
Project-URL: Repository, https://github.com/OpenAdaptAI/openadapt-types
|
|
@@ -26,6 +26,12 @@ from openadapt_types.action import (
|
|
|
26
26
|
ActionTarget,
|
|
27
27
|
ActionType,
|
|
28
28
|
)
|
|
29
|
+
from openadapt_types.benchmark import (
|
|
30
|
+
BenchmarkAction,
|
|
31
|
+
BenchmarkAgent,
|
|
32
|
+
BenchmarkObservation,
|
|
33
|
+
BenchmarkTask,
|
|
34
|
+
)
|
|
29
35
|
from openadapt_types.computer_state import (
|
|
30
36
|
BoundingBox,
|
|
31
37
|
ComputerState,
|
|
@@ -57,6 +63,11 @@ __all__ = [
|
|
|
57
63
|
"ActionResult",
|
|
58
64
|
"ActionTarget",
|
|
59
65
|
"ActionType",
|
|
66
|
+
# benchmark
|
|
67
|
+
"BenchmarkAction",
|
|
68
|
+
"BenchmarkAgent",
|
|
69
|
+
"BenchmarkObservation",
|
|
70
|
+
"BenchmarkTask",
|
|
60
71
|
# episode
|
|
61
72
|
"Episode",
|
|
62
73
|
"Step",
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
"""Canonical benchmark vocabulary shared across the OpenAdapt ecosystem.
|
|
2
|
+
|
|
3
|
+
This module hosts the ``Benchmark*`` types that describe the interaction
|
|
4
|
+
surface between GUI-agent benchmarks and the agents/environments that run
|
|
5
|
+
them. They live here (in the canonical schema package) so that both
|
|
6
|
+
``openadapt-ml`` (model library) and ``openadapt-evals`` (evaluation
|
|
7
|
+
harness) can import them without depending on each other -- breaking the
|
|
8
|
+
historical ``ml <-> evals`` import cycle.
|
|
9
|
+
|
|
10
|
+
The definitions are deliberately dependency-free (plain ``dataclasses`` and
|
|
11
|
+
an ``abc.ABC``) so importing them never pulls in heavy optional deps.
|
|
12
|
+
|
|
13
|
+
Example::
|
|
14
|
+
|
|
15
|
+
from openadapt_types import (
|
|
16
|
+
BenchmarkAgent,
|
|
17
|
+
BenchmarkAction,
|
|
18
|
+
BenchmarkObservation,
|
|
19
|
+
BenchmarkTask,
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
class MyAgent(BenchmarkAgent):
|
|
23
|
+
def act(self, observation, task, history=None):
|
|
24
|
+
return BenchmarkAction(type="click", x=0.5, y=0.5)
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
from __future__ import annotations
|
|
28
|
+
|
|
29
|
+
from abc import ABC, abstractmethod
|
|
30
|
+
from dataclasses import dataclass, field
|
|
31
|
+
from typing import Any
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@dataclass
|
|
35
|
+
class BenchmarkTask:
|
|
36
|
+
"""Canonical task representation.
|
|
37
|
+
|
|
38
|
+
Attributes:
|
|
39
|
+
task_id: Unique identifier for the task.
|
|
40
|
+
instruction: Natural language task instruction.
|
|
41
|
+
domain: Task domain ("web", "desktop", "mobile").
|
|
42
|
+
initial_state_ref: Reference to initial state (VM snapshot, URL, etc.).
|
|
43
|
+
time_limit_steps: Maximum steps allowed for the task.
|
|
44
|
+
raw_config: Original benchmark config (lossless preservation).
|
|
45
|
+
evaluation_spec: Benchmark-native evaluation specification.
|
|
46
|
+
"""
|
|
47
|
+
|
|
48
|
+
task_id: str
|
|
49
|
+
instruction: str
|
|
50
|
+
domain: str # "web", "desktop", "mobile"
|
|
51
|
+
|
|
52
|
+
# Environment setup
|
|
53
|
+
initial_state_ref: str | None = None # VM snapshot, storage_state, start URL
|
|
54
|
+
time_limit_steps: int | None = None
|
|
55
|
+
|
|
56
|
+
# Preserve original config losslessly
|
|
57
|
+
raw_config: dict[str, Any] = field(default_factory=dict)
|
|
58
|
+
|
|
59
|
+
# Evaluation spec (benchmark-native)
|
|
60
|
+
evaluation_spec: dict[str, Any] | None = None
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
@dataclass
|
|
64
|
+
class BenchmarkObservation:
|
|
65
|
+
"""Canonical observation at each step.
|
|
66
|
+
|
|
67
|
+
Supports multiple observation modalities:
|
|
68
|
+
- Visual: screenshots with viewport info
|
|
69
|
+
- Structured UI: accessibility tree (UIA/AXTree/DOM)
|
|
70
|
+
- Context: URL, window title, focused element
|
|
71
|
+
|
|
72
|
+
Attributes:
|
|
73
|
+
screenshot: PNG image bytes.
|
|
74
|
+
screenshot_path: Path to saved screenshot.
|
|
75
|
+
viewport: (width, height) of the viewport.
|
|
76
|
+
accessibility_tree: Platform-specific UI tree (UIA/AXTree/DOM).
|
|
77
|
+
dom_html: Raw HTML for web tasks.
|
|
78
|
+
url: Current URL for web tasks.
|
|
79
|
+
window_title: Active window title for desktop tasks.
|
|
80
|
+
focused_element: Currently focused UI element.
|
|
81
|
+
raw_observation: Original benchmark observation (lossless).
|
|
82
|
+
"""
|
|
83
|
+
|
|
84
|
+
# Visual
|
|
85
|
+
screenshot: bytes | None = None # PNG image bytes
|
|
86
|
+
screenshot_path: str | None = None
|
|
87
|
+
viewport: tuple[int, int] | None = None # (width, height)
|
|
88
|
+
|
|
89
|
+
# Structured UI (format varies by platform)
|
|
90
|
+
accessibility_tree: dict | None = None # UIA (Windows), AXTree (macOS), DOM (web)
|
|
91
|
+
dom_html: str | None = None # Raw HTML for web
|
|
92
|
+
|
|
93
|
+
# Context
|
|
94
|
+
url: str | None = None # For web tasks
|
|
95
|
+
window_title: str | None = None # For desktop tasks
|
|
96
|
+
app_name: str | None = None # Active application
|
|
97
|
+
focused_element: dict | None = None # {node_id, bbox, text}
|
|
98
|
+
|
|
99
|
+
# Raw benchmark-specific data (lossless)
|
|
100
|
+
raw_observation: dict[str, Any] | None = None
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
@dataclass
|
|
104
|
+
class BenchmarkAction:
|
|
105
|
+
"""Canonical action representation.
|
|
106
|
+
|
|
107
|
+
Supports multiple action types with both coordinate-based and element-based
|
|
108
|
+
grounding. The "grounding-first" approach stores both when available.
|
|
109
|
+
|
|
110
|
+
Attributes:
|
|
111
|
+
type: Action type ("click", "type", "scroll", "key", "drag", "answer", "done").
|
|
112
|
+
x: X coordinate (normalized [0,1] or pixels).
|
|
113
|
+
y: Y coordinate (normalized [0,1] or pixels).
|
|
114
|
+
target_node_id: Element ID from accessibility tree.
|
|
115
|
+
target_bbox: Element bounding box.
|
|
116
|
+
target_role: Element role (button, textfield, etc.).
|
|
117
|
+
target_name: Element accessible name.
|
|
118
|
+
text: Text to type (for "type" action).
|
|
119
|
+
key: Single key (for "key" action, e.g., "Enter", "Tab").
|
|
120
|
+
modifiers: Key modifiers (["ctrl", "shift", "alt"]).
|
|
121
|
+
scroll_direction: Scroll direction ("up", "down", "left", "right").
|
|
122
|
+
scroll_amount: Scroll amount (pixels or normalized).
|
|
123
|
+
end_x: Drag end X coordinate.
|
|
124
|
+
end_y: Drag end Y coordinate.
|
|
125
|
+
answer: Answer string (for benchmarks that score by answer).
|
|
126
|
+
raw_action: Original benchmark action (lossless).
|
|
127
|
+
"""
|
|
128
|
+
|
|
129
|
+
type: str # "click", "type", "scroll", "key", "drag", "answer", "done", "error"
|
|
130
|
+
|
|
131
|
+
# Pointer actions - coordinates
|
|
132
|
+
x: float | None = None # Normalized [0,1] or pixel
|
|
133
|
+
y: float | None = None
|
|
134
|
+
|
|
135
|
+
# Element grounding (when available)
|
|
136
|
+
target_node_id: str | None = None # DOM/AX/UIA node ID
|
|
137
|
+
target_bbox: tuple[float, float, float, float] | None = None
|
|
138
|
+
target_role: str | None = None # "button", "textfield", etc.
|
|
139
|
+
target_name: str | None = None # Accessible name
|
|
140
|
+
|
|
141
|
+
# Keyboard actions
|
|
142
|
+
text: str | None = None # For "type" action - text to type
|
|
143
|
+
key: str | None = None # For "key" action - single key
|
|
144
|
+
modifiers: list[str] | None = None # ["ctrl", "shift", "alt"]
|
|
145
|
+
|
|
146
|
+
# Scroll actions
|
|
147
|
+
scroll_direction: str | None = None # "up", "down", "left", "right"
|
|
148
|
+
scroll_amount: float | None = None # Pixels or normalized
|
|
149
|
+
|
|
150
|
+
# Drag actions
|
|
151
|
+
end_x: float | None = None
|
|
152
|
+
end_y: float | None = None
|
|
153
|
+
|
|
154
|
+
# Answer action (some benchmarks score by final answer)
|
|
155
|
+
answer: str | None = None
|
|
156
|
+
|
|
157
|
+
# Raw benchmark-specific format (lossless)
|
|
158
|
+
raw_action: dict[str, Any] | None = None
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
class BenchmarkAgent(ABC):
|
|
162
|
+
"""Abstract interface for agents evaluated on benchmarks.
|
|
163
|
+
|
|
164
|
+
Agents must implement the `act` method to receive observations
|
|
165
|
+
and return actions. The agent can maintain internal state across
|
|
166
|
+
steps within an episode.
|
|
167
|
+
"""
|
|
168
|
+
|
|
169
|
+
@abstractmethod
|
|
170
|
+
def act(
|
|
171
|
+
self,
|
|
172
|
+
observation: BenchmarkObservation,
|
|
173
|
+
task: BenchmarkTask,
|
|
174
|
+
history: list[tuple[BenchmarkObservation, BenchmarkAction]] | None = None,
|
|
175
|
+
) -> BenchmarkAction:
|
|
176
|
+
"""Given observation and task, return next action.
|
|
177
|
+
|
|
178
|
+
Args:
|
|
179
|
+
observation: Current observation from the environment.
|
|
180
|
+
task: Task being performed.
|
|
181
|
+
history: Optional list of previous (observation, action) pairs.
|
|
182
|
+
|
|
183
|
+
Returns:
|
|
184
|
+
Action to execute.
|
|
185
|
+
"""
|
|
186
|
+
...
|
|
187
|
+
|
|
188
|
+
def reset(self) -> None:
|
|
189
|
+
"""Reset agent state between episodes.
|
|
190
|
+
|
|
191
|
+
Called before starting a new task. Override to clear any
|
|
192
|
+
internal state.
|
|
193
|
+
"""
|
|
194
|
+
pass
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
__all__ = [
|
|
198
|
+
"BenchmarkAction",
|
|
199
|
+
"BenchmarkAgent",
|
|
200
|
+
"BenchmarkObservation",
|
|
201
|
+
"BenchmarkTask",
|
|
202
|
+
]
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "openadapt-types"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.3.0"
|
|
8
8
|
description = "Canonical Pydantic schemas for computer-use agents: ComputerState, Action, ActionResult, UINode"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "MIT"
|
|
@@ -40,6 +40,7 @@ dev = [
|
|
|
40
40
|
"pytest-cov",
|
|
41
41
|
]
|
|
42
42
|
|
|
43
|
+
|
|
43
44
|
[tool.hatch.build.targets.wheel]
|
|
44
45
|
packages = ["openadapt_types"]
|
|
45
46
|
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"""Tests for the canonical Benchmark* vocabulary."""
|
|
2
|
+
|
|
3
|
+
from openadapt_types import (
|
|
4
|
+
BenchmarkAction,
|
|
5
|
+
BenchmarkAgent,
|
|
6
|
+
BenchmarkObservation,
|
|
7
|
+
BenchmarkTask,
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def test_benchmark_task_defaults():
|
|
12
|
+
task = BenchmarkTask(task_id="t1", instruction="do it", domain="desktop")
|
|
13
|
+
assert task.task_id == "t1"
|
|
14
|
+
assert task.instruction == "do it"
|
|
15
|
+
assert task.domain == "desktop"
|
|
16
|
+
assert task.initial_state_ref is None
|
|
17
|
+
assert task.time_limit_steps is None
|
|
18
|
+
assert task.raw_config == {}
|
|
19
|
+
assert task.evaluation_spec is None
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def test_benchmark_task_raw_config_is_independent():
|
|
23
|
+
a = BenchmarkTask(task_id="a", instruction="i", domain="web")
|
|
24
|
+
b = BenchmarkTask(task_id="b", instruction="i", domain="web")
|
|
25
|
+
a.raw_config["k"] = "v"
|
|
26
|
+
assert b.raw_config == {}
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def test_benchmark_observation_defaults():
|
|
30
|
+
obs = BenchmarkObservation()
|
|
31
|
+
assert obs.screenshot is None
|
|
32
|
+
assert obs.viewport is None
|
|
33
|
+
assert obs.raw_observation is None
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def test_benchmark_observation_fields():
|
|
37
|
+
obs = BenchmarkObservation(
|
|
38
|
+
screenshot=b"png",
|
|
39
|
+
viewport=(1920, 1080),
|
|
40
|
+
url="https://example.com",
|
|
41
|
+
window_title="Notepad",
|
|
42
|
+
)
|
|
43
|
+
assert obs.screenshot == b"png"
|
|
44
|
+
assert obs.viewport == (1920, 1080)
|
|
45
|
+
assert obs.url == "https://example.com"
|
|
46
|
+
assert obs.window_title == "Notepad"
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def test_benchmark_action_click():
|
|
50
|
+
action = BenchmarkAction(type="click", x=0.5, y=0.25)
|
|
51
|
+
assert action.type == "click"
|
|
52
|
+
assert action.x == 0.5
|
|
53
|
+
assert action.y == 0.25
|
|
54
|
+
assert action.text is None
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def test_benchmark_action_type_and_key():
|
|
58
|
+
typing = BenchmarkAction(type="type", text="hello")
|
|
59
|
+
assert typing.text == "hello"
|
|
60
|
+
key = BenchmarkAction(type="key", key="Enter", modifiers=["ctrl"])
|
|
61
|
+
assert key.key == "Enter"
|
|
62
|
+
assert key.modifiers == ["ctrl"]
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def test_benchmark_agent_is_abstract():
|
|
66
|
+
# BenchmarkAgent is an ABC; instantiating directly must fail.
|
|
67
|
+
try:
|
|
68
|
+
BenchmarkAgent() # type: ignore[abstract]
|
|
69
|
+
except TypeError:
|
|
70
|
+
pass
|
|
71
|
+
else: # pragma: no cover
|
|
72
|
+
raise AssertionError("BenchmarkAgent should be abstract")
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def test_benchmark_agent_subclass_roundtrip():
|
|
76
|
+
class Echo(BenchmarkAgent):
|
|
77
|
+
def act(self, observation, task, history=None):
|
|
78
|
+
return BenchmarkAction(type="done")
|
|
79
|
+
|
|
80
|
+
agent = Echo()
|
|
81
|
+
obs = BenchmarkObservation()
|
|
82
|
+
task = BenchmarkTask(task_id="t", instruction="i", domain="desktop")
|
|
83
|
+
action = agent.act(obs, task)
|
|
84
|
+
assert action.type == "done"
|
|
85
|
+
# Default reset is a no-op and must not raise.
|
|
86
|
+
agent.reset()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|