inspect-ai 0.3.70__py3-none-any.whl → 0.3.71__py3-none-any.whl

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.
Files changed (208) hide show
  1. inspect_ai/_cli/eval.py +14 -8
  2. inspect_ai/_display/core/display.py +2 -0
  3. inspect_ai/_display/core/footer.py +13 -3
  4. inspect_ai/_display/plain/display.py +6 -2
  5. inspect_ai/_display/rich/display.py +19 -6
  6. inspect_ai/_display/textual/app.py +6 -1
  7. inspect_ai/_display/textual/display.py +4 -0
  8. inspect_ai/_display/textual/widgets/transcript.py +10 -6
  9. inspect_ai/_eval/task/run.py +5 -8
  10. inspect_ai/_util/content.py +20 -1
  11. inspect_ai/_util/transcript.py +10 -4
  12. inspect_ai/_util/working.py +4 -0
  13. inspect_ai/_view/www/App.css +6 -0
  14. inspect_ai/_view/www/dist/assets/index.css +115 -87
  15. inspect_ai/_view/www/dist/assets/index.js +5324 -2276
  16. inspect_ai/_view/www/eslint.config.mjs +24 -1
  17. inspect_ai/_view/www/log-schema.json +283 -20
  18. inspect_ai/_view/www/package.json +8 -3
  19. inspect_ai/_view/www/src/App.tsx +2 -2
  20. inspect_ai/_view/www/src/components/AnsiDisplay.tsx +4 -3
  21. inspect_ai/_view/www/src/components/Card.tsx +9 -8
  22. inspect_ai/_view/www/src/components/DownloadButton.tsx +2 -1
  23. inspect_ai/_view/www/src/components/EmptyPanel.tsx +2 -2
  24. inspect_ai/_view/www/src/components/ErrorPanel.tsx +4 -3
  25. inspect_ai/_view/www/src/components/ExpandablePanel.tsx +13 -5
  26. inspect_ai/_view/www/src/components/FindBand.tsx +3 -3
  27. inspect_ai/_view/www/src/components/HumanBaselineView.tsx +3 -3
  28. inspect_ai/_view/www/src/components/LabeledValue.tsx +5 -4
  29. inspect_ai/_view/www/src/components/LargeModal.tsx +18 -13
  30. inspect_ai/_view/www/src/components/{LightboxCarousel.css → LightboxCarousel.module.css} +22 -18
  31. inspect_ai/_view/www/src/components/LightboxCarousel.tsx +36 -27
  32. inspect_ai/_view/www/src/components/MessageBand.tsx +2 -1
  33. inspect_ai/_view/www/src/components/NavPills.tsx +9 -8
  34. inspect_ai/_view/www/src/components/ProgressBar.tsx +2 -1
  35. inspect_ai/_view/www/src/components/TabSet.tsx +21 -15
  36. inspect_ai/_view/www/src/index.tsx +2 -2
  37. inspect_ai/_view/www/src/metadata/MetaDataGrid.tsx +11 -9
  38. inspect_ai/_view/www/src/metadata/MetaDataView.tsx +3 -2
  39. inspect_ai/_view/www/src/metadata/MetadataGrid.module.css +1 -0
  40. inspect_ai/_view/www/src/metadata/RenderedContent.tsx +16 -0
  41. inspect_ai/_view/www/src/plan/DatasetDetailView.tsx +3 -2
  42. inspect_ai/_view/www/src/plan/DetailStep.tsx +2 -1
  43. inspect_ai/_view/www/src/plan/PlanCard.tsx +2 -5
  44. inspect_ai/_view/www/src/plan/PlanDetailView.tsx +6 -9
  45. inspect_ai/_view/www/src/plan/ScorerDetailView.tsx +2 -1
  46. inspect_ai/_view/www/src/plan/SolverDetailView.tsx +3 -3
  47. inspect_ai/_view/www/src/samples/InlineSampleDisplay.tsx +2 -2
  48. inspect_ai/_view/www/src/samples/SampleDialog.tsx +3 -3
  49. inspect_ai/_view/www/src/samples/SampleDisplay.tsx +2 -2
  50. inspect_ai/_view/www/src/samples/SampleSummaryView.tsx +2 -2
  51. inspect_ai/_view/www/src/samples/SamplesTools.tsx +2 -1
  52. inspect_ai/_view/www/src/samples/chat/ChatMessage.tsx +3 -19
  53. inspect_ai/_view/www/src/samples/chat/ChatMessageRenderer.tsx +2 -1
  54. inspect_ai/_view/www/src/samples/chat/ChatMessageRow.tsx +2 -1
  55. inspect_ai/_view/www/src/samples/chat/ChatView.tsx +2 -1
  56. inspect_ai/_view/www/src/samples/chat/ChatViewVirtualList.tsx +22 -7
  57. inspect_ai/_view/www/src/samples/chat/MessageContent.tsx +35 -6
  58. inspect_ai/_view/www/src/samples/chat/MessageContents.tsx +2 -2
  59. inspect_ai/_view/www/src/samples/chat/messages.ts +15 -2
  60. inspect_ai/_view/www/src/samples/chat/tools/ToolCallView.tsx +13 -4
  61. inspect_ai/_view/www/src/samples/chat/tools/ToolInput.module.css +2 -2
  62. inspect_ai/_view/www/src/samples/chat/tools/ToolInput.tsx +18 -19
  63. inspect_ai/_view/www/src/samples/chat/tools/ToolOutput.module.css +1 -1
  64. inspect_ai/_view/www/src/samples/chat/tools/ToolOutput.tsx +4 -3
  65. inspect_ai/_view/www/src/samples/chat/tools/ToolTitle.tsx +2 -2
  66. inspect_ai/_view/www/src/samples/error/FlatSampleErrorView.tsx +2 -3
  67. inspect_ai/_view/www/src/samples/error/SampleErrorView.tsx +3 -2
  68. inspect_ai/_view/www/src/samples/list/SampleFooter.tsx +2 -1
  69. inspect_ai/_view/www/src/samples/list/SampleHeader.tsx +2 -1
  70. inspect_ai/_view/www/src/samples/list/SampleList.tsx +57 -45
  71. inspect_ai/_view/www/src/samples/list/SampleRow.tsx +2 -1
  72. inspect_ai/_view/www/src/samples/list/SampleSeparator.tsx +2 -1
  73. inspect_ai/_view/www/src/samples/sample-tools/EpochFilter.tsx +2 -2
  74. inspect_ai/_view/www/src/samples/sample-tools/SelectScorer.tsx +4 -3
  75. inspect_ai/_view/www/src/samples/sample-tools/SortFilter.tsx +2 -5
  76. inspect_ai/_view/www/src/samples/sample-tools/sample-filter/SampleFilter.tsx +2 -2
  77. inspect_ai/_view/www/src/samples/scores/SampleScoreView.tsx +2 -1
  78. inspect_ai/_view/www/src/samples/scores/SampleScores.tsx +2 -2
  79. inspect_ai/_view/www/src/samples/transcript/ApprovalEventView.tsx +2 -1
  80. inspect_ai/_view/www/src/samples/transcript/ErrorEventView.tsx +2 -1
  81. inspect_ai/_view/www/src/samples/transcript/InfoEventView.tsx +2 -1
  82. inspect_ai/_view/www/src/samples/transcript/InputEventView.tsx +2 -1
  83. inspect_ai/_view/www/src/samples/transcript/LoggerEventView.module.css +4 -0
  84. inspect_ai/_view/www/src/samples/transcript/LoggerEventView.tsx +12 -2
  85. inspect_ai/_view/www/src/samples/transcript/ModelEventView.module.css +1 -1
  86. inspect_ai/_view/www/src/samples/transcript/ModelEventView.tsx +25 -28
  87. inspect_ai/_view/www/src/samples/transcript/SampleInitEventView.tsx +2 -1
  88. inspect_ai/_view/www/src/samples/transcript/SampleLimitEventView.tsx +5 -4
  89. inspect_ai/_view/www/src/samples/transcript/SampleTranscript.tsx +2 -2
  90. inspect_ai/_view/www/src/samples/transcript/SandboxEventView.tsx +8 -7
  91. inspect_ai/_view/www/src/samples/transcript/ScoreEventView.tsx +2 -2
  92. inspect_ai/_view/www/src/samples/transcript/StepEventView.tsx +3 -3
  93. inspect_ai/_view/www/src/samples/transcript/SubtaskEventView.tsx +18 -14
  94. inspect_ai/_view/www/src/samples/transcript/ToolEventView.tsx +5 -5
  95. inspect_ai/_view/www/src/samples/transcript/TranscriptView.tsx +34 -15
  96. inspect_ai/_view/www/src/samples/transcript/event/EventNav.tsx +2 -1
  97. inspect_ai/_view/www/src/samples/transcript/event/EventNavs.tsx +2 -1
  98. inspect_ai/_view/www/src/samples/transcript/event/EventRow.tsx +3 -2
  99. inspect_ai/_view/www/src/samples/transcript/event/EventSection.tsx +2 -2
  100. inspect_ai/_view/www/src/samples/transcript/event/EventTimingPanel.module.css +28 -0
  101. inspect_ai/_view/www/src/samples/transcript/event/EventTimingPanel.tsx +115 -0
  102. inspect_ai/_view/www/src/samples/transcript/event/utils.ts +29 -0
  103. inspect_ai/_view/www/src/samples/transcript/state/StateDiffView.tsx +2 -1
  104. inspect_ai/_view/www/src/samples/transcript/state/StateEventRenderers.tsx +3 -3
  105. inspect_ai/_view/www/src/samples/transcript/state/StateEventView.tsx +11 -8
  106. inspect_ai/_view/www/src/types/log.d.ts +129 -34
  107. inspect_ai/_view/www/src/usage/ModelTokenTable.tsx +6 -10
  108. inspect_ai/_view/www/src/usage/ModelUsagePanel.module.css +4 -0
  109. inspect_ai/_view/www/src/usage/ModelUsagePanel.tsx +32 -9
  110. inspect_ai/_view/www/src/usage/TokenTable.tsx +4 -6
  111. inspect_ai/_view/www/src/usage/UsageCard.tsx +2 -1
  112. inspect_ai/_view/www/src/utils/format.ts +1 -1
  113. inspect_ai/_view/www/src/utils/json.ts +24 -0
  114. inspect_ai/_view/www/src/workspace/WorkSpace.tsx +6 -5
  115. inspect_ai/_view/www/src/workspace/WorkSpaceView.tsx +9 -2
  116. inspect_ai/_view/www/src/workspace/error/TaskErrorPanel.tsx +2 -1
  117. inspect_ai/_view/www/src/workspace/navbar/Navbar.tsx +2 -1
  118. inspect_ai/_view/www/src/workspace/navbar/PrimaryBar.tsx +3 -3
  119. inspect_ai/_view/www/src/workspace/navbar/ResultsPanel.tsx +4 -3
  120. inspect_ai/_view/www/src/workspace/navbar/SecondaryBar.tsx +5 -4
  121. inspect_ai/_view/www/src/workspace/navbar/StatusPanel.tsx +5 -8
  122. inspect_ai/_view/www/src/workspace/sidebar/EvalStatus.tsx +5 -4
  123. inspect_ai/_view/www/src/workspace/sidebar/LogDirectoryTitleView.tsx +2 -1
  124. inspect_ai/_view/www/src/workspace/sidebar/Sidebar.tsx +2 -1
  125. inspect_ai/_view/www/src/workspace/sidebar/SidebarLogEntry.tsx +2 -2
  126. inspect_ai/_view/www/src/workspace/sidebar/SidebarScoreView.tsx +2 -1
  127. inspect_ai/_view/www/src/workspace/sidebar/SidebarScoresView.tsx +2 -2
  128. inspect_ai/_view/www/src/workspace/tabs/InfoTab.tsx +2 -2
  129. inspect_ai/_view/www/src/workspace/tabs/JsonTab.tsx +2 -5
  130. inspect_ai/_view/www/src/workspace/tabs/SamplesTab.tsx +12 -11
  131. inspect_ai/_view/www/yarn.lock +241 -5
  132. inspect_ai/log/_condense.py +3 -0
  133. inspect_ai/log/_recorders/eval.py +6 -1
  134. inspect_ai/log/_transcript.py +58 -1
  135. inspect_ai/model/__init__.py +2 -0
  136. inspect_ai/model/_call_tools.py +7 -0
  137. inspect_ai/model/_chat_message.py +22 -7
  138. inspect_ai/model/_conversation.py +10 -8
  139. inspect_ai/model/_generate_config.py +25 -4
  140. inspect_ai/model/_model.py +133 -57
  141. inspect_ai/model/_model_output.py +3 -0
  142. inspect_ai/model/_openai.py +106 -40
  143. inspect_ai/model/_providers/anthropic.py +134 -26
  144. inspect_ai/model/_providers/google.py +27 -8
  145. inspect_ai/model/_providers/groq.py +9 -4
  146. inspect_ai/model/_providers/openai.py +57 -4
  147. inspect_ai/model/_providers/openai_o1.py +10 -0
  148. inspect_ai/model/_providers/providers.py +1 -1
  149. inspect_ai/model/_reasoning.py +15 -2
  150. inspect_ai/scorer/_model.py +23 -19
  151. inspect_ai/solver/_human_agent/agent.py +14 -10
  152. inspect_ai/solver/_human_agent/commands/__init__.py +7 -3
  153. inspect_ai/solver/_human_agent/commands/submit.py +76 -30
  154. inspect_ai/tool/__init__.py +2 -0
  155. inspect_ai/tool/_tool.py +3 -1
  156. inspect_ai/tool/_tools/_computer/_resources/tool/_run.py +1 -1
  157. inspect_ai/tool/_tools/_web_browser/_resources/.pylintrc +8 -0
  158. inspect_ai/tool/_tools/_web_browser/_resources/.vscode/launch.json +24 -0
  159. inspect_ai/tool/_tools/_web_browser/_resources/.vscode/settings.json +25 -0
  160. inspect_ai/tool/_tools/_web_browser/_resources/Dockerfile +5 -6
  161. inspect_ai/tool/_tools/_web_browser/_resources/README.md +10 -11
  162. inspect_ai/tool/_tools/_web_browser/_resources/accessibility_tree.py +71 -0
  163. inspect_ai/tool/_tools/_web_browser/_resources/accessibility_tree_node.py +323 -0
  164. inspect_ai/tool/_tools/_web_browser/_resources/cdp/__init__.py +5 -0
  165. inspect_ai/tool/_tools/_web_browser/_resources/cdp/a11y.py +279 -0
  166. inspect_ai/tool/_tools/_web_browser/_resources/cdp/dom.py +9 -0
  167. inspect_ai/tool/_tools/_web_browser/_resources/cdp/dom_snapshot.py +293 -0
  168. inspect_ai/tool/_tools/_web_browser/_resources/cdp/page.py +94 -0
  169. inspect_ai/tool/_tools/_web_browser/_resources/constants.py +2 -0
  170. inspect_ai/tool/_tools/_web_browser/_resources/images/usage_diagram.svg +2 -0
  171. inspect_ai/tool/_tools/_web_browser/_resources/playwright_browser.py +50 -0
  172. inspect_ai/tool/_tools/_web_browser/_resources/playwright_crawler.py +31 -359
  173. inspect_ai/tool/_tools/_web_browser/_resources/playwright_page_crawler.py +280 -0
  174. inspect_ai/tool/_tools/_web_browser/_resources/pyproject.toml +65 -0
  175. inspect_ai/tool/_tools/_web_browser/_resources/rectangle.py +64 -0
  176. inspect_ai/tool/_tools/_web_browser/_resources/rpc_client_helpers.py +146 -0
  177. inspect_ai/tool/_tools/_web_browser/_resources/scale_factor.py +64 -0
  178. inspect_ai/tool/_tools/_web_browser/_resources/test_accessibility_tree_node.py +180 -0
  179. inspect_ai/tool/_tools/_web_browser/_resources/test_playwright_crawler.py +15 -9
  180. inspect_ai/tool/_tools/_web_browser/_resources/test_rectangle.py +15 -0
  181. inspect_ai/tool/_tools/_web_browser/_resources/test_web_client.py +44 -0
  182. inspect_ai/tool/_tools/_web_browser/_resources/web_browser_rpc_types.py +39 -0
  183. inspect_ai/tool/_tools/_web_browser/_resources/web_client.py +198 -48
  184. inspect_ai/tool/_tools/_web_browser/_resources/web_client_new_session.py +26 -25
  185. inspect_ai/tool/_tools/_web_browser/_resources/web_server.py +178 -39
  186. inspect_ai/tool/_tools/_web_browser/_web_browser.py +38 -19
  187. inspect_ai/util/__init__.py +2 -1
  188. inspect_ai/util/_display.py +12 -0
  189. inspect_ai/util/_sandbox/events.py +55 -21
  190. inspect_ai/util/_sandbox/self_check.py +131 -43
  191. inspect_ai/util/_subtask.py +11 -0
  192. {inspect_ai-0.3.70.dist-info → inspect_ai-0.3.71.dist-info}/METADATA +1 -1
  193. {inspect_ai-0.3.70.dist-info → inspect_ai-0.3.71.dist-info}/RECORD +197 -182
  194. {inspect_ai-0.3.70.dist-info → inspect_ai-0.3.71.dist-info}/WHEEL +1 -1
  195. inspect_ai/_view/www/node_modules/flatted/python/flatted.py +0 -149
  196. inspect_ai/_view/www/node_modules/flatted/python/test.py +0 -63
  197. inspect_ai/_view/www/src/components/VirtualList.module.css +0 -19
  198. inspect_ai/_view/www/src/components/VirtualList.tsx +0 -292
  199. inspect_ai/tool/_tools/_web_browser/_resources/accessibility_node.py +0 -312
  200. inspect_ai/tool/_tools/_web_browser/_resources/dm_env_servicer.py +0 -275
  201. inspect_ai/tool/_tools/_web_browser/_resources/images/usage_diagram.png +0 -0
  202. inspect_ai/tool/_tools/_web_browser/_resources/test_accessibility_node.py +0 -176
  203. inspect_ai/tool/_tools/_web_browser/_resources/test_dm_env_servicer.py +0 -135
  204. inspect_ai/tool/_tools/_web_browser/_resources/test_web_environment.py +0 -71
  205. inspect_ai/tool/_tools/_web_browser/_resources/web_environment.py +0 -184
  206. {inspect_ai-0.3.70.dist-info → inspect_ai-0.3.71.dist-info}/LICENSE +0 -0
  207. {inspect_ai-0.3.70.dist-info → inspect_ai-0.3.71.dist-info}/entry_points.txt +0 -0
  208. {inspect_ai-0.3.70.dist-info → inspect_ai-0.3.71.dist-info}/top_level.txt +0 -0
