inspect-ai 0.3.70__py3-none-any.whl → 0.3.72__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 (219) 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 +281 -153
  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/_common.py +117 -58
  157. inspect_ai/tool/_tools/_computer/_computer.py +80 -57
  158. inspect_ai/tool/_tools/_computer/_resources/image_home_dir/.config/Code/User/settings.json +7 -1
  159. inspect_ai/tool/_tools/_computer/_resources/image_home_dir/.config/xfce4/xfconf/xfce-perchannel-xml/xfwm4.xml +91 -0
  160. inspect_ai/tool/_tools/_computer/_resources/tool/.pylintrc +8 -0
  161. inspect_ai/tool/_tools/_computer/_resources/tool/.vscode/settings.json +12 -0
  162. inspect_ai/tool/_tools/_computer/_resources/tool/_args.py +78 -0
  163. inspect_ai/tool/_tools/_computer/_resources/tool/_constants.py +20 -0
  164. inspect_ai/tool/_tools/_computer/_resources/tool/_run.py +1 -1
  165. inspect_ai/tool/_tools/_computer/_resources/tool/_x11_client.py +175 -113
  166. inspect_ai/tool/_tools/_computer/_resources/tool/computer_tool.py +76 -20
  167. inspect_ai/tool/_tools/_computer/_resources/tool/pyproject.toml +65 -0
  168. inspect_ai/tool/_tools/_computer/test_args.py +151 -0
  169. inspect_ai/tool/_tools/_web_browser/_resources/.pylintrc +8 -0
  170. inspect_ai/tool/_tools/_web_browser/_resources/.vscode/launch.json +24 -0
  171. inspect_ai/tool/_tools/_web_browser/_resources/.vscode/settings.json +25 -0
  172. inspect_ai/tool/_tools/_web_browser/_resources/Dockerfile +5 -6
  173. inspect_ai/tool/_tools/_web_browser/_resources/README.md +10 -11
  174. inspect_ai/tool/_tools/_web_browser/_resources/accessibility_tree.py +71 -0
  175. inspect_ai/tool/_tools/_web_browser/_resources/accessibility_tree_node.py +323 -0
  176. inspect_ai/tool/_tools/_web_browser/_resources/cdp/__init__.py +5 -0
  177. inspect_ai/tool/_tools/_web_browser/_resources/cdp/a11y.py +279 -0
  178. inspect_ai/tool/_tools/_web_browser/_resources/cdp/dom.py +9 -0
  179. inspect_ai/tool/_tools/_web_browser/_resources/cdp/dom_snapshot.py +293 -0
  180. inspect_ai/tool/_tools/_web_browser/_resources/cdp/page.py +94 -0
  181. inspect_ai/tool/_tools/_web_browser/_resources/constants.py +2 -0
  182. inspect_ai/tool/_tools/_web_browser/_resources/images/usage_diagram.svg +2 -0
  183. inspect_ai/tool/_tools/_web_browser/_resources/playwright_browser.py +50 -0
  184. inspect_ai/tool/_tools/_web_browser/_resources/playwright_crawler.py +31 -359
  185. inspect_ai/tool/_tools/_web_browser/_resources/playwright_page_crawler.py +280 -0
  186. inspect_ai/tool/_tools/_web_browser/_resources/pyproject.toml +65 -0
  187. inspect_ai/tool/_tools/_web_browser/_resources/rectangle.py +64 -0
  188. inspect_ai/tool/_tools/_web_browser/_resources/rpc_client_helpers.py +146 -0
  189. inspect_ai/tool/_tools/_web_browser/_resources/scale_factor.py +64 -0
  190. inspect_ai/tool/_tools/_web_browser/_resources/test_accessibility_tree_node.py +180 -0
  191. inspect_ai/tool/_tools/_web_browser/_resources/test_playwright_crawler.py +15 -9
  192. inspect_ai/tool/_tools/_web_browser/_resources/test_rectangle.py +15 -0
  193. inspect_ai/tool/_tools/_web_browser/_resources/test_web_client.py +44 -0
  194. inspect_ai/tool/_tools/_web_browser/_resources/web_browser_rpc_types.py +39 -0
  195. inspect_ai/tool/_tools/_web_browser/_resources/web_client.py +198 -48
  196. inspect_ai/tool/_tools/_web_browser/_resources/web_client_new_session.py +26 -25
  197. inspect_ai/tool/_tools/_web_browser/_resources/web_server.py +178 -39
  198. inspect_ai/tool/_tools/_web_browser/_web_browser.py +38 -19
  199. inspect_ai/util/__init__.py +2 -1
  200. inspect_ai/util/_display.py +12 -0
  201. inspect_ai/util/_sandbox/events.py +55 -21
  202. inspect_ai/util/_sandbox/self_check.py +131 -43
  203. inspect_ai/util/_subtask.py +11 -0
  204. {inspect_ai-0.3.70.dist-info → inspect_ai-0.3.72.dist-info}/METADATA +1 -1
  205. {inspect_ai-0.3.70.dist-info → inspect_ai-0.3.72.dist-info}/RECORD +209 -186
  206. {inspect_ai-0.3.70.dist-info → inspect_ai-0.3.72.dist-info}/WHEEL +1 -1
  207. inspect_ai/_view/www/src/components/VirtualList.module.css +0 -19
  208. inspect_ai/_view/www/src/components/VirtualList.tsx +0 -292
  209. inspect_ai/tool/_tools/_computer/_computer_split.py +0 -198
  210. inspect_ai/tool/_tools/_web_browser/_resources/accessibility_node.py +0 -312
  211. inspect_ai/tool/_tools/_web_browser/_resources/dm_env_servicer.py +0 -275
  212. inspect_ai/tool/_tools/_web_browser/_resources/images/usage_diagram.png +0 -0
  213. inspect_ai/tool/_tools/_web_browser/_resources/test_accessibility_node.py +0 -176
  214. inspect_ai/tool/_tools/_web_browser/_resources/test_dm_env_servicer.py +0 -135
  215. inspect_ai/tool/_tools/_web_browser/_resources/test_web_environment.py +0 -71
  216. inspect_ai/tool/_tools/_web_browser/_resources/web_environment.py +0 -184
  217. {inspect_ai-0.3.70.dist-info → inspect_ai-0.3.72.dist-info}/LICENSE +0 -0
  218. {inspect_ai-0.3.70.dist-info → inspect_ai-0.3.72.dist-info}/entry_points.txt +0 -0
  219. {inspect_ai-0.3.70.dist-info → inspect_ai-0.3.72.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,279 @@
1
+ """
2
+ Types and pure functional helpers associated with Chrome DevTools Protocol's 'Accessibility' Domain
3
+
4
+ https://chromedevtools.github.io/devtools-protocol/tot/Accessibility/
5
+ """
6
+
7
+ from typing import Literal, NewType, Optional
8
+
9
+ from pydantic import BaseModel
10
+
11
+ from cdp.dom import DOMBackendNodeId
12
+
13
+ # brand these str's so that we don't confuse them with other str's
14
+ PageFrameId = NewType("PageFrameId", str)
15
+ AXNodeId = NewType("AXNodeId", str)
16
+
17
+
18
+ AXPropertyName = Literal[
19
+ "actions",
20
+ "busy",
21
+ "disabled",
22
+ "editable",
23
+ "focusable",
24
+ "focused",
25
+ "hidden",
26
+ "hiddenRoot",
27
+ "invalid",
28
+ "keyshortcuts",
29
+ "settable",
30
+ "roledescription",
31
+ "live",
32
+ "atomic",
33
+ "relevant",
34
+ "root",
35
+ "autocomplete",
36
+ "hasPopup",
37
+ "level",
38
+ "multiselectable",
39
+ "orientation",
40
+ "multiline",
41
+ "readonly",
42
+ "required",
43
+ "valuemin",
44
+ "valuemax",
45
+ "valuetext",
46
+ "checked",
47
+ "expanded",
48
+ "modal",
49
+ "pressed",
50
+ "selected",
51
+ "activedescendant",
52
+ "controls",
53
+ "describedby",
54
+ "details",
55
+ "errormessage",
56
+ "flowto",
57
+ "labelledby",
58
+ "owns",
59
+ "url",
60
+ ]
61
+
62
+
63
+ AXValueType = Literal[
64
+ "boolean",
65
+ "tristate",
66
+ "booleanOrUndefined",
67
+ "idref",
68
+ "idrefList",
69
+ "integer",
70
+ "node",
71
+ "nodeList",
72
+ "number",
73
+ "string",
74
+ "computedString",
75
+ "token",
76
+ "tokenList",
77
+ "domRelation",
78
+ "role",
79
+ "internalRole",
80
+ "valueUndefined",
81
+ ]
82
+
83
+
84
+ class AXRelatedNode(BaseModel, frozen=True):
85
+ backendDOMNodeId: DOMBackendNodeId
86
+ """The BackendNodeId of the related DOM node."""
87
+ idref: str | None = None
88
+ """The IDRef value provided, if any."""
89
+ text: str | None = None
90
+ """The text alternative of this node in the current context."""
91
+
92
+
93
+ AXValueSourceType = Literal[
94
+ "attribute", "implicit", "stylet", "contents", "placeholder", "relatedElement"
95
+ ]
96
+
97
+ AXValueNativeSourceType = Literal[
98
+ "description",
99
+ "figcaption",
100
+ "label",
101
+ "labelfor",
102
+ "labelwrapped",
103
+ "legend",
104
+ "rubyannotation",
105
+ "tablecaption",
106
+ "title",
107
+ "other",
108
+ ]
109
+
110
+
111
+ class AXValueSource(BaseModel, frozen=True):
112
+ """A single source for a computed AX property."""
113
+
114
+ type: AXValueSourceType
115
+ """What type of source this is."""
116
+ value: Optional["AXValue"] = None
117
+ """The value of this property source."""
118
+ attribute: str | None = None
119
+ """The name of the relevant attribute, if any."""
120
+ attributeValue: Optional["AXValue"] = None
121
+ """The value of the relevant attribute, if any."""
122
+ superseded: bool | None = None
123
+ """Whether this source is superseded by a higher priority source."""
124
+ nativeSource: AXValueNativeSourceType | None = None
125
+ """The native markup source for this value, e.g. a `<label>` element."""
126
+ nativeSourceValue: Optional["AXValue"] = None
127
+ """The value, such as a node or node list, of the native source."""
128
+ invalid: bool | None = None
129
+ """Whether the value for this property is invalid."""
130
+ invalidReason: str | None = None
131
+ """Reason for the value being invalid, if it is."""
132
+
133
+
134
+ class AXValue(BaseModel, frozen=True):
135
+ """A single computed AX property."""
136
+
137
+ type: AXValueType
138
+ """The type of this value."""
139
+ value: object | None = None
140
+ """The computed value of this property."""
141
+ relatedNodes: tuple[AXRelatedNode, ...] | None = None
142
+ """One or more related nodes, if applicable."""
143
+ sources: tuple[AXValueSource, ...] | None = None
144
+ """The sources which contributed to the computation of this property."""
145
+
146
+
147
+ class AXProperty(BaseModel, frozen=True):
148
+ name: AXPropertyName
149
+ """The name of this property."""
150
+ value: AXValue
151
+ """The value of this property."""
152
+
153
+
154
+ # AXNode's ignoredReasons is documented to be a list[AXProperty]. However, that seems to
155
+ # be erroneous since the namespace for ignored reason names is completely separate from property names.
156
+ class AXIgnoredReason(BaseModel, frozen=True):
157
+ # name: Literal[
158
+ # "uninteresting",
159
+ # "ariaHiddenSubtree",
160
+ # "ariaHiddenElement",
161
+ # "notRendered",
162
+ # "notVisible",
163
+ # "emptyAlt",
164
+ # ]
165
+ name: str # since this is all undocumented, it's best to be loose
166
+ """The name of this ignored reason."""
167
+ value: AXValue
168
+ """The value of this ignored reason."""
169
+
170
+
171
+ class AXNode(BaseModel, frozen=True):
172
+ """A node in the accessibility tree."""
173
+
174
+ nodeId: AXNodeId
175
+ """Unique identifier for this node."""
176
+ ignored: bool
177
+ """Wether this node is ignore3d for accessibility"""
178
+ ignoredReasons: tuple[AXIgnoredReason, ...] | None = None
179
+ """Collection of reasons why this node is hidden."""
180
+ role: AXValue | None = None
181
+ """This `Node`'s role, wether explicit or implicit."""
182
+ chromeRole: AXValue | None = None
183
+ """This `Node`'s Chrome raw role."""
184
+ name: AXValue | None = None
185
+ """The accessible name for this `Node`."""
186
+ description: AXValue | None = None
187
+ """The accessible description for this `Node`."""
188
+ value: AXValue | None = None
189
+ """The value for this `Node`."""
190
+ properties: tuple[AXProperty, ...] | None = None
191
+ """All other properties"""
192
+ parentId: AXNodeId | None = None
193
+ """ID for this node's parent."""
194
+ childIds: tuple[AXNodeId, ...] | None = None
195
+ """IDs for each of this node's child nodes."""
196
+ backendDOMNodeId: DOMBackendNodeId | None = None
197
+ """The backend ID for the associated DOM node, if any."""
198
+ frameId: PageFrameId | None = None
199
+ """The frame ID for the frame associated with this nodes document."""
200
+
201
+
202
+ class AXTree(BaseModel, frozen=True):
203
+ nodes: tuple[AXNode, ...]
204
+
205
+
206
+ # CDP defines `AXValue.value` as `Any`, so this is a coercion function
207
+ def string_from_ax_value(value: AXValue | None) -> str:
208
+ """
209
+ Coerces an AXValue to a string.
210
+
211
+ Args:
212
+ value (AXValue | None): The AXValue to be coerced.
213
+
214
+ Returns:
215
+ str: The string representation of the AXValue, or an empty string if the value is None or not a string.
216
+ """
217
+ return value.value if value and isinstance(value.value, str) else ""
218
+
219
+
220
+ def node_has_property(node: AXNode, property_name: AXPropertyName) -> bool:
221
+ """
222
+ Checks if an AXNode has a specific property.
223
+
224
+ Args:
225
+ node (AXNode): The AXNode to check.
226
+ property_name (AXPropertyName): The name of the property to look for.
227
+
228
+ Returns:
229
+ bool: True if the node has the property, False otherwise.
230
+ """
231
+ return node_property(node, property_name) is not None
232
+
233
+
234
+ def node_property(node: AXNode, property_name: AXPropertyName) -> AXProperty | None:
235
+ """
236
+ Retrieves a specific property from an AXNode.
237
+
238
+ Args:
239
+ node (AXNode): The AXNode to retrieve the property from.
240
+ property_name (AXPropertyName): The name of the property to retrieve.
241
+
242
+ Returns:
243
+ AXProperty | None: The property if found, otherwise None.
244
+ """
245
+ return next(
246
+ (prop for prop in node.properties or () if prop.name == property_name), None
247
+ )
248
+
249
+
250
+ def node_bool_property(node: AXNode, property_name: AXPropertyName) -> bool:
251
+ """
252
+ Retrieves a boolean property from an AXNode.
253
+
254
+ Args:
255
+ node (AXNode): The AXNode to retrieve the property from.
256
+ property_name (AXPropertyName): The name of the property to retrieve.
257
+
258
+ Returns:
259
+ bool: The boolean value of the property, or False if the property is not found or not a boolean.
260
+ """
261
+ return bool(prop.value) if (prop := node_property(node, property_name)) else False
262
+
263
+
264
+ def node_str_property(node: AXNode, property_name: AXPropertyName) -> str | None:
265
+ """
266
+ Retrieves a str property from an AXNode.
267
+
268
+ Args:
269
+ node (AXNode): The AXNode to retrieve the property from.
270
+ property_name (AXPropertyName): The name of the property to retrieve.
271
+
272
+ Returns:
273
+ str: The str value of the property, or None if the property is not found or not a str.
274
+ """
275
+ prop = node_property(node, property_name)
276
+ if prop is None:
277
+ return None
278
+ v = prop.value
279
+ return v.value if isinstance(v.value, str) else None
@@ -0,0 +1,9 @@
1
+ """
2
+ Types associated with Chrome DevTools Protocol's 'DOM' Domain
3
+
4
+ https://chromedevtools.github.io/devtools-protocol/tot/DOM/
5
+ """
6
+
7
+ from typing import NewType
8
+
9
+ DOMBackendNodeId = NewType("DOMBackendNodeId", int)
@@ -0,0 +1,293 @@
1
+ """
2
+ Types and pure functional helpers associated with Chrome DevTools Protocol's 'DOMSnapshot' Domain
3
+
4
+ https://chromedevtools.github.io/devtools-protocol/tot/DOMSnapshot/
5
+ """
6
+
7
+ from typing import Literal, NewType, TypedDict
8
+
9
+ from pydantic import BaseModel
10
+
11
+ from cdp.dom import DOMBackendNodeId
12
+
13
+ StringIndex = NewType("StringIndex", int)
14
+ Rectangle = tuple[float, ...]
15
+ ArrayOfStrings = tuple[StringIndex, ...]
16
+
17
+
18
+ class RareStringData(BaseModel, frozen=True):
19
+ """Data that is only present on rare nodes."""
20
+
21
+ index: tuple[int, ...]
22
+ value: tuple[StringIndex, ...]
23
+
24
+
25
+ class RareIntegerData(BaseModel, frozen=True):
26
+ index: tuple[int, ...]
27
+ value: tuple[int, ...]
28
+
29
+
30
+ class RareBooleanData(BaseModel, frozen=True):
31
+ index: tuple[int, ...]
32
+
33
+
34
+ class NodeTreeSnapshot(BaseModel, frozen=True):
35
+ """Table containing nodes."""
36
+
37
+ parentIndex: tuple[int, ...] | None = None
38
+ """Parent node index."""
39
+ nodeType: tuple[int, ...] | None = None
40
+ """`Node`'s nodeType"""
41
+ shadowRootType: RareStringData | None = None
42
+ """Type of the shadow root the `Node` is in. String values are equal to the `ShadowRootType` enum."""
43
+ nodeName: tuple[StringIndex, ...] | None = None
44
+ """`Node`'s nodeName."""
45
+ nodeValue: tuple[StringIndex, ...] | None = None
46
+ """`Node`'s nodeValue."""
47
+ backendNodeId: tuple[DOMBackendNodeId, ...] | None = None
48
+ """`Node`'s id, corresponds to DOM.Node.backendNodeId."""
49
+ attributes: tuple[ArrayOfStrings, ...] | None = None
50
+ # attributes: list[int] | None = None
51
+ """Attributes of an `Element` node. Flatten name, value pairs."""
52
+ textValue: RareStringData | None = None
53
+ """Only set for textarea elements, contains the text value."""
54
+ inputValue: RareStringData | None = None
55
+ """Only set for input elements, contains the input's associated text value."""
56
+ inputChecked: RareBooleanData | None = None
57
+ """Only set for radio and checkbox input elements, indicates if the element has been checked"""
58
+ optionSelected: RareBooleanData | None = None
59
+ """Only set for option elements, indicates if the element has been selected"""
60
+ contentDocumentIndex: RareIntegerData | None = None
61
+ """The index of the document in the list of the snapshot documents."""
62
+ pseudoType: RareStringData | None = None
63
+ """Type of a pseudo element node."""
64
+ pseudoIdentifier: RareStringData | None = None
65
+ """Pseudo element identifier for this node. Only present if there is a valid pseudoType."""
66
+ isClickable: RareBooleanData | None = None
67
+ """Whether this DOM node responds to mouse clicks. This includes nodes that have had click event listeners attached via JavaScript as well as anchor tags that naturally navigate when clicked."""
68
+ currentSourceURL: RareStringData | None = None
69
+ """The selected url for nodes with a srcset attribute."""
70
+ originURL: RareStringData | None = None
71
+ """The url of the script (if any) that generates this node."""
72
+
73
+
74
+ class LayoutTreeSnapshot(BaseModel, frozen=True):
75
+ """Table of details of an element in the DOM tree with a `LayoutObject`."""
76
+
77
+ nodeIndex: tuple[int, ...]
78
+ """Index of the corresponding node in the `NodeTreeSnapshot` array returned by `captureSnapshot`."""
79
+ styles: tuple[ArrayOfStrings, ...]
80
+ """Array of indexes specifying computed style strings, filtered according to the computedStyles parameter passed to captureSnapshot."""
81
+ bounds: tuple[Rectangle, ...]
82
+ """The absolute position bounding box."""
83
+ text: tuple[StringIndex, ...]
84
+ """Contents of the `LayoutText`, if any."""
85
+ stackingContexts: RareBooleanData
86
+ """Stacking context information."""
87
+ paintOrders: tuple[int, ...] | None = None
88
+ """Global paint order index, which is determined by the stacking order of the nodes. Nodes that are painted together will have the same index. Only provided if `includePaintOrder` in `captureSnapshot` was true."""
89
+ offsetRects: tuple[Rectangle, ...] | None = None
90
+ """The offset rect of nodes. Only available when `includeDOMRects` is set to true"""
91
+ scrollRects: tuple[Rectangle, ...] | None = None
92
+ """The scroll rect of nodes. Only available when `includeDOMRects` is set to true"""
93
+ clientRects: tuple[Rectangle, ...] | None = None
94
+ """The client rect of nodes. Only available when `includeDOMRects` is set to true"""
95
+ blendedBackgroundColors: tuple[StringIndex, ...] | None = None
96
+ """The list of background colors that are blended with colors of overlapping elements. Experimental"""
97
+ textColorOpacities: tuple[float, ...] | None = None
98
+ """The list of computed text opacities. Experimental"""
99
+
100
+
101
+ class TextBoxSnapshot(BaseModel, frozen=True):
102
+ """Table of details of the post layout rendered text positions. The exact layout should not be regarded as stable and may change between versions."""
103
+
104
+ layoutIndex: tuple[int, ...]
105
+ """Index of the layout tree node that owns this box collection."""
106
+ bounds: tuple[Rectangle, ...]
107
+ """The absolute position bounding box."""
108
+ start: tuple[int, ...]
109
+ """The starting index in characters, for this post layout textbox substring. Characters that would be represented as a surrogate pair in UTF-16 have length 2."""
110
+ length: tuple[int, ...]
111
+ """The number of characters in this post layout textbox substring. Characters that would be represented as a surrogate pair in UTF-16 have length 2."""
112
+
113
+
114
+ class DocumentSnapshot(BaseModel, frozen=True):
115
+ documentURL: StringIndex
116
+ """Document URL that `Document` or `FrameOwner` node points to."""
117
+ title: StringIndex
118
+ """Document title."""
119
+ baseURL: StringIndex
120
+ """Base URL that `Document` or `FrameOwner` node uses for URL completion."""
121
+ contentLanguage: StringIndex
122
+ """Contains the document's content language."""
123
+ encodingName: StringIndex
124
+ """Contains the document's character set encoding."""
125
+ publicId: StringIndex
126
+ """`DocumentType` node's publicId."""
127
+ systemId: StringIndex
128
+ """`DocumentType` node's systemId."""
129
+ frameId: StringIndex
130
+ """Frame ID for frame owner elements and also for the document node."""
131
+ nodes: NodeTreeSnapshot
132
+ """A table with dom nodes."""
133
+ layout: LayoutTreeSnapshot
134
+ """The nodes in the layout tree."""
135
+ textBoxes: TextBoxSnapshot
136
+ """The post-layout inline text nodes."""
137
+ scrollOffsetX: float | None = None
138
+ """Horizontal scroll offset."""
139
+ scrollOffsetY: float | None = None
140
+ """Vertical scroll offset."""
141
+ contentWidth: float | None = None
142
+ """Document content width."""
143
+ contentHeight: float | None = None
144
+ """Document content height."""
145
+
146
+
147
+ class DOMSnapshot(BaseModel, frozen=True):
148
+ documents: tuple[DocumentSnapshot, ...]
149
+ """The nodes in the DOM tree. The DOMNode at index 0 corresponds to the root document."""
150
+ strings: tuple[str, ...]
151
+ """Shared string table that all string properties refer to with indexes."""
152
+
153
+
154
+ NodeIndex = NewType("NodeIndex", int)
155
+ LayoutIndex = NewType("LayoutIndex", int)
156
+
157
+
158
+ class DOMSnapshotContext(TypedDict):
159
+ dom_snapshot: DOMSnapshot
160
+ node_id_to_node_index: dict[DOMBackendNodeId, NodeIndex]
161
+ # Below are dicts to help with non-1:1 index mappings like layouts and RareStringData's
162
+ node_index_to_layout_index: dict[NodeIndex, LayoutIndex]
163
+ node_index_to_current_src_url_index: dict[NodeIndex, StringIndex]
164
+ node_index_to_text_value_index: dict[NodeIndex, StringIndex]
165
+
166
+
167
+ def create_snapshot_context(dom_snapshot: DOMSnapshot) -> DOMSnapshotContext:
168
+ """
169
+ Creates a DOMSnapshotContext that can be used to efficiently extract data from a DOMSnapshot.
170
+
171
+ Given the fact that a DOMSnapshot uses long flat correlated lists (see below), this context
172
+ enables efficient lookups that avoid full list scans.
173
+
174
+ NOTE: Currently, this context and associated code assumes document[0]
175
+
176
+ The DOMSnapshot domain represents the DOM tree as as set of objects each containing
177
+ parallel flat arrays — NodeTreeSnapshot, LayoutTreeSnapshot, etc.
178
+
179
+ Each node in the DOM tree is represented in the NodeTreeSnapshot object by corresponding
180
+ entries at the same index across multiple arrays (parentIndex, nodeType, backendNodeId, etc.):
181
+
182
+ NodeTreeSnapshot:
183
+ │ 0 │ 1 | 2 | 3 | 4
184
+ ──────────────┼────────┼────────┼────────┼────────┼────────
185
+ actual tag │ <html> │ <body> │ <div> │ <p> │ <span>
186
+ parentIndex │ -1 │ 0 │ 1 │ 2 │ 3
187
+ backendNodeId │ 666 │ 745 │ 103 │ 421 │ 7
188
+ attributes │ ... │ ... │ ... │ ... │ ...
189
+ nodeValue │ ... │ ... │ ... │ ... │ ...
190
+ etc │ ... │ ... │ ... │ ... │ ...
191
+
192
+
193
+ Because layout is independent of node ordering, entries in the LayoutTreeSnapshot object maintain
194
+ a reference back to their corresponding node through the nodeIndex field along with layout specific
195
+ properties (bounds, paintOrders, etc).
196
+
197
+ LayoutTreeSnapshot:
198
+ │ 0 │ 1 │ 2 │ 3 │ 4
199
+ ──────────────┼────────┼────────┼────────┼────────┼────────
200
+ bounds │ ... │ ... │ ... │ ... │ ...
201
+ etc │ ... │ ... │ ... │ ... │ ...
202
+ nodeIndex │ 3 │ 0 │ 4 │ 2 │ 1
203
+ ──────────────┼────────┼────────┼────────┼────────┼────────
204
+ │ ↓ │ ↓ │ ↓ │ ↓ │ ↓
205
+ backendNodeId │ 421 │ 666 │ 7 │ 103 │ 745
206
+ actual tag │ <p> │ <html> │ <span> │ <div> │ <body>
207
+ """
208
+ document = dom_snapshot.documents[0]
209
+ return {
210
+ "dom_snapshot": dom_snapshot,
211
+ "node_id_to_node_index": {
212
+ node_id: index
213
+ for index, node_id in enumerate(document.nodes.backendNodeId or [])
214
+ },
215
+ "node_index_to_layout_index": {
216
+ node_index: layout_index
217
+ for layout_index, node_index in enumerate(document.layout.nodeIndex)
218
+ },
219
+ "node_index_to_current_src_url_index": _dict_for_rare_string_data(
220
+ document.nodes.currentSourceURL
221
+ ),
222
+ "node_index_to_text_value_index": _dict_for_rare_string_data(
223
+ document.nodes.textValue
224
+ ),
225
+ }
226
+
227
+
228
+ def index_for_node_id(
229
+ node_id: DOMBackendNodeId, context: DOMSnapshotContext
230
+ ) -> NodeIndex | None:
231
+ return context["node_id_to_node_index"].get(node_id, None)
232
+
233
+
234
+ def bounds_for_node_index(
235
+ node_index: NodeIndex, context: DOMSnapshotContext
236
+ ) -> Rectangle | None:
237
+ return (
238
+ context["dom_snapshot"].documents[0].layout.bounds[layout_index]
239
+ if (layout_index := _layout_index_for_node_index(node_index, context))
240
+ is not None
241
+ else None
242
+ )
243
+
244
+
245
+ def current_url_src_for_node_index(
246
+ node_index: NodeIndex, context: DOMSnapshotContext
247
+ ) -> str | None:
248
+ return _rare_string_for_node_index(
249
+ node_index, "node_index_to_current_src_url_index", context
250
+ )
251
+
252
+
253
+ def text_value_for_node_index(
254
+ node_index: NodeIndex, context: DOMSnapshotContext
255
+ ) -> str | None:
256
+ return _rare_string_for_node_index(
257
+ node_index, "node_index_to_text_value_index", context
258
+ )
259
+
260
+
261
+ def _layout_index_for_node_index(
262
+ node_index: NodeIndex, context: DOMSnapshotContext
263
+ ) -> LayoutIndex | None:
264
+ """Returns the index of the layout entry, if any, corresponding to the given node id."""
265
+ return context["node_index_to_layout_index"].get(node_index, None)
266
+
267
+
268
+ def _rare_string_for_node_index(
269
+ node_index: NodeIndex,
270
+ dict_name: Literal[
271
+ "node_index_to_text_value_index", "node_index_to_current_src_url_index"
272
+ ],
273
+ context: DOMSnapshotContext,
274
+ ) -> str | None:
275
+ return (
276
+ _string_for_string_index(text_value_index, context)
277
+ if (text_value_index := context[dict_name].get(node_index, None))
278
+ else None
279
+ )
280
+
281
+
282
+ def _string_for_string_index(index: StringIndex, context: DOMSnapshotContext) -> str:
283
+ return context["dom_snapshot"].strings[index]
284
+
285
+
286
+ def _dict_for_rare_string_data(
287
+ rare_string_data: RareStringData,
288
+ ) -> dict[NodeIndex, StringIndex]:
289
+ """Creates a dictionary from a RareStringData object enabling lookups given a node index."""
290
+ return {
291
+ index: value
292
+ for index, value in zip(rare_string_data.index, rare_string_data.value)
293
+ }
@@ -0,0 +1,94 @@
1
+ """
2
+ Types associated with Chrome DevTools Protocol's 'Page' Domain
3
+
4
+ https://chromedevtools.github.io/devtools-protocol/tot/Page/
5
+ """
6
+
7
+ from typing import Literal, NewType
8
+
9
+ from pydantic import BaseModel
10
+
11
+ FrameId = NewType("FrameId", str)
12
+
13
+
14
+ class SecurityOriginDetails(BaseModel, frozen=True):
15
+ """Additional information about the frame document's security origin."""
16
+
17
+ isLocalhost: bool
18
+ """Indicates whether the frame document's security origin is one of the local hostnames (e.g. "localhost") or IP addresses (IPv4 127.0.0.0/8 or IPv6 ::1)."""
19
+
20
+
21
+ AdFrameType = Literal["none", "child", "root"]
22
+
23
+ AdFrameExplanation = Literal["ParentIsAd", "CreatedByAdScript", "MatchedBlockingRule"]
24
+
25
+
26
+ class AdFrameStatus(BaseModel, frozen=True):
27
+ """Indicates whether a frame has been identified as an ad and why."""
28
+
29
+ adFrameType: AdFrameType
30
+ explanations: tuple[AdFrameExplanation, ...] | None = None
31
+
32
+
33
+ SecureContextType = Literal[
34
+ "Secure", "SecureLocalhost", "InsecureScheme", "InsecureAncestor"
35
+ ]
36
+
37
+
38
+ CrossOriginIsolatedContextType = Literal[
39
+ "Isolated", "NotIsolated", "NotIsolatedFeatureDisabled"
40
+ ]
41
+
42
+
43
+ GatedAPIFeatures = Literal[
44
+ "SharedArrayBuffers",
45
+ "SharedArrayBuffersTransferAllowed",
46
+ "PerformanceMeasureMemory",
47
+ "PerformanceProfile",
48
+ ]
49
+
50
+
51
+ class Frame(BaseModel, frozen=True):
52
+ """Information about the Frame on the page."""
53
+
54
+ id: FrameId
55
+ """Frame unique identifier."""
56
+ parentId: FrameId | None = None
57
+ """Parent frame identifier."""
58
+ loaderId: object # Network.LoaderId
59
+ """Identifier of the loader associated with this frame."""
60
+ name: str | None = None
61
+ """Frame's name as specified in the tag."""
62
+ url: str
63
+ """Frame document's URL without fragment."""
64
+ urlFragment: str | None = None
65
+ """Frame document's URL fragment including the '#'."""
66
+ domainAndRegistry: str
67
+ """Frame document's registered domain, taking the public suffixes list into account. Extracted from the Frame's url. Example URLs: http://www.google.com/file.html -> "google.com" http://a.b.co.uk/file.html -> "b.co.uk"""
68
+ securityOrigin: str
69
+ """Frame document's security origin."""
70
+ securityOriginDetails: SecurityOriginDetails | None = None
71
+ """Additional details about the frame document's security origin."""
72
+ mimeType: str
73
+ """Frame document's mimeType as determined by the browser."""
74
+ unreachableUrl: str | None = None
75
+ """If the frame failed to load, this contains the URL that could not be loaded. Note that unlike url above, this URL may contain a fragment."""
76
+ adFrameStatus: AdFrameStatus | None = None
77
+ """Indicates whether this frame was tagged as an ad and why."""
78
+ secureContextType: SecureContextType
79
+ """Indicates whether the main document is a secure context and explains why that is the case."""
80
+ crossOriginIsolatedContextType: CrossOriginIsolatedContextType
81
+ """Indicates whether this is a cross origin isolated context."""
82
+ gatedAPIFeatures: tuple[GatedAPIFeatures, ...]
83
+ """Indicated which gated APIs / features are available."""
84
+
85
+
86
+ class FrameTree(BaseModel, frozen=True):
87
+ frame: Frame
88
+ """Frame information for this tree item."""
89
+ childFrames: tuple["FrameTree", ...] | None = None
90
+ """Child frames."""
91
+
92
+
93
+ class FrameTrees(BaseModel, frozen=True):
94
+ frameTree: FrameTree
@@ -0,0 +1,2 @@
1
+ SERVER_PORT = 5555
2
+ DEFAULT_SESSION_NAME = "WebBrowser"