@@ -1,312 +0,0 @@
1
- from __future__ import annotations
2
-
3
- from typing import Any
4
-
5
- # Properities to ignore when printing out the accessibility tree.
6
- _IGNORED_ACTREE_PROPERTIES = (
7
- "focusable",
8
- "readonly",
9
- "level",
10
- "settable",
11
- "multiline",
12
- "invalid",
13
- )
14
-
15
- _IGNORED_AT_ROLES = (
16
- "generic",
17
- "list",
18
- "strong",
19
- "paragraph",
20
- "banner",
21
- "navigation",
22
- "Section",
23
- "LabelText",
24
- "Legend",
25
- "listitem",
26
- )
27
-
28
- # Typically we use the bounding rect provided by the DOM in order to track
29
- # if an element is visible or not. However, sometimes it can be useful to
30
- # instead take the union of the elements bounds and all its children's bounds.
31
- # This process is a little slower, and is probably not needed, but can be
32
- # enabled using this flag.
33
- USE_UNION_BOUNDS = False
34
-
35
- # The maximum length of the description 'tag' for each element.
36
- # Tags longer than this will be truncated with an '...' appended.
37
- MAX_NODE_TEXT_LENGTH = 120
38
-
39
- # Used for debugging, include the elements bounding rect during output.
40
- _INCLUDE_BOUNDS_IN_OUTPUT = False
41
-
42
-
43
- class NodeBounds:
44
- """Class to hold bounding rect for notes."""
45
-
46
- def __init__(self, x: int, y: int, width: int, height: int):
47
- self.x = int(x)
48
- self.y = int(y)
49
- self.width = int(width)
50
- self.height = int(height)
51
-
52
- @classmethod
53
- def from_lrtb(cls, left: int, right: int, top: int, bottom: int) -> NodeBounds:
54
- return cls(left, top, right - left, bottom - top)
55
-
56
- @property
57
- def left(self) -> int:
58
- return self.x
59
-
60
- @property
61
- def top(self) -> int:
62
- return self.y
63
-
64
- @property
65
- def right(self) -> int:
66
- return self.x + self.width
67
-
68
- @property
69
- def bottom(self) -> int:
70
- return self.y + self.height
71
-
72
- @property
73
- def center_x(self) -> int:
74
- return self.x + self.width // 2
75
-
76
- @property
77
- def center_y(self) -> int:
78
- return self.y + self.height // 2
79
-
80
- @property
81
- def area(self) -> int:
82
- return self.width * self.height
83
-
84
- def __str__(self):
85
- return f"({self.left}, {self.top}, {self.width}, {self.height})"
86
-
87
- def union(self, other: NodeBounds) -> NodeBounds:
88
- """Return the (convex) union of two bounds."""
89
- # Special cases when one or the other is an empty bound.
90
- if self.area == 0:
91
- return other
92
- if other.area == 0:
93
- return self
94
-
95
- return self.from_lrtb(
96
- min(self.left, other.left),
97
- max(self.right, other.right),
98
- min(self.top, other.top),
99
- max(self.bottom, other.bottom),
100
- )
101
-
102
- def is_inside(self, x: int, y: int) -> bool:
103
- """Returns if given point is inside the bounds or not."""
104
- return x >= self.left and y >= self.top and x < self.right and y < self.bottom
105
-
106
- def overlaps(self, other: NodeBounds) -> bool:
107
- """Returns if the two bounds intersect."""
108
- return (
109
- other.left < self.right
110
- and other.right > self.left
111
- and other.top < self.bottom
112
- and other.bottom > self.top
113
- )
114
-
115
-
116
- class AccessibilityNode:
117
- """A class for an accessibility node.
118
-
119
- Accessibility properties are specified here:
120
- https://chromedevtools.github.io/devtools-protocol/tot/Accessibility/#type-AXNode
121
- """
122
-
123
- def __init__(self, node: dict[str, Any], parent=None, bounds=None):
124
- self._node = node
125
- self._parent: AccessibilityNode | None = parent
126
- self.bounds = bounds or NodeBounds(0, 0, 0, 0)
127
-
128
- def __getitem__(self, key: str) -> Any:
129
- """Access the underlying node fields."""
130
- return self._node.get(key)
131
-
132
- def __setitem__(self, key: str, value: Any):
133
- """Set the underlying node fields."""
134
- self._node[key] = value
135
-
136
- def __str__(self):
137
- if _INCLUDE_BOUNDS_IN_OUTPUT:
138
- bounds_string = " " + str(
139
- self.get_union_bounds() if USE_UNION_BOUNDS else self.bounds
140
- )
141
- else:
142
- bounds_string = ""
143
-
144
- node_input = self.current_input
145
- if node_input:
146
- input_string = f' Current input: "{node_input}"'
147
- else:
148
- input_string = ""
149
-
150
- # Without bounds we can't click on the element, and therefore the ID
151
- # can not be used. Still useful to show these elements though (e.g.)
152
- # to let user know about options.
153
- id_string = "*" if self.bounds.area == 0 else self.node_id
154
-
155
- url_string = ""
156
- if self.role == "image" and self._node.get("src", ""):
157
- url_string = " " + self._node["src"]
158
-
159
- return (
160
- f'[{id_string}] {self.role} "{self.short_name}"'
161
- + url_string
162
- + input_string
163
- + self.property_string()
164
- + bounds_string
165
- )
166
-
167
- @property
168
- def node_id(self) -> str:
169
- return self._node["nodeId"]
170
-
171
- @property
172
- def value(self) -> str:
173
- value_field = self._node.get("value", "{}")
174
- if isinstance(value_field, dict):
175
- return value_field.get("value", "")
176
- else:
177
- return ""
178
-
179
- @property
180
- def parent(self) -> Any | None:
181
- """Returns the first visible parent."""
182
- parent = self._parent
183
- while parent and parent.is_ignored:
184
- parent = parent._parent
185
- return parent
186
-
187
- @property
188
- def role(self) -> str:
189
- if "role" in self._node:
190
- return self._node["role"].get("value", "")
191
- else:
192
- return ""
193
-
194
- @property
195
- def name(self) -> str:
196
- if "name" in self._node:
197
- return self._node["name"].get("value", "")
198
- else:
199
- return ""
200
-
201
- @property
202
- def short_name(self) -> str:
203
- short_name = self.name[:MAX_NODE_TEXT_LENGTH]
204
- if short_name != self.name:
205
- short_name = short_name[: MAX_NODE_TEXT_LENGTH - 3] + "..."
206
- return short_name
207
-
208
- @property
209
- def is_ignored(self) -> bool:
210
- return (
211
- self._node["ignored"]
212
- or self.role in _IGNORED_AT_ROLES
213
- or not self.is_visible
214
- or (not self.name and self.role != "textbox")
215
- or (self.parent and self.parent.name == self.name)
216
- )
217
-
218
- @property
219
- def children(self) -> list[Any]:
220
- return self._node.get("children", [])
221
-
222
- @property
223
- def is_visible(self) -> bool:
224
- return self._node.get("is_visible", True)
225
-
226
- def is_selected(self) -> bool:
227
- return self._node.get("selected", False)
228
-
229
- @property
230
- def is_editable(self) -> bool:
231
- for node_property in self.properties:
232
- if node_property["name"] == "editable":
233
- return True
234
- return False
235
-
236
- @property
237
- def is_expanded(self) -> bool:
238
- for node_property in self.properties:
239
- if node_property["name"] == "expanded":
240
- return bool(node_property["value"].get("value"))
241
- return False
242
-
243
- @property
244
- def properties(self) -> list[Any]:
245
- return self._node.get("properties", [])
246
-
247
- @property
248
- def dom_id(self) -> str:
249
- """Returns the backend DOM id associated with this node."""
250
- return self._node.get("backendDOMNodeId", -1)
251
-
252
- @property
253
- def current_input(self) -> str:
254
- # For some reason text edit boxes get 'input' but comboboxs get 'values'
255
- if self.role == "combobox":
256
- value = self.value
257
- else:
258
- value = self.get_property("input")
259
- return value
260
-
261
- def get_union_bounds(self) -> list[int]:
262
- """Returns the union of the bounds for this element all children."""
263
- bounds = self.bounds
264
- for child in self.children:
265
- child_union_bounds = child.get_union_bounds()
266
- bounds = bounds.union(child_union_bounds)
267
- return bounds
268
-
269
- def link_children(self, node_lookup: dict[str, Any]) -> None:
270
- """Creates links to children nodes.
271
-
272
- Nodes are referenced by id, but we want to add references to the
273
- AccessibilityNode. We also want add a reference to the parent node.
274
-
275
- To perform this task we need access to the complete list of nodes, and
276
- therefore it must be done on a second pass after initializing the nodes.
277
-
278
- Args:
279
- node_lookup: A dictionary mapping from node_id to node instances.
280
- """
281
- self._node["children"] = []
282
- for idx in self._node.get("childIds", []):
283
- self._node["children"].append(node_lookup[idx])
284
- node_lookup[idx]._parent = self
285
-
286
- def get_property(self, property_name: str) -> Any:
287
- """Query the property value of the underlying accessibility tree node."""
288
- return self._node.get(property_name)
289
-
290
- def to_string(self, indent: int = 0, include_children: bool = True) -> str:
291
- """Returns the string representation of the node."""
292
- result = ""
293
- if not self.is_ignored:
294
- result = " " * indent + str(self) + "\n"
295
- indent += 1
296
- if include_children:
297
- children_strings = [child.to_string(indent) for child in self.children]
298
- result = result + "".join([s for s in children_strings if s])
299
- return result
300
-
301
- def property_string(self) -> str:
302
- properties = []
303
- for node_property in self.properties:
304
- try:
305
- if node_property["name"] in _IGNORED_ACTREE_PROPERTIES:
306
- continue
307
- properties.append(
308
- f"{node_property['name']}: {node_property['value'].get('value')}"
309
- )
310
- except KeyError:
311
- pass
312
- return " [" + ", ".join(properties) + "]" if properties else ""
@@ -1,275 +0,0 @@
1
- """Environment service that allows clients to run shell commands in steps."""
2
-
3
- import threading
4
- from typing import Any, Iterable, Type
5
-
6
- import dm_env
7
- import grpc
8
- import playwright_crawler
9
- from dm_env import specs
10
- from dm_env_rpc.v1 import (
11
- dm_env_rpc_pb2,
12
- dm_env_rpc_pb2_grpc,
13
- dm_env_utils,
14
- spec_manager,
15
- )
16
- from google.rpc import code_pb2, status_pb2
17
-
18
- _DEFAULT_WORLD_NAME = "WebBrowser"
19
-
20
-
21
- class EnvironmentSpec:
22
- """Specifications for a dm_environment.
23
-
24
- This class holds action and observation specs, as well as the required
25
- managers to pack actions and observations.
26
- """
27
-
28
- def __init__(self, env: dm_env.Environment):
29
- convert = dm_env_utils.dm_env_spec_to_tensor_spec
30
-
31
- # We support either a single spec, of flat dictionary of specs.
32
- # In the dictionary case we need to map names to unique IDs.
33
- env_obs_spec: dict[str, Any] = env.observation_spec()
34
- if isinstance(env_obs_spec, specs.Array):
35
- self.observation_spec = {1: convert(env_obs_spec)}
36
- else:
37
- self.observation_spec = {}
38
- for i, obs_spec in enumerate(env_obs_spec.values()):
39
- self.observation_spec[i + 1] = convert(obs_spec)
40
-
41
- assert isinstance(env.action_spec(), specs.Array), (
42
- "Only a single action type is supported."
43
- )
44
- self.action_spec = {1: convert(env.action_spec())}
45
-
46
- self.observation_manager = spec_manager.SpecManager(self.observation_spec)
47
- self.action_manager = spec_manager.SpecManager(self.action_spec)
48
-
49
-
50
- class EnvironmentService(dm_env_rpc_pb2_grpc.EnvironmentServicer):
51
- """Runs the environment as a gRPC EnvironmentServicer."""
52
-
53
- def __init__(self, env_type: Type[dm_env.Environment]) -> None:
54
- """Initializes the environment.
55
-
56
- Args:
57
- env_type: A dm_env class to serve.
58
- """
59
- self._env_type = env_type
60
- self._envs: dict[str, dm_env.Environment] = {}
61
- self._specs: dict[str, EnvironmentSpec] = {}
62
- self._joined_worlds: set[str] = set()
63
- self._browser: playwright_crawler.PlaywrightBrowser = None
64
- self._lock = threading.Lock()
65
- self._num_worlds = 0
66
-
67
- def Process(
68
- self,
69
- request_iterator: Iterable[dm_env_rpc_pb2.EnvironmentRequest],
70
- context: grpc.ServicerContext,
71
- ):
72
- """Processes incoming EnvironmentRequests.
73
-
74
- For each EnvironmentRequest the internal message is extracted and handled.
75
- The response for that message is then placed in a EnvironmentResponse which
76
- is returned to the client.
77
-
78
- An error status will be returned if an unknown message type is received or
79
- if the message is invalid for the current world state.
80
-
81
-
82
- Args:
83
- request_iterator: Message iterator provided by gRPC.
84
- context: Context provided by gRPC.
85
-
86
- Yields:
87
- EnvironmentResponse: Response for each incoming EnvironmentRequest.
88
- """
89
- cur_world = None
90
- for request in request_iterator:
91
- environment_response = dm_env_rpc_pb2.EnvironmentResponse()
92
- try:
93
- message_type = request.WhichOneof("payload")
94
- internal_request = getattr(request, message_type)
95
- match type(internal_request):
96
- case dm_env_rpc_pb2.CreateWorldRequest:
97
- response = self._handle_create_world_request(internal_request)
98
- case dm_env_rpc_pb2.JoinWorldRequest:
99
- response, cur_world = self._handle_join_world_request(
100
- internal_request
101
- )
102
- case dm_env_rpc_pb2.LeaveWorldRequest:
103
- response = self._handle_leave_world_request(
104
- internal_request, cur_world
105
- )
106
- case dm_env_rpc_pb2.DestroyWorldRequest:
107
- response = self._handle_destroy_world_request(internal_request)
108
- case dm_env_rpc_pb2.StepRequest:
109
- response = self._handle_step_request(
110
- internal_request, cur_world
111
- )
112
- case _:
113
- raise ValueError(
114
- f"Unsupported request type: {type(internal_request)}"
115
- )
116
- getattr(environment_response, message_type).CopyFrom(response)
117
- except Exception as e: # pylint: disable=broad-except
118
- environment_response.error.CopyFrom(
119
- status_pb2.Status(code=code_pb2.INTERNAL, message=str(e))
120
- )
121
- yield environment_response
122
-
123
- def _validate_settings(self, settings, valid_settings):
124
- """Validate the provided settings with list of valid setting keys."""
125
- unrecognized_settings = [
126
- setting for setting in settings if setting not in valid_settings
127
- ]
128
-
129
- if unrecognized_settings:
130
- raise ValueError(
131
- "Unrecognized settings provided! Invalid settings:"
132
- f" {unrecognized_settings}"
133
- )
134
-
135
- def _add_spec_to_response(
136
- self, world_name: str, response: dm_env_rpc_pb2.EnvironmentResponse
137
- ):
138
- """Modifies given respose to include action/observation specifications."""
139
- if not self._specs.get(world_name):
140
- raise ValueError(f"Not found a spec for {world_name} world")
141
-
142
- spec = self._specs[world_name]
143
- for uid, action in spec.action_spec.items():
144
- response.specs.actions[uid].CopyFrom(action)
145
- for uid, observation in spec.observation_spec.items():
146
- response.specs.observations[uid].CopyFrom(observation)
147
-
148
- def _handle_create_world_request(
149
- self, request: dm_env_rpc_pb2.CreateWorldRequest
150
- ) -> dm_env_rpc_pb2.CreateWorldResponse:
151
- """Handles create_world requests."""
152
- self._validate_settings(request.settings, [])
153
- del request
154
- world_name = _DEFAULT_WORLD_NAME
155
- with self._lock:
156
- if self._browser is None:
157
- self._browser = playwright_crawler.PlaywrightBrowser()
158
- else:
159
- world_name += f"_{self._num_worlds}"
160
- self._num_worlds += 1
161
-
162
- new_context = self._browser.get_new_context()
163
- env = self._env_type(new_context)
164
- spec = EnvironmentSpec(env)
165
- self._envs[world_name] = env
166
- self._specs[world_name] = spec
167
-
168
- return dm_env_rpc_pb2.CreateWorldResponse(world_name=world_name)
169
-
170
- def _handle_join_world_request(
171
- self, request: dm_env_rpc_pb2.JoinWorldRequest
172
- ) -> tuple[dm_env_rpc_pb2.JoinWorldResponse, str]:
173
- """Handles join_world requests."""
174
- self._validate_settings(request.settings, [])
175
- response = dm_env_rpc_pb2.JoinWorldResponse()
176
- world_name = request.world_name
177
- with self._lock:
178
- if not self._envs.get(world_name):
179
- raise ValueError(f"Joining with the wrong world_name {world_name}")
180
- if world_name in self._joined_worlds:
181
- raise ValueError(f"Only one client can joint the world {world_name}")
182
- self._joined_worlds.add(world_name)
183
- self._add_spec_to_response(world_name, response)
184
-
185
- del request
186
- return (response, world_name)
187
-
188
- def _handle_leave_world_request(
189
- self, request: dm_env_rpc_pb2.LeaveWorldRequest, world_name: str
190
- ) -> dm_env_rpc_pb2.LeaveWorldResponse:
191
- """Handles leave_world requests."""
192
- del request
193
- if world_name in self._joined_worlds:
194
- self._joined_worlds.remove(world_name)
195
- response = dm_env_rpc_pb2.LeaveWorldResponse()
196
- return response
197
-
198
- def _handle_destroy_world_request(
199
- self, request: dm_env_rpc_pb2.DestroyWorldRequest
200
- ) -> dm_env_rpc_pb2.DestroyWorldResponse:
201
- """Handles destroy_world requests."""
202
- world_name = request.world_name
203
- del request
204
- with self._lock:
205
- if not self._envs.get(world_name):
206
- raise ValueError("Can not destroy uncreated environment.")
207
- if world_name in self._joined_worlds:
208
- raise ValueError("Can not destroy environment with a joined agent.")
209
- env = self._envs.pop(world_name)
210
- env.close()
211
- env = None
212
- self._specs.pop(world_name, None)
213
-
214
- if not self._envs:
215
- self._browser.close()
216
- self._browser = None
217
- response = dm_env_rpc_pb2.DestroyWorldResponse()
218
- return response
219
-
220
- def _handle_step_request(
221
- self, request: dm_env_rpc_pb2.StepRequest, cur_world: str
222
- ) -> dm_env_rpc_pb2.StepResponse:
223
- """Handles step requests.
224
-
225
- Args:
226
- request: The request, which should contain a 'command' entry.
227
- cur_world: The name of the world in which we're making a step.
228
-
229
- Returns:
230
- Response including requested observations.
231
-
232
- Raises:
233
- KeyError: If the requested observation is not in the list of available
234
- observations.
235
- """
236
- with self._lock:
237
- assert cur_world in self._envs, (
238
- "Current world does not have an assosiated environment"
239
- )
240
- assert cur_world in self._joined_worlds, (
241
- "Please join world before calling step."
242
- )
243
- env = self._envs[cur_world]
244
- spec = self._specs[cur_world]
245
-
246
- action = spec.action_manager.unpack(request.actions)
247
-
248
- if "command" in action:
249
- command = action["command"]
250
- else:
251
- # For some reason dm_env calls step without actions after a reset.
252
- command = ""
253
-
254
- timestep: dm_env.TimeStep = env.step(command)
255
-
256
- packed_observations = spec.observation_manager.pack(timestep.observation)
257
-
258
- match timestep.step_type:
259
- case dm_env.StepType.MID:
260
- step_state = dm_env_rpc_pb2.RUNNING
261
- case dm_env.StepType.LAST:
262
- step_state = dm_env_rpc_pb2.TERMINATED
263
- case _:
264
- raise ValueError(f"Unsupported step type {timestep.step_type}.")
265
-
266
- response = dm_env_rpc_pb2.StepResponse(state=step_state)
267
- for requested_observation in request.requested_observations:
268
- if requested_observation not in packed_observations:
269
- name = spec.observation_manager.uid_to_name(requested_observation)
270
- raise KeyError(f"Requested observation not found: {name}")
271
- response.observations[requested_observation].CopyFrom(
272
- packed_observations[requested_observation]
273
- )
274
-
275
- return response