wandb 0.17.0rc1__py3-none-any.whl → 0.17.1__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
Files changed (173) hide show
  1. wandb/__init__.py +1 -2
  2. wandb/apis/importers/internals/internal.py +0 -1
  3. wandb/apis/importers/wandb.py +12 -7
  4. wandb/apis/internal.py +0 -3
  5. wandb/apis/public/api.py +213 -79
  6. wandb/apis/public/artifacts.py +335 -100
  7. wandb/apis/public/files.py +9 -9
  8. wandb/apis/public/jobs.py +16 -4
  9. wandb/apis/public/projects.py +26 -28
  10. wandb/apis/public/query_generator.py +1 -1
  11. wandb/apis/public/runs.py +163 -65
  12. wandb/apis/public/sweeps.py +2 -2
  13. wandb/apis/reports/__init__.py +1 -7
  14. wandb/apis/reports/v1/__init__.py +5 -27
  15. wandb/apis/reports/v2/__init__.py +7 -19
  16. wandb/apis/workspaces/__init__.py +8 -0
  17. wandb/beta/workflows.py +8 -3
  18. wandb/cli/cli.py +131 -59
  19. wandb/data_types.py +6 -3
  20. wandb/docker/__init__.py +2 -2
  21. wandb/env.py +3 -3
  22. wandb/errors/term.py +10 -2
  23. wandb/filesync/step_checksum.py +1 -4
  24. wandb/filesync/step_prepare.py +4 -24
  25. wandb/filesync/step_upload.py +5 -107
  26. wandb/filesync/upload_job.py +0 -76
  27. wandb/integration/gym/__init__.py +35 -15
  28. wandb/integration/huggingface/resolver.py +2 -2
  29. wandb/integration/keras/callbacks/metrics_logger.py +1 -1
  30. wandb/integration/keras/keras.py +1 -1
  31. wandb/integration/openai/fine_tuning.py +21 -3
  32. wandb/integration/prodigy/prodigy.py +1 -1
  33. wandb/jupyter.py +16 -17
  34. wandb/old/summary.py +1 -1
  35. wandb/plot/confusion_matrix.py +1 -1
  36. wandb/plot/pr_curve.py +2 -1
  37. wandb/plot/roc_curve.py +2 -1
  38. wandb/{plots → plot}/utils.py +13 -25
  39. wandb/proto/v3/wandb_internal_pb2.py +54 -54
  40. wandb/proto/v3/wandb_settings_pb2.py +2 -2
  41. wandb/proto/v3/wandb_telemetry_pb2.py +10 -10
  42. wandb/proto/v4/wandb_internal_pb2.py +54 -54
  43. wandb/proto/v4/wandb_settings_pb2.py +2 -2
  44. wandb/proto/v4/wandb_telemetry_pb2.py +10 -10
  45. wandb/proto/v5/wandb_base_pb2.py +30 -0
  46. wandb/proto/v5/wandb_internal_pb2.py +355 -0
  47. wandb/proto/v5/wandb_server_pb2.py +63 -0
  48. wandb/proto/v5/wandb_settings_pb2.py +45 -0
  49. wandb/proto/v5/wandb_telemetry_pb2.py +41 -0
  50. wandb/proto/wandb_base_pb2.py +2 -0
  51. wandb/proto/wandb_deprecated.py +9 -1
  52. wandb/proto/wandb_generate_deprecated.py +34 -0
  53. wandb/proto/{wandb_internal_codegen.py → wandb_generate_proto.py} +1 -35
  54. wandb/proto/wandb_internal_pb2.py +2 -0
  55. wandb/proto/wandb_server_pb2.py +2 -0
  56. wandb/proto/wandb_settings_pb2.py +2 -0
  57. wandb/proto/wandb_telemetry_pb2.py +2 -0
  58. wandb/sdk/artifacts/artifact.py +68 -22
  59. wandb/sdk/artifacts/artifact_manifest.py +1 -1
  60. wandb/sdk/artifacts/artifact_manifest_entry.py +6 -3
  61. wandb/sdk/artifacts/artifact_manifests/artifact_manifest_v1.py +1 -1
  62. wandb/sdk/artifacts/artifact_saver.py +1 -10
  63. wandb/sdk/artifacts/storage_handlers/local_file_handler.py +6 -2
  64. wandb/sdk/artifacts/storage_handlers/multi_handler.py +1 -1
  65. wandb/sdk/artifacts/storage_handlers/tracking_handler.py +6 -4
  66. wandb/sdk/artifacts/storage_policies/wandb_storage_policy.py +2 -42
  67. wandb/sdk/artifacts/storage_policy.py +1 -12
  68. wandb/sdk/data_types/_dtypes.py +8 -8
  69. wandb/sdk/data_types/image.py +2 -2
  70. wandb/sdk/data_types/video.py +5 -3
  71. wandb/sdk/integration_utils/data_logging.py +5 -5
  72. wandb/sdk/interface/interface.py +14 -1
  73. wandb/sdk/interface/interface_shared.py +1 -1
  74. wandb/sdk/internal/file_pusher.py +2 -5
  75. wandb/sdk/internal/file_stream.py +6 -19
  76. wandb/sdk/internal/internal_api.py +148 -136
  77. wandb/sdk/internal/job_builder.py +208 -136
  78. wandb/sdk/internal/progress.py +0 -28
  79. wandb/sdk/internal/sender.py +102 -39
  80. wandb/sdk/internal/settings_static.py +8 -1
  81. wandb/sdk/internal/system/assets/trainium.py +3 -3
  82. wandb/sdk/internal/system/system_info.py +4 -2
  83. wandb/sdk/internal/update.py +1 -1
  84. wandb/sdk/launch/__init__.py +9 -1
  85. wandb/sdk/launch/_launch.py +4 -24
  86. wandb/sdk/launch/_launch_add.py +1 -3
  87. wandb/sdk/launch/_project_spec.py +187 -225
  88. wandb/sdk/launch/agent/agent.py +59 -19
  89. wandb/sdk/launch/agent/config.py +0 -3
  90. wandb/sdk/launch/builder/abstract.py +68 -1
  91. wandb/sdk/launch/builder/build.py +165 -576
  92. wandb/sdk/launch/builder/context_manager.py +235 -0
  93. wandb/sdk/launch/builder/docker_builder.py +7 -23
  94. wandb/sdk/launch/builder/kaniko_builder.py +12 -25
  95. wandb/sdk/launch/builder/templates/dockerfile.py +92 -0
  96. wandb/sdk/launch/create_job.py +51 -45
  97. wandb/sdk/launch/environment/aws_environment.py +26 -1
  98. wandb/sdk/launch/inputs/files.py +148 -0
  99. wandb/sdk/launch/inputs/internal.py +224 -0
  100. wandb/sdk/launch/inputs/manage.py +95 -0
  101. wandb/sdk/launch/registry/google_artifact_registry.py +1 -1
  102. wandb/sdk/launch/runner/abstract.py +2 -2
  103. wandb/sdk/launch/runner/kubernetes_monitor.py +45 -12
  104. wandb/sdk/launch/runner/kubernetes_runner.py +6 -8
  105. wandb/sdk/launch/runner/local_container.py +2 -3
  106. wandb/sdk/launch/runner/local_process.py +8 -29
  107. wandb/sdk/launch/runner/sagemaker_runner.py +20 -14
  108. wandb/sdk/launch/runner/vertex_runner.py +8 -7
  109. wandb/sdk/launch/sweeps/scheduler.py +5 -3
  110. wandb/sdk/launch/sweeps/scheduler_sweep.py +1 -1
  111. wandb/sdk/launch/sweeps/utils.py +4 -4
  112. wandb/sdk/launch/utils.py +16 -138
  113. wandb/sdk/lib/_settings_toposort_generated.py +2 -5
  114. wandb/sdk/lib/apikey.py +4 -2
  115. wandb/sdk/lib/config_util.py +3 -3
  116. wandb/sdk/lib/import_hooks.py +1 -1
  117. wandb/sdk/lib/proto_util.py +22 -1
  118. wandb/sdk/lib/redirect.py +20 -15
  119. wandb/sdk/lib/tracelog.py +1 -1
  120. wandb/sdk/service/service.py +2 -1
  121. wandb/sdk/service/streams.py +5 -5
  122. wandb/sdk/wandb_init.py +25 -59
  123. wandb/sdk/wandb_login.py +28 -25
  124. wandb/sdk/wandb_run.py +123 -53
  125. wandb/sdk/wandb_settings.py +33 -64
  126. wandb/sdk/wandb_setup.py +1 -1
  127. wandb/sdk/wandb_watch.py +1 -1
  128. wandb/sklearn/plot/classifier.py +10 -12
  129. wandb/sklearn/plot/clusterer.py +1 -1
  130. wandb/sync/sync.py +2 -2
  131. wandb/testing/relay.py +32 -17
  132. wandb/util.py +36 -37
  133. wandb/wandb_agent.py +3 -3
  134. wandb/wandb_controller.py +5 -4
  135. {wandb-0.17.0rc1.dist-info → wandb-0.17.1.dist-info}/METADATA +8 -10
  136. {wandb-0.17.0rc1.dist-info → wandb-0.17.1.dist-info}/RECORD +139 -161
  137. {wandb-0.17.0rc1.dist-info → wandb-0.17.1.dist-info}/WHEEL +1 -1
  138. wandb/apis/reports/v1/_blocks.py +0 -1406
  139. wandb/apis/reports/v1/_helpers.py +0 -70
  140. wandb/apis/reports/v1/_panels.py +0 -1282
  141. wandb/apis/reports/v1/_templates.py +0 -478
  142. wandb/apis/reports/v1/blocks.py +0 -27
  143. wandb/apis/reports/v1/helpers.py +0 -2
  144. wandb/apis/reports/v1/mutations.py +0 -66
  145. wandb/apis/reports/v1/panels.py +0 -17
  146. wandb/apis/reports/v1/report.py +0 -268
  147. wandb/apis/reports/v1/runset.py +0 -144
  148. wandb/apis/reports/v1/templates.py +0 -7
  149. wandb/apis/reports/v1/util.py +0 -406
  150. wandb/apis/reports/v1/validators.py +0 -131
  151. wandb/apis/reports/v2/blocks.py +0 -25
  152. wandb/apis/reports/v2/expr_parsing.py +0 -257
  153. wandb/apis/reports/v2/gql.py +0 -68
  154. wandb/apis/reports/v2/interface.py +0 -1911
  155. wandb/apis/reports/v2/internal.py +0 -867
  156. wandb/apis/reports/v2/metrics.py +0 -6
  157. wandb/apis/reports/v2/panels.py +0 -15
  158. wandb/catboost/__init__.py +0 -9
  159. wandb/fastai/__init__.py +0 -9
  160. wandb/keras/__init__.py +0 -19
  161. wandb/lightgbm/__init__.py +0 -9
  162. wandb/plots/__init__.py +0 -6
  163. wandb/plots/explain_text.py +0 -36
  164. wandb/plots/heatmap.py +0 -81
  165. wandb/plots/named_entity.py +0 -43
  166. wandb/plots/part_of_speech.py +0 -50
  167. wandb/plots/plot_definitions.py +0 -768
  168. wandb/plots/precision_recall.py +0 -121
  169. wandb/plots/roc.py +0 -103
  170. wandb/sacred/__init__.py +0 -3
  171. wandb/xgboost/__init__.py +0 -9
  172. {wandb-0.17.0rc1.dist-info → wandb-0.17.1.dist-info}/entry_points.txt +0 -0
  173. {wandb-0.17.0rc1.dist-info → wandb-0.17.1.dist-info}/licenses/LICENSE +0 -0
@@ -1,1911 +0,0 @@
1
- """Public interfaces for the Report API."""
2
-
3
- import os
4
- from datetime import datetime
5
- from typing import Dict, Iterable, Optional, Tuple, Union
6
- from typing import List as LList
7
-
8
- from annotated_types import Annotated, Ge, Le
9
-
10
- try:
11
- from typing import Literal
12
- except ImportError:
13
- from typing_extensions import Literal
14
-
15
- from urllib.parse import urlparse, urlunparse
16
-
17
- from pydantic import ConfigDict, Field, validator
18
- from pydantic.dataclasses import dataclass
19
-
20
- import wandb
21
-
22
- from . import expr_parsing, gql, internal
23
- from .internal import (
24
- CodeCompareDiff,
25
- FontSize,
26
- GroupAgg,
27
- GroupArea,
28
- Language,
29
- LegendPosition,
30
- LinePlotStyle,
31
- Range,
32
- ReportWidth,
33
- SmoothingType,
34
- )
35
-
36
- TextLike = Union[str, "TextWithInlineComments", "Link", "InlineLatex", "InlineCode"]
37
- TextLikeField = Union[TextLike, LList[TextLike]]
38
- SpecialMetricType = Union["Config", "SummaryMetric", "Metric"]
39
- MetricType = Union[str, SpecialMetricType]
40
- ParallelCoordinatesMetric = Union[str, "Config", "SummaryMetric"]
41
- RunId = str
42
-
43
-
44
- dataclass_config = ConfigDict(validate_assignment=True, extra="forbid", slots=True)
45
-
46
-
47
- @dataclass(config=dataclass_config)
48
- class Base:
49
- ...
50
- # TODO: Add __repr__ that hides Nones
51
-
52
- @property
53
- def _model(self):
54
- return self.to_model()
55
-
56
- @property
57
- def _spec(self):
58
- return self._model.model_dump(by_alias=True, exclude_none=True)
59
-
60
-
61
- @dataclass(config=dataclass_config, frozen=True)
62
- class RunsetGroupKey:
63
- key: MetricType
64
- value: str
65
-
66
-
67
- @dataclass(config=dataclass_config, frozen=True)
68
- class RunsetGroup:
69
- runset_name: str
70
- keys: Tuple[RunsetGroupKey, ...]
71
-
72
-
73
- @dataclass(config=dataclass_config, frozen=True)
74
- class Metric:
75
- name: str
76
-
77
-
78
- @dataclass(config=dataclass_config, frozen=True)
79
- class Config:
80
- name: str
81
-
82
-
83
- @dataclass(config=dataclass_config, frozen=True)
84
- class SummaryMetric:
85
- name: str
86
-
87
-
88
- @dataclass(config=dataclass_config)
89
- class Layout(Base):
90
- x: int = 0
91
- y: int = 0
92
- w: int = 8
93
- h: int = 6
94
-
95
- def to_model(self):
96
- return internal.Layout(x=self.x, y=self.y, w=self.w, h=self.h)
97
-
98
- @classmethod
99
- def from_model(cls, model: internal.Layout):
100
- return cls(x=model.x, y=model.y, w=model.w, h=model.h)
101
-
102
-
103
- @dataclass(config=dataclass_config)
104
- class Block(Base): ...
105
-
106
-
107
- @dataclass(config=ConfigDict(validate_assignment=True, extra="allow", slots=True))
108
- class UnknownBlock(Block):
109
- def __repr__(self) -> str:
110
- class_name = self.__class__.__name__
111
- attributes = ", ".join(
112
- f"{key}={value!r}" for key, value in self.__dict__.items()
113
- )
114
- return f"{class_name}({attributes})"
115
-
116
- def to_model(self):
117
- d = self.__dict__
118
- return internal.UnknownBlock.model_validate(d)
119
-
120
- @classmethod
121
- def from_model(cls, model: internal.UnknownBlock):
122
- d = model.model_dump()
123
- return cls(**d)
124
-
125
-
126
- @dataclass(config=dataclass_config)
127
- class TextWithInlineComments(Base):
128
- text: str
129
-
130
- _inline_comments: Optional[LList[internal.InlineComment]] = Field(
131
- default_factory=lambda: None, repr=False
132
- )
133
-
134
-
135
- @dataclass(config=dataclass_config)
136
- class Heading(Block):
137
- @classmethod
138
- def from_model(cls, model: internal.Heading):
139
- text = _internal_children_to_text(model.children)
140
-
141
- blocks = None
142
- if model.collapsed_children:
143
- blocks = [_lookup(b) for b in model.collapsed_children]
144
-
145
- if model.level == 1:
146
- return H1(text=text, collapsed_blocks=blocks)
147
- if model.level == 2:
148
- return H2(text=text, collapsed_blocks=blocks)
149
- if model.level == 3:
150
- return H3(text=text, collapsed_blocks=blocks)
151
-
152
-
153
- @dataclass(config=dataclass_config)
154
- class H1(Heading):
155
- text: TextLikeField = ""
156
- collapsed_blocks: Optional[LList["BlockTypes"]] = None
157
-
158
- def to_model(self):
159
- collapsed_children = self.collapsed_blocks
160
- if collapsed_children is not None:
161
- collapsed_children = [b.to_model() for b in collapsed_children]
162
-
163
- return internal.Heading(
164
- level=1,
165
- children=_text_to_internal_children(self.text),
166
- collapsed_children=collapsed_children,
167
- )
168
-
169
-
170
- @dataclass(config=dataclass_config)
171
- class H2(Heading):
172
- text: TextLikeField = ""
173
- collapsed_blocks: Optional[LList["BlockTypes"]] = None
174
-
175
- def to_model(self):
176
- collapsed_children = self.collapsed_blocks
177
- if collapsed_children is not None:
178
- collapsed_children = [b.to_model() for b in collapsed_children]
179
-
180
- return internal.Heading(
181
- level=2,
182
- children=_text_to_internal_children(self.text),
183
- collapsed_children=collapsed_children,
184
- )
185
-
186
-
187
- @dataclass(config=dataclass_config)
188
- class H3(Heading):
189
- text: TextLikeField = ""
190
- collapsed_blocks: Optional[LList["BlockTypes"]] = None
191
-
192
- def to_model(self):
193
- collapsed_children = self.collapsed_blocks
194
- if collapsed_children is not None:
195
- collapsed_children = [b.to_model() for b in collapsed_children]
196
-
197
- return internal.Heading(
198
- level=3,
199
- children=_text_to_internal_children(self.text),
200
- collapsed_children=collapsed_children,
201
- )
202
-
203
-
204
- @dataclass(config=dataclass_config)
205
- class Link(Base):
206
- text: Union[str, TextWithInlineComments]
207
- url: str
208
-
209
- _inline_comments: Optional[LList[internal.InlineComment]] = Field(
210
- default_factory=lambda: None, init=False, repr=False
211
- )
212
-
213
-
214
- @dataclass(config=dataclass_config)
215
- class InlineLatex(Base):
216
- text: str
217
-
218
-
219
- @dataclass(config=dataclass_config)
220
- class InlineCode(Base):
221
- text: str
222
-
223
-
224
- @dataclass(config=dataclass_config)
225
- class P(Block):
226
- text: TextLikeField = ""
227
-
228
- def to_model(self):
229
- children = _text_to_internal_children(self.text)
230
- return internal.Paragraph(children=children)
231
-
232
- @classmethod
233
- def from_model(cls, model: internal.Paragraph):
234
- pieces = _internal_children_to_text(model.children)
235
- return cls(text=pieces)
236
-
237
-
238
- @dataclass(config=dataclass_config)
239
- class ListItem(Base):
240
- @classmethod
241
- def from_model(cls, model: internal.ListItem):
242
- text = _internal_children_to_text(model.children)
243
- if model.checked is not None:
244
- return CheckedListItem(text=text, checked=model.checked)
245
- return text
246
- # if model.ordered is not None:
247
- # return OrderedListItem(text=text)
248
- # return UnorderedListItem(text=text)
249
-
250
-
251
- @dataclass(config=dataclass_config)
252
- class CheckedListItem(Base):
253
- text: TextLikeField = ""
254
- checked: bool = False
255
-
256
- def to_model(self):
257
- return internal.ListItem(
258
- children=[
259
- internal.Paragraph(children=_text_to_internal_children(self.text))
260
- ],
261
- checked=self.checked,
262
- )
263
-
264
-
265
- @dataclass(config=dataclass_config)
266
- class OrderedListItem(Base):
267
- text: TextLikeField = ""
268
-
269
- def to_model(self):
270
- return internal.ListItem(
271
- children=[
272
- internal.Paragraph(children=_text_to_internal_children(self.text))
273
- ],
274
- ordered=True,
275
- )
276
-
277
-
278
- @dataclass(config=dataclass_config)
279
- class UnorderedListItem(Base):
280
- text: TextLikeField = ""
281
-
282
- def to_model(self):
283
- return internal.ListItem(
284
- children=[
285
- internal.Paragraph(children=_text_to_internal_children(self.text))
286
- ],
287
- )
288
-
289
-
290
- @dataclass(config=dataclass_config)
291
- class List(Block):
292
- @classmethod
293
- def from_model(cls, model: internal.List):
294
- if not model.children:
295
- return UnorderedList()
296
-
297
- item = model.children[0]
298
- items = [ListItem.from_model(x) for x in model.children]
299
- if item.checked is not None:
300
- return CheckedList(items=items)
301
-
302
- if item.ordered is not None:
303
- return OrderedList(items=items)
304
-
305
- # else unordered
306
- return UnorderedList(items=items)
307
-
308
-
309
- @dataclass(config=dataclass_config)
310
- class CheckedList(List):
311
- items: LList[CheckedListItem] = Field(default_factory=lambda: [CheckedListItem()])
312
-
313
- def to_model(self):
314
- items = [x.to_model() for x in self.items]
315
- return internal.List(children=items)
316
-
317
-
318
- @dataclass(config=dataclass_config)
319
- class OrderedList(List):
320
- items: LList[str] = Field(default_factory=lambda: [""])
321
-
322
- def to_model(self):
323
- children = [OrderedListItem(li).to_model() for li in self.items]
324
- return internal.List(children=children, ordered=True)
325
-
326
-
327
- @dataclass(config=dataclass_config)
328
- class UnorderedList(List):
329
- items: LList[str] = Field(default_factory=lambda: [""])
330
-
331
- def to_model(self):
332
- children = [UnorderedListItem(li).to_model() for li in self.items]
333
- return internal.List(children=children)
334
-
335
-
336
- @dataclass(config=dataclass_config)
337
- class BlockQuote(Block):
338
- text: TextLikeField = ""
339
-
340
- def to_model(self):
341
- return internal.BlockQuote(children=_text_to_internal_children(self.text))
342
-
343
- @classmethod
344
- def from_model(cls, model: internal.BlockQuote):
345
- return cls(text=_internal_children_to_text(model.children))
346
-
347
-
348
- @dataclass(config=dataclass_config)
349
- class CodeBlock(Block):
350
- code: TextLikeField = ""
351
- language: Optional[Language] = "python"
352
-
353
- def to_model(self):
354
- return internal.CodeBlock(
355
- children=[
356
- internal.CodeLine(
357
- children=_text_to_internal_children(self.code),
358
- language=self.language,
359
- )
360
- ],
361
- language=self.language,
362
- )
363
-
364
- @classmethod
365
- def from_model(cls, model: internal.CodeBlock):
366
- code = _internal_children_to_text(model.children[0].children)
367
- return cls(code=code, language=model.language)
368
-
369
-
370
- @dataclass(config=dataclass_config)
371
- class MarkdownBlock(Block):
372
- text: str = ""
373
-
374
- def to_model(self):
375
- return internal.MarkdownBlock(content=self.text)
376
-
377
- @classmethod
378
- def from_model(cls, model: internal.MarkdownBlock):
379
- return cls(text=model.content)
380
-
381
-
382
- @dataclass(config=dataclass_config)
383
- class LatexBlock(Block):
384
- text: str = ""
385
-
386
- def to_model(self):
387
- return internal.LatexBlock(content=self.text)
388
-
389
- @classmethod
390
- def from_model(cls, model: internal.LatexBlock):
391
- return cls(text=model.content)
392
-
393
-
394
- @dataclass(config=dataclass_config)
395
- class Image(Block):
396
- url: str = "https://raw.githubusercontent.com/wandb/assets/main/wandb-logo-yellow-dots-black-wb.svg"
397
- caption: TextLikeField = ""
398
-
399
- def to_model(self):
400
- has_caption = False
401
- children = _text_to_internal_children(self.caption)
402
- if children:
403
- has_caption = True
404
-
405
- return internal.Image(children=children, url=self.url, has_caption=has_caption)
406
-
407
- @classmethod
408
- def from_model(cls, model: internal.Image):
409
- caption = _internal_children_to_text(model.children)
410
- return cls(url=model.url, caption=caption)
411
-
412
-
413
- @dataclass(config=dataclass_config)
414
- class CalloutBlock(Block):
415
- text: TextLikeField = ""
416
-
417
- def to_model(self):
418
- return internal.CalloutBlock(
419
- children=[
420
- internal.CalloutLine(children=_text_to_internal_children(self.text))
421
- ]
422
- )
423
-
424
- @classmethod
425
- def from_model(cls, model: internal.CalloutBlock):
426
- text = _internal_children_to_text(model.children[0].children)
427
- return cls(text=text)
428
-
429
-
430
- @dataclass(config=dataclass_config)
431
- class HorizontalRule(Block):
432
- def to_model(self):
433
- return internal.HorizontalRule()
434
-
435
- @classmethod
436
- def from_model(cls, model: internal.HorizontalRule):
437
- return cls()
438
-
439
-
440
- @dataclass(config=dataclass_config)
441
- class Video(Block):
442
- url: str = "https://www.youtube.com/watch?v=krWjJcW80_A"
443
-
444
- def to_model(self):
445
- return internal.Video(url=self.url)
446
-
447
- @classmethod
448
- def from_model(cls, model: internal.Video):
449
- return cls(url=model.url)
450
-
451
-
452
- @dataclass(config=dataclass_config)
453
- class Spotify(Block):
454
- spotify_id: str
455
-
456
- def to_model(self):
457
- return internal.Spotify(spotify_id=self.spotify_id)
458
-
459
- @classmethod
460
- def from_model(cls, model: internal.Spotify):
461
- return cls(spotify_id=model.spotify_id)
462
-
463
-
464
- @dataclass(config=dataclass_config)
465
- class SoundCloud(Block):
466
- html: str
467
-
468
- def to_model(self):
469
- return internal.SoundCloud(html=self.html)
470
-
471
- @classmethod
472
- def from_model(cls, model: internal.SoundCloud):
473
- return cls(html=model.html)
474
-
475
-
476
- @dataclass(config=dataclass_config)
477
- class GalleryReport(Base):
478
- report_id: str
479
-
480
-
481
- @dataclass(config=dataclass_config)
482
- class GalleryURL(Base):
483
- url: str # app accepts non-standard URL unfortunately
484
- title: Optional[str] = None
485
- description: Optional[str] = None
486
- image_url: Optional[str] = None
487
-
488
-
489
- @dataclass(config=dataclass_config)
490
- class Gallery(Block):
491
- items: LList[Union[GalleryReport, GalleryURL]] = Field(default_factory=list)
492
-
493
- def to_model(self):
494
- links = []
495
- for x in self.items:
496
- if isinstance(x, GalleryReport):
497
- link = internal.GalleryLinkReport(id=x.report_id)
498
- elif isinstance(x, GalleryURL):
499
- link = internal.GalleryLinkURL(
500
- url=x.url,
501
- title=x.title,
502
- description=x.description,
503
- image_url=x.image_url,
504
- )
505
- links.append(link)
506
-
507
- return internal.Gallery(links=links)
508
-
509
- @classmethod
510
- def from_model(cls, model: internal.Gallery):
511
- items = []
512
- if model.ids:
513
- items = [GalleryReport(x) for x in model.ids]
514
- elif model.links:
515
- for x in model.links:
516
- if isinstance(x, internal.GalleryLinkReport):
517
- item = GalleryReport(report_id=x.id)
518
- elif isinstance(x, internal.GalleryLinkURL):
519
- item = GalleryURL(
520
- url=x.url,
521
- title=x.title,
522
- description=x.description,
523
- image_url=x.image_url,
524
- )
525
- items.append(item)
526
-
527
- return cls(items=items)
528
-
529
-
530
- @dataclass(config=dataclass_config)
531
- class OrderBy(Base):
532
- name: MetricType
533
- ascending: bool = False
534
-
535
- def to_model(self):
536
- return internal.SortKey(
537
- key=internal.SortKeyKey(name=_metric_to_backend(self.name)),
538
- ascending=self.ascending,
539
- )
540
-
541
- @classmethod
542
- def from_model(cls, model: internal.SortKey):
543
- return cls(
544
- name=_metric_to_frontend(model.key.name),
545
- ascending=model.ascending,
546
- )
547
-
548
-
549
- @dataclass(config=dataclass_config)
550
- class Runset(Base):
551
- entity: str = ""
552
- project: str = ""
553
- name: str = "Run set"
554
- query: str = ""
555
- filters: Optional[str] = ""
556
- groupby: LList[str] = Field(default_factory=list)
557
- order: LList[OrderBy] = Field(
558
- default_factory=lambda: [OrderBy("CreatedTimestamp", ascending=False)]
559
- )
560
-
561
- # this field does not get exported to model, but is used in PanelGrid
562
- custom_run_colors: Dict[Union[str, Tuple[MetricType, ...]], str] = Field(
563
- default_factory=dict
564
- )
565
-
566
- _id: str = Field(default_factory=internal._generate_name, init=False, repr=False)
567
-
568
- def to_model(self):
569
- project = None
570
- if self.entity or self.project:
571
- project = internal.Project(entity_name=self.entity, name=self.project)
572
-
573
- obj = internal.Runset(
574
- project=project,
575
- name=self.name,
576
- filters=expr_parsing.expr_to_filters(self.filters),
577
- grouping=[
578
- internal.Key(name=expr_parsing.to_backend_name(g)) for g in self.groupby
579
- ],
580
- sort=internal.Sort(keys=[o.to_model() for o in self.order]),
581
- )
582
- obj.id = self._id
583
- return obj
584
-
585
- @classmethod
586
- def from_model(cls, model: internal.Runset):
587
- entity = ""
588
- project = ""
589
-
590
- p = model.project
591
- if p is not None:
592
- if p.entity_name:
593
- entity = p.entity_name
594
- if p.name:
595
- project = p.name
596
-
597
- obj = cls(
598
- entity=entity,
599
- project=project,
600
- name=model.name,
601
- filters=expr_parsing.filters_to_expr(model.filters),
602
- groupby=[expr_parsing.to_frontend_name(k.name) for k in model.grouping],
603
- order=[OrderBy.from_model(s) for s in model.sort.keys],
604
- )
605
- obj._id = model.id
606
- return obj
607
-
608
-
609
- @dataclass(config=dataclass_config)
610
- class Panel(Base):
611
- id: str = Field(default_factory=internal._generate_name, kw_only=True)
612
- layout: Layout = Field(default_factory=Layout, kw_only=True)
613
-
614
- _ref: Optional[internal.Ref] = Field(
615
- default_factory=lambda: None, init=False, repr=False
616
- )
617
-
618
-
619
- @dataclass(config=dataclass_config)
620
- class PanelGrid(Block):
621
- runsets: LList["Runset"] = Field(default_factory=lambda: [Runset()])
622
- panels: LList["PanelTypes"] = Field(default_factory=list)
623
- active_runset: int = 0
624
- custom_run_colors: Dict[Union[RunId, RunsetGroup], str] = Field(
625
- default_factory=dict
626
- )
627
-
628
- _ref: Optional[internal.Ref] = Field(
629
- default_factory=lambda: None, init=False, repr=False
630
- )
631
- _open_viz: bool = Field(default_factory=lambda: True, init=False, repr=False)
632
- _panel_bank_sections: LList[dict] = Field(
633
- default_factory=list, init=False, repr=False
634
- )
635
- _panel_grid_metadata_ref: Optional[internal.Ref] = Field(
636
- default_factory=lambda: None, init=False, repr=False
637
- )
638
-
639
- def to_model(self):
640
- return internal.PanelGrid(
641
- metadata=internal.PanelGridMetadata(
642
- run_sets=[rs.to_model() for rs in self.runsets],
643
- panel_bank_section_config=internal.PanelBankSectionConfig(
644
- panels=[p.to_model() for p in self.panels],
645
- ),
646
- panels=internal.PanelGridMetadataPanels(
647
- ref=self._panel_grid_metadata_ref,
648
- panel_bank_config=internal.PanelBankConfig(),
649
- open_viz=self._open_viz,
650
- ),
651
- custom_run_colors=_to_color_dict(self.custom_run_colors, self.runsets),
652
- )
653
- )
654
-
655
- @classmethod
656
- def from_model(cls, model: internal.PanelGrid):
657
- runsets = [Runset.from_model(rs) for rs in model.metadata.run_sets]
658
- obj = cls(
659
- runsets=runsets,
660
- panels=[
661
- _lookup_panel(p)
662
- for p in model.metadata.panel_bank_section_config.panels
663
- ],
664
- active_runset=model.metadata.open_run_set,
665
- custom_run_colors=_from_color_dict(
666
- model.metadata.custom_run_colors, runsets
667
- ),
668
- # _panel_bank_sections=model.metadata.panel_bank_config.sections,
669
- )
670
- obj._open_viz = model.metadata.open_viz
671
- obj._ref = model.metadata.panels.ref
672
- return obj
673
-
674
- @validator("panels")
675
- def _resolve_collisions(cls, v): # noqa: N805
676
- v2 = _resolve_collisions(v)
677
- return v2
678
-
679
- @validator("runsets")
680
- def _validate_list_not_empty(cls, v): # noqa: N805
681
- if len(v) < 1:
682
- raise ValueError("must have at least one runset")
683
- return v
684
-
685
-
686
- @dataclass(config=dataclass_config)
687
- class TableOfContents(Block):
688
- def to_model(self):
689
- return internal.TableOfContents()
690
-
691
- @classmethod
692
- def from_model(cls, model: internal.TableOfContents):
693
- return cls()
694
-
695
-
696
- @dataclass(config=dataclass_config)
697
- class Twitter(Block):
698
- html: str
699
-
700
- def to_model(self):
701
- return internal.Twitter(html=self.html)
702
-
703
- @classmethod
704
- def from_model(cls, model: internal.Twitter):
705
- return cls(html=model.html)
706
-
707
-
708
- @dataclass(config=dataclass_config)
709
- class WeaveBlock(Block): ...
710
-
711
-
712
- BlockTypes = Union[
713
- H1,
714
- H2,
715
- H3,
716
- P,
717
- CodeBlock,
718
- MarkdownBlock,
719
- LatexBlock,
720
- Image,
721
- UnorderedList,
722
- OrderedList,
723
- CheckedList,
724
- CalloutBlock,
725
- Video,
726
- HorizontalRule,
727
- Spotify,
728
- SoundCloud,
729
- Gallery,
730
- PanelGrid,
731
- TableOfContents,
732
- BlockQuote,
733
- Twitter,
734
- UnknownBlock,
735
- ]
736
-
737
-
738
- block_mapping = {
739
- internal.Paragraph: P,
740
- internal.CalloutBlock: CalloutBlock,
741
- internal.CodeBlock: CodeBlock,
742
- internal.Gallery: Gallery,
743
- internal.Heading: Heading,
744
- internal.HorizontalRule: HorizontalRule,
745
- internal.Image: Image,
746
- internal.LatexBlock: LatexBlock,
747
- internal.List: List,
748
- internal.MarkdownBlock: MarkdownBlock,
749
- internal.PanelGrid: PanelGrid,
750
- internal.TableOfContents: TableOfContents,
751
- internal.Video: Video,
752
- internal.BlockQuote: BlockQuote,
753
- internal.Spotify: Spotify,
754
- internal.Twitter: Twitter,
755
- internal.SoundCloud: SoundCloud,
756
- internal.UnknownBlock: UnknownBlock,
757
- }
758
-
759
-
760
- @dataclass(config=dataclass_config)
761
- class GradientPoint(Base):
762
- color: Annotated[str, internal.ColorStrConstraints]
763
- offset: Annotated[float, Ge(0), Le(100)] = 0
764
-
765
- def to_model(self):
766
- return internal.GradientPoint(color=self.color, offset=self.offset)
767
-
768
- @classmethod
769
- def from_model(cls, model: internal.GradientPoint):
770
- return cls(color=model.color, offset=model.offset)
771
-
772
-
773
- @dataclass(config=dataclass_config)
774
- class LinePlot(Panel):
775
- title: Optional[str] = None
776
- x: Optional[MetricType] = "Step"
777
- y: LList[MetricType] = Field(default_factory=list)
778
- range_x: Range = Field(default_factory=lambda: (None, None))
779
- range_y: Range = Field(default_factory=lambda: (None, None))
780
- log_x: Optional[bool] = None
781
- log_y: Optional[bool] = None
782
- title_x: Optional[str] = None
783
- title_y: Optional[str] = None
784
- ignore_outliers: Optional[bool] = None
785
- groupby: Optional[str] = None
786
- groupby_aggfunc: Optional[GroupAgg] = None
787
- groupby_rangefunc: Optional[GroupArea] = None
788
- smoothing_factor: Optional[float] = None
789
- smoothing_type: Optional[SmoothingType] = None
790
- smoothing_show_original: Optional[bool] = None
791
- max_runs_to_show: Optional[int] = None
792
- custom_expressions: Optional[LList[str]] = None
793
- plot_type: Optional[LinePlotStyle] = None
794
- font_size: Optional[FontSize] = None
795
- legend_position: Optional[LegendPosition] = None
796
- legend_template: Optional[str] = None
797
- aggregate: Optional[bool] = None
798
- xaxis_expression: Optional[str] = None
799
-
800
- def to_model(self):
801
- obj = internal.LinePlot(
802
- config=internal.LinePlotConfig(
803
- chart_title=self.title,
804
- x_axis=_metric_to_backend(self.x),
805
- metrics=[_metric_to_backend(name) for name in _listify(self.y)],
806
- x_axis_min=self.range_x[0],
807
- x_axis_max=self.range_x[1],
808
- y_axis_min=self.range_x[0],
809
- y_axis_max=self.range_x[1],
810
- x_log_scale=self.log_x,
811
- y_log_scale=self.log_y,
812
- x_axis_title=self.title_x,
813
- y_axis_title=self.title_y,
814
- ignore_outliers=self.ignore_outliers,
815
- group_by=self.groupby,
816
- group_agg=self.groupby_aggfunc,
817
- group_area=self.groupby_rangefunc,
818
- smoothing_weight=self.smoothing_factor,
819
- smoothing_type=self.smoothing_type,
820
- show_original_after_smoothing=self.smoothing_show_original,
821
- limit=self.max_runs_to_show,
822
- expressions=self.custom_expressions,
823
- plot_type=self.plot_type,
824
- font_size=self.font_size,
825
- legend_position=self.legend_position,
826
- legend_template=self.legend_template,
827
- aggregate=self.aggregate,
828
- x_expression=self.xaxis_expression,
829
- ),
830
- id=self.id,
831
- layout=self.layout.to_model(),
832
- )
833
- obj.ref = self._ref
834
- return obj
835
-
836
- @classmethod
837
- def from_model(cls, model: internal.LinePlot):
838
- obj = cls(
839
- title=model.config.chart_title,
840
- x=_metric_to_frontend(model.config.x_axis),
841
- y=[_metric_to_frontend(name) for name in model.config.metrics],
842
- range_x=(model.config.x_axis_min, model.config.x_axis_max),
843
- range_y=(model.config.y_axis_min, model.config.y_axis_max),
844
- log_x=model.config.x_log_scale,
845
- log_y=model.config.y_log_scale,
846
- title_x=model.config.x_axis_title,
847
- title_y=model.config.y_axis_title,
848
- ignore_outliers=model.config.ignore_outliers,
849
- groupby=model.config.group_by,
850
- groupby_aggfunc=model.config.group_agg,
851
- groupby_rangefunc=model.config.group_area,
852
- smoothing_factor=model.config.smoothing_weight,
853
- smoothing_type=model.config.smoothing_type,
854
- smoothing_show_original=model.config.show_original_after_smoothing,
855
- max_runs_to_show=model.config.limit,
856
- custom_expressions=model.config.expressions,
857
- plot_type=model.config.plot_type,
858
- font_size=model.config.font_size,
859
- legend_position=model.config.legend_position,
860
- legend_template=model.config.legend_template,
861
- aggregate=model.config.aggregate,
862
- xaxis_expression=model.config.x_expression,
863
- layout=Layout.from_model(model.layout),
864
- id=model.id,
865
- )
866
- obj._ref = model.ref
867
- return obj
868
-
869
-
870
- @dataclass(config=dataclass_config)
871
- class ScatterPlot(Panel):
872
- title: Optional[str] = None
873
- x: Optional[MetricType] = None
874
- y: Optional[MetricType] = None
875
- z: Optional[MetricType] = None
876
- range_x: Range = Field(default_factory=lambda: (None, None))
877
- range_y: Range = Field(default_factory=lambda: (None, None))
878
- range_z: Range = Field(default_factory=lambda: (None, None))
879
- log_x: Optional[bool] = None
880
- log_y: Optional[bool] = None
881
- log_z: Optional[bool] = None
882
- running_ymin: Optional[bool] = None
883
- running_ymax: Optional[bool] = None
884
- running_ymean: Optional[bool] = None
885
- legend_template: Optional[str] = None
886
- gradient: Optional[LList[GradientPoint]] = None
887
- font_size: Optional[FontSize] = None
888
- regression: Optional[bool] = None
889
-
890
- def to_model(self):
891
- custom_gradient = self.gradient
892
- if custom_gradient is not None:
893
- custom_gradient = [cgp.to_model() for cgp in self.gradient]
894
-
895
- obj = internal.ScatterPlot(
896
- config=internal.ScatterPlotConfig(
897
- chart_title=self.title,
898
- x_axis=_metric_to_backend(self.x),
899
- y_axis=_metric_to_backend(self.y),
900
- z_axis=_metric_to_backend(self.z),
901
- x_axis_min=self.range_x[0],
902
- x_axis_max=self.range_x[1],
903
- y_axis_min=self.range_y[0],
904
- y_axis_max=self.range_y[1],
905
- z_axis_min=self.range_z[0],
906
- z_axis_max=self.range_z[1],
907
- x_axis_log_scale=self.log_x,
908
- y_axis_log_scale=self.log_y,
909
- z_axis_log_scale=self.log_z,
910
- show_min_y_axis_line=self.running_ymin,
911
- show_max_y_axis_line=self.running_ymax,
912
- show_avg_y_axis_line=self.running_ymean,
913
- legend_template=self.legend_template,
914
- custom_gradient=custom_gradient,
915
- font_size=self.font_size,
916
- show_linear_regression=self.regression,
917
- ),
918
- layout=self.layout.to_model(),
919
- id=self.id,
920
- )
921
- obj.ref = self._ref
922
- return obj
923
-
924
- @classmethod
925
- def from_model(cls, model: internal.ScatterPlot):
926
- gradient = model.config.custom_gradient
927
- if gradient is not None:
928
- gradient = [GradientPoint.from_model(cgp) for cgp in gradient]
929
-
930
- obj = cls(
931
- title=model.config.chart_title,
932
- x=_metric_to_frontend(model.config.x_axis),
933
- y=_metric_to_frontend(model.config.y_axis),
934
- z=_metric_to_frontend(model.config.z_axis),
935
- range_x=(model.config.x_axis_min, model.config.x_axis_max),
936
- range_y=(model.config.y_axis_min, model.config.y_axis_max),
937
- range_z=(model.config.z_axis_min, model.config.z_axis_max),
938
- log_x=model.config.x_axis_log_scale,
939
- log_y=model.config.y_axis_log_scale,
940
- log_z=model.config.z_axis_log_scale,
941
- running_ymin=model.config.show_min_y_axis_line,
942
- running_ymax=model.config.show_max_y_axis_line,
943
- running_ymean=model.config.show_avg_y_axis_line,
944
- legend_template=model.config.legend_template,
945
- gradient=gradient,
946
- font_size=model.config.font_size,
947
- regression=model.config.show_linear_regression,
948
- layout=Layout.from_model(model.layout),
949
- id=model.id,
950
- )
951
- obj._ref = model.ref
952
- return obj
953
-
954
-
955
- @dataclass(config=dataclass_config)
956
- class BarPlot(Panel):
957
- title: Optional[str] = None
958
- metrics: LList[MetricType] = Field(default_factory=list)
959
- orientation: Literal["v", "h"] = "h"
960
- range_x: Range = Field(default_factory=lambda: (None, None))
961
- title_x: Optional[str] = None
962
- title_y: Optional[str] = None
963
- groupby: Optional[str] = None
964
- groupby_aggfunc: Optional[GroupAgg] = None
965
- groupby_rangefunc: Optional[GroupArea] = None
966
- max_runs_to_show: Optional[int] = None
967
- max_bars_to_show: Optional[int] = None
968
- custom_expressions: Optional[LList[str]] = None
969
- legend_template: Optional[str] = None
970
- font_size: Optional[FontSize] = None
971
- line_titles: Optional[dict] = None
972
- line_colors: Optional[dict] = None
973
-
974
- def to_model(self):
975
- obj = internal.BarPlot(
976
- config=internal.BarPlotConfig(
977
- chart_title=self.title,
978
- metrics=[_metric_to_backend(name) for name in _listify(self.metrics)],
979
- vertical=self.orientation == "v",
980
- x_axis_min=self.range_x[0],
981
- x_axis_max=self.range_x[1],
982
- x_axis_title=self.title_x,
983
- y_axis_title=self.title_y,
984
- group_by=self.groupby,
985
- group_agg=self.groupby_aggfunc,
986
- group_area=self.groupby_rangefunc,
987
- limit=self.max_runs_to_show,
988
- bar_limit=self.max_bars_to_show,
989
- expressions=self.custom_expressions,
990
- legend_template=self.legend_template,
991
- font_size=self.font_size,
992
- override_series_titles=self.line_titles,
993
- override_colors=self.line_colors,
994
- ),
995
- layout=self.layout.to_model(),
996
- id=self.id,
997
- )
998
- obj.ref = self._ref
999
- return obj
1000
-
1001
- @classmethod
1002
- def from_model(cls, model: internal.ScatterPlot):
1003
- obj = cls(
1004
- title=model.config.chart_title,
1005
- metrics=[_metric_to_frontend(name) for name in model.config.metrics],
1006
- orientation="v" if model.config.vertical else "h",
1007
- range_x=(model.config.x_axis_min, model.config.x_axis_max),
1008
- title_x=model.config.x_axis_title,
1009
- title_y=model.config.y_axis_title,
1010
- groupby=model.config.group_by,
1011
- groupby_aggfunc=model.config.group_agg,
1012
- groupby_rangefunc=model.config.group_area,
1013
- max_runs_to_show=model.config.limit,
1014
- max_bars_to_show=model.config.bar_limit,
1015
- custom_expressions=model.config.expressions,
1016
- legend_template=model.config.legend_template,
1017
- font_size=model.config.font_size,
1018
- line_titles=model.config.override_series_titles,
1019
- line_colors=model.config.override_colors,
1020
- layout=Layout.from_model(model.layout),
1021
- id=model.id,
1022
- )
1023
- obj._ref = model.ref
1024
- return obj
1025
-
1026
-
1027
- @dataclass(config=dataclass_config)
1028
- class ScalarChart(Panel):
1029
- title: Optional[str] = None
1030
- metric: MetricType = ""
1031
- groupby_aggfunc: Optional[GroupAgg] = None
1032
- groupby_rangefunc: Optional[GroupArea] = None
1033
- custom_expressions: Optional[LList[str]] = None
1034
- legend_template: Optional[str] = None
1035
- font_size: Optional[FontSize] = None
1036
-
1037
- def to_model(self):
1038
- obj = internal.ScalarChart(
1039
- config=internal.ScalarChartConfig(
1040
- chart_title=self.title,
1041
- metrics=[_metric_to_backend(self.metric)],
1042
- group_agg=self.groupby_aggfunc,
1043
- group_area=self.groupby_rangefunc,
1044
- expressions=self.custom_expressions,
1045
- legend_template=self.legend_template,
1046
- font_size=self.font_size,
1047
- ),
1048
- layout=self.layout.to_model(),
1049
- id=self.id,
1050
- )
1051
- obj.ref = self._ref
1052
- return obj
1053
-
1054
- @classmethod
1055
- def from_model(cls, model: internal.ScatterPlot):
1056
- obj = cls(
1057
- title=model.config.chart_title,
1058
- metric=_metric_to_frontend(model.config.metrics[0]),
1059
- groupby_aggfunc=model.config.group_agg,
1060
- groupby_rangefunc=model.config.group_area,
1061
- custom_expressions=model.config.expressions,
1062
- legend_template=model.config.legend_template,
1063
- font_size=model.config.font_size,
1064
- layout=Layout.from_model(model.layout),
1065
- id=model.id,
1066
- )
1067
- obj._ref = model.ref
1068
- return obj
1069
-
1070
-
1071
- @dataclass(config=dataclass_config)
1072
- class CodeComparer(Panel):
1073
- diff: CodeCompareDiff = "split"
1074
-
1075
- def to_model(self):
1076
- obj = internal.CodeComparer(
1077
- config=internal.CodeComparerConfig(diff=self.diff),
1078
- layout=self.layout.to_model(),
1079
- id=self.id,
1080
- )
1081
- obj.ref = self._ref
1082
- return obj
1083
-
1084
- @classmethod
1085
- def from_model(cls, model: internal.ScatterPlot):
1086
- obj = cls(
1087
- diff=model.config.diff,
1088
- layout=Layout.from_model(model.layout),
1089
- id=model.id,
1090
- )
1091
- obj._ref = model.ref
1092
- return obj
1093
-
1094
-
1095
- @dataclass(config=dataclass_config)
1096
- class ParallelCoordinatesPlotColumn(Base):
1097
- metric: ParallelCoordinatesMetric
1098
- display_name: Optional[str] = None
1099
- inverted: Optional[bool] = None
1100
- log: Optional[bool] = None
1101
-
1102
- _ref: Optional[internal.Ref] = Field(
1103
- default_factory=lambda: None, init=False, repr=False
1104
- )
1105
-
1106
- def to_model(self):
1107
- obj = internal.Column(
1108
- accessor=_metric_to_backend_pc(self.metric),
1109
- display_name=self.display_name,
1110
- inverted=self.inverted,
1111
- log=self.log,
1112
- )
1113
- obj.ref = self._ref
1114
- return obj
1115
-
1116
- @classmethod
1117
- def from_model(cls, model: internal.Column):
1118
- obj = cls(
1119
- metric=_metric_to_frontend_pc(model.accessor),
1120
- display_name=model.display_name,
1121
- inverted=model.inverted,
1122
- log=model.log,
1123
- )
1124
- obj._ref = model.ref
1125
- return obj
1126
-
1127
-
1128
- @dataclass(config=dataclass_config)
1129
- class ParallelCoordinatesPlot(Panel):
1130
- columns: LList[ParallelCoordinatesPlotColumn] = Field(default_factory=list)
1131
- title: Optional[str] = None
1132
- gradient: Optional[LList[GradientPoint]] = None
1133
- font_size: Optional[FontSize] = None
1134
-
1135
- def to_model(self):
1136
- gradient = self.gradient
1137
- if gradient is not None:
1138
- gradient = [x.to_model() for x in self.gradient]
1139
-
1140
- obj = internal.ParallelCoordinatesPlot(
1141
- config=internal.ParallelCoordinatesPlotConfig(
1142
- chart_title=self.title,
1143
- columns=[c.to_model() for c in self.columns],
1144
- custom_gradient=gradient,
1145
- font_size=self.font_size,
1146
- ),
1147
- layout=self.layout.to_model(),
1148
- id=self.id,
1149
- )
1150
- obj.ref = self._ref
1151
- return obj
1152
-
1153
- @classmethod
1154
- def from_model(cls, model: internal.ScatterPlot):
1155
- gradient = model.config.custom_gradient
1156
- if gradient is not None:
1157
- gradient = [GradientPoint.from_model(x) for x in gradient]
1158
-
1159
- obj = cls(
1160
- columns=[
1161
- ParallelCoordinatesPlotColumn.from_model(c)
1162
- for c in model.config.columns
1163
- ],
1164
- title=model.config.chart_title,
1165
- gradient=gradient,
1166
- font_size=model.config.font_size,
1167
- layout=Layout.from_model(model.layout),
1168
- id=model.id,
1169
- )
1170
- obj._ref = model.ref
1171
- return obj
1172
-
1173
-
1174
- @dataclass(config=dataclass_config)
1175
- class ParameterImportancePlot(Panel):
1176
- with_respect_to: str = ""
1177
-
1178
- def to_model(self):
1179
- obj = internal.ParameterImportancePlot(
1180
- config=internal.ParameterImportancePlotConfig(
1181
- target_key=self.with_respect_to
1182
- ),
1183
- layout=self.layout.to_model(),
1184
- id=self.id,
1185
- )
1186
- obj.ref = self._ref
1187
- return obj
1188
-
1189
- @classmethod
1190
- def from_model(cls, model: internal.ScatterPlot):
1191
- obj = cls(
1192
- with_respect_to=model.config.target_key,
1193
- layout=Layout.from_model(model.layout),
1194
- id=model.id,
1195
- )
1196
- obj._ref = model.ref
1197
- return obj
1198
-
1199
-
1200
- @dataclass(config=dataclass_config)
1201
- class RunComparer(Panel):
1202
- diff_only: Optional[Literal["split", True]] = None
1203
-
1204
- def to_model(self):
1205
- obj = internal.RunComparer(
1206
- config=internal.RunComparerConfig(diff_only=self.diff_only),
1207
- layout=self.layout.to_model(),
1208
- id=self.id,
1209
- )
1210
- obj.ref = self._ref
1211
- return obj
1212
-
1213
- @classmethod
1214
- def from_model(cls, model: internal.ScatterPlot):
1215
- obj = cls(
1216
- diff_only=model.config.diff_only,
1217
- layout=Layout.from_model(model.layout),
1218
- id=model.id,
1219
- )
1220
- obj._ref = model.ref
1221
- return obj
1222
-
1223
-
1224
- @dataclass(config=dataclass_config)
1225
- class MediaBrowser(Panel):
1226
- num_columns: Optional[int] = None
1227
- media_keys: LList[str] = Field(default_factory=list)
1228
-
1229
- def to_model(self):
1230
- obj = internal.MediaBrowser(
1231
- config=internal.MediaBrowserConfig(
1232
- column_count=self.num_columns,
1233
- media_keys=self.media_keys,
1234
- ),
1235
- layout=self.layout.to_model(),
1236
- id=self.id,
1237
- )
1238
- obj.ref = self._ref
1239
- return obj
1240
-
1241
- @classmethod
1242
- def from_model(cls, model: internal.MediaBrowser):
1243
- obj = cls(
1244
- num_columns=model.config.column_count,
1245
- media_keys=model.config.media_keys,
1246
- layout=Layout.from_model(model.layout),
1247
- id=model.id,
1248
- )
1249
- obj._ref = model.ref
1250
- return obj
1251
-
1252
-
1253
- @dataclass(config=dataclass_config)
1254
- class MarkdownPanel(Panel):
1255
- markdown: str = ""
1256
-
1257
- def to_model(self):
1258
- obj = internal.MarkdownPanel(
1259
- config=internal.MarkdownPanelConfig(value=self.markdown),
1260
- layout=self.layout.to_model(),
1261
- id=self.id,
1262
- )
1263
- obj.ref = self._ref
1264
- return obj
1265
-
1266
- @classmethod
1267
- def from_model(cls, model: internal.ScatterPlot):
1268
- obj = cls(
1269
- markdown=model.config.value,
1270
- layout=Layout.from_model(model.layout),
1271
- id=model.id,
1272
- )
1273
- obj._ref = model.ref
1274
- return obj
1275
-
1276
-
1277
- # @dataclass(config=dataclass_config)
1278
- # class ConfusionMatrix(Panel):
1279
- # def to_model(self):
1280
- # ...
1281
-
1282
- # @classmethod
1283
- # def from_model(cls, model: internal.ConfusionMatrix):
1284
- # ...
1285
-
1286
-
1287
- # @dataclass(config=dataclass_config)
1288
- # class DataFrames(Panel):
1289
- # def to_model(self):
1290
- # ...
1291
-
1292
- # @classmethod
1293
- # def from_model(cls, model: internal.ScatterPlot):
1294
- # ...
1295
-
1296
-
1297
- # @dataclass(config=dataclass_config)
1298
- # class MultiRunTable(Panel):
1299
- # def to_model(self):
1300
- # ...
1301
-
1302
- # @classmethod
1303
- # def from_model(cls, model: internal.ScatterPlot):
1304
- # ...
1305
-
1306
-
1307
- # @dataclass(config=dataclass_config)
1308
- # class Vega(Panel):
1309
- # def to_model(self):
1310
- # ...
1311
-
1312
- # @classmethod
1313
- # def from_model(cls, model: internal.ScatterPlot):
1314
- # ...
1315
-
1316
-
1317
- # @dataclass(config=dataclass_config)
1318
- # class Vega3(Panel):
1319
- # def to_model(self):
1320
- # ...
1321
-
1322
- # @classmethod
1323
- # def from_model(cls, model: internal.ScatterPlot):
1324
- # ...
1325
-
1326
-
1327
- @dataclass(config=dataclass_config)
1328
- class CustomChart(Panel):
1329
- query: dict = Field(default_factory=dict)
1330
- chart_name: str = Field(default_factory=dict)
1331
- chart_fields: dict = Field(default_factory=dict)
1332
- chart_strings: dict = Field(default_factory=dict)
1333
-
1334
- @classmethod
1335
- def from_table(
1336
- cls, table_name: str, chart_fields: dict = None, chart_strings: dict = None
1337
- ):
1338
- return cls(
1339
- query={"summaryTable": {"tableKey": table_name}},
1340
- chart_fields=chart_fields,
1341
- chart_strings=chart_strings,
1342
- )
1343
-
1344
- def to_model(self):
1345
- obj = internal.Vega2(
1346
- config=internal.Vega2Config(
1347
- # user_query=internal.UserQuery(
1348
- # query_fields=[
1349
- # internal.QueryField(
1350
- # args=...,
1351
- # fields=...,
1352
- # name=...,
1353
- # )
1354
- # ]
1355
- # )
1356
- ),
1357
- layout=self.layout.to_model(),
1358
- )
1359
- obj.ref = self._ref
1360
- # obj.id=self.id,
1361
- return obj
1362
-
1363
- @classmethod
1364
- def from_model(cls, model: internal.ScatterPlot):
1365
- obj = cls(
1366
- # query=model.config.user_query.query_fields,
1367
- # chart_name=model.config.panel_def_id,
1368
- # chart_fields=model.config.field_settings,
1369
- # chart_strings=model.config.string_settings,
1370
- layout=Layout.from_model(model.layout),
1371
- )
1372
- obj._ref = model.ref
1373
- return obj
1374
-
1375
-
1376
- @dataclass(config=ConfigDict(validate_assignment=True, extra="forbid", slots=True))
1377
- class UnknownPanel(Base):
1378
- def __repr__(self) -> str:
1379
- class_name = self.__class__.__name__
1380
- attributes = ", ".join(
1381
- f"{key}={value!r}" for key, value in self.__dict__.items()
1382
- )
1383
- return f"{class_name}({attributes})"
1384
-
1385
- def to_model(self):
1386
- d = self.__dict__
1387
- print(d)
1388
- return internal.UnknownPanel.model_validate(d)
1389
-
1390
- @classmethod
1391
- def from_model(cls, model: internal.UnknownPanel):
1392
- d = model.model_dump()
1393
- return cls(**d)
1394
-
1395
-
1396
- @dataclass(config=ConfigDict(validate_assignment=True, extra="forbid", slots=True))
1397
- class WeavePanel(Panel):
1398
- config: dict = Field(default_factory=dict)
1399
-
1400
- def to_model(self):
1401
- return internal.WeavePanel(config=self.config)
1402
-
1403
- @classmethod
1404
- def from_model(cls, model: internal.WeavePanel):
1405
- return cls(config=model.config)
1406
-
1407
-
1408
- @dataclass(config=dataclass_config)
1409
- class Report(Base):
1410
- project: str
1411
- entity: str = Field(default_factory=lambda: _get_api().default_entity)
1412
- title: str = Field("Untitled Report", max_length=128)
1413
- width: ReportWidth = "readable"
1414
- description: str = ""
1415
- blocks: LList[BlockTypes] = Field(default_factory=list)
1416
-
1417
- id: str = Field(default_factory=lambda: "", init=False, repr=False, kw_only=True)
1418
- _discussion_threads: list = Field(default_factory=list, init=False, repr=False)
1419
- _ref: dict = Field(default_factory=dict, init=False, repr=False)
1420
- _panel_settings: dict = Field(default_factory=dict, init=False, repr=False)
1421
- _authors: LList[dict] = Field(default_factory=list, init=False, repr=False)
1422
- _created_at: Optional[datetime] = Field(
1423
- default_factory=lambda: None, init=False, repr=False
1424
- )
1425
- _updated_at: Optional[datetime] = Field(
1426
- default_factory=lambda: None, init=False, repr=False
1427
- )
1428
-
1429
- def to_model(self):
1430
- blocks = self.blocks
1431
- if len(blocks) > 0 and blocks[0] != P():
1432
- blocks = [P()] + blocks
1433
-
1434
- if len(blocks) > 0 and blocks[-1] != P():
1435
- blocks = blocks + [P()]
1436
-
1437
- if not blocks:
1438
- blocks = [P(), P()]
1439
-
1440
- return internal.ReportViewspec(
1441
- display_name=self.title,
1442
- description=self.description,
1443
- project=internal.Project(name=self.project, entity_name=self.entity),
1444
- id=self.id,
1445
- created_at=self._created_at,
1446
- updated_at=self._updated_at,
1447
- spec=internal.Spec(
1448
- panel_settings=self._panel_settings,
1449
- blocks=[b.to_model() for b in blocks],
1450
- width=self.width,
1451
- authors=self._authors,
1452
- discussion_threads=self._discussion_threads,
1453
- ref=self._ref,
1454
- ),
1455
- )
1456
-
1457
- @classmethod
1458
- def from_model(cls, model: internal.ReportViewspec):
1459
- blocks = model.spec.blocks
1460
-
1461
- if blocks[0] == internal.Paragraph():
1462
- blocks = blocks[1:]
1463
-
1464
- if blocks[-1] == internal.Paragraph():
1465
- blocks = blocks[:-1]
1466
-
1467
- obj = cls(
1468
- title=model.display_name,
1469
- description=model.description,
1470
- entity=model.project.entity_name,
1471
- project=model.project.name,
1472
- blocks=[_lookup(b) for b in blocks],
1473
- )
1474
- obj.id = model.id
1475
- obj._discussion_threads = model.spec.discussion_threads
1476
- obj._panel_settings = model.spec.panel_settings
1477
- obj._ref = model.spec.ref
1478
- obj._authors = model.spec.authors
1479
- obj._created_at = model.created_at
1480
- obj._updated_at = model.updated_at
1481
-
1482
- return obj
1483
-
1484
- @property
1485
- def url(self):
1486
- if self.id == "":
1487
- raise AttributeError("save report or explicitly pass `id` to get a url")
1488
-
1489
- base = urlparse(_get_api().client.app_url)
1490
-
1491
- title = self.title.replace(" ", "-")
1492
-
1493
- scheme = base.scheme
1494
- netloc = base.netloc
1495
- path = os.path.join(self.entity, self.project, "reports", f"{title}--{self.id}")
1496
- params = ""
1497
- query = ""
1498
- fragment = ""
1499
-
1500
- return urlunparse((scheme, netloc, path, params, query, fragment))
1501
-
1502
- def save(self, draft: bool = False, clone: bool = False):
1503
- model = self.to_model()
1504
-
1505
- # create project if not exists
1506
- projects = _get_api().projects(self.entity)
1507
- is_new_project = True
1508
- for p in projects:
1509
- if p.name == self.project:
1510
- is_new_project = False
1511
- break
1512
-
1513
- if is_new_project:
1514
- _get_api().create_project(self.project, self.entity)
1515
-
1516
- r = _get_api().client.execute(
1517
- gql.upsert_view,
1518
- variable_values={
1519
- "id": None if clone or not model.id else model.id,
1520
- "name": internal._generate_name()
1521
- if clone or not model.name
1522
- else model.name,
1523
- "entityName": model.project.entity_name,
1524
- "projectName": model.project.name,
1525
- "description": model.description,
1526
- "displayName": model.display_name,
1527
- "type": "runs/draft" if draft else "runs",
1528
- "spec": model.spec.model_dump_json(by_alias=True, exclude_none=True),
1529
- },
1530
- )
1531
-
1532
- viewspec = r["upsertView"]["view"]
1533
- new_model = internal.ReportViewspec.model_validate(viewspec)
1534
- self.id = new_model.id
1535
-
1536
- wandb.termlog(f"Saved report to: {self.url}")
1537
- return self
1538
-
1539
- @classmethod
1540
- def from_url(cls, url, *, as_model: bool = False):
1541
- vs = _url_to_viewspec(url)
1542
- model = internal.ReportViewspec.model_validate(vs)
1543
- if as_model:
1544
- return model
1545
- return cls.from_model(model)
1546
-
1547
- def to_html(self, height: int = 1024, hidden: bool = False) -> str:
1548
- """Generate HTML containing an iframe displaying this report."""
1549
- try:
1550
- url = self.url + "?jupyter=true"
1551
- style = f"border:none;width:100%;height:{height}px;"
1552
- prefix = ""
1553
- if hidden:
1554
- style += "display:none;"
1555
- prefix = wandb.sdk.lib.ipython.toggle_button("report")
1556
- return prefix + f"<iframe src={url!r} style={style!r}></iframe>"
1557
- except AttributeError:
1558
- wandb.termlog("HTML repr will be available after you save the report!")
1559
-
1560
- def _repr_html_(self) -> str:
1561
- return self.to_html()
1562
-
1563
-
1564
- def _get_api():
1565
- try:
1566
- return wandb.Api()
1567
- except wandb.errors.UsageError as e:
1568
- raise Exception("not logged in to W&B, try `wandb login --relogin`") from e
1569
-
1570
-
1571
- def _url_to_viewspec(url):
1572
- report_id = _url_to_report_id(url)
1573
- r = _get_api().client.execute(
1574
- gql.view_report, variable_values={"reportId": report_id}
1575
- )
1576
- viewspec = r["view"]
1577
- return viewspec
1578
-
1579
-
1580
- def _url_to_report_id(url):
1581
- parse_result = urlparse(url)
1582
- path = parse_result.path
1583
-
1584
- _, entity, project, _, name = path.split("/")
1585
- title, report_id = name.split("--")
1586
-
1587
- return report_id
1588
-
1589
-
1590
- def _lookup(block):
1591
- cls = block_mapping.get(block.__class__, UnknownBlock)
1592
- return cls.from_model(block)
1593
-
1594
-
1595
- def _should_show_attr(k, v):
1596
- if k.startswith("_"):
1597
- return False
1598
- if k == "id":
1599
- return False
1600
- if v is None:
1601
- return False
1602
- if isinstance(v, Iterable) and not isinstance(v, (str, bytes, bytearray)):
1603
- return not all(x is None for x in v)
1604
- # ignore the default layout
1605
- if isinstance(v, Layout) and v.x == 0 and v.y == 0 and v.w == 8 and v.h == 6:
1606
- return False
1607
- return True
1608
-
1609
-
1610
- def _listify(x):
1611
- if isinstance(x, Iterable):
1612
- return list(x)
1613
- return [x]
1614
-
1615
-
1616
- def _lookup_panel(panel):
1617
- cls = panel_mapping.get(panel.__class__, UnknownPanel)
1618
- return cls.from_model(panel)
1619
-
1620
-
1621
- def _load_spec_from_url(url, as_model=False):
1622
- import json
1623
-
1624
- vs = _url_to_viewspec(url)
1625
- spec = vs["spec"]
1626
- if as_model:
1627
- return internal.Spec.model_validate_json(spec)
1628
- return json.loads(spec)
1629
-
1630
-
1631
- panel_mapping = {
1632
- internal.LinePlot: LinePlot,
1633
- internal.ScatterPlot: ScatterPlot,
1634
- internal.BarPlot: BarPlot,
1635
- internal.ScalarChart: ScalarChart,
1636
- internal.CodeComparer: CodeComparer,
1637
- internal.ParallelCoordinatesPlot: ParallelCoordinatesPlot,
1638
- internal.ParameterImportancePlot: ParameterImportancePlot,
1639
- internal.RunComparer: RunComparer,
1640
- internal.Vega2: CustomChart,
1641
- internal.WeavePanel: WeavePanel,
1642
- internal.MediaBrowser: MediaBrowser,
1643
- internal.MarkdownPanel: MarkdownPanel,
1644
- }
1645
-
1646
- PanelTypes = Union[
1647
- LinePlot,
1648
- ScatterPlot,
1649
- ScalarChart,
1650
- BarPlot,
1651
- CodeComparer,
1652
- ParallelCoordinatesPlot,
1653
- ParameterImportancePlot,
1654
- RunComparer,
1655
- MediaBrowser,
1656
- MarkdownPanel,
1657
- CustomChart,
1658
- WeavePanel,
1659
- UnknownPanel,
1660
- ]
1661
-
1662
-
1663
- def _text_to_internal_children(text_field):
1664
- text = text_field
1665
- if text == []:
1666
- text = ""
1667
- # if isinstance(text, str):
1668
- if not isinstance(text, list):
1669
- text = [text]
1670
-
1671
- texts = []
1672
- for x in text:
1673
- t = None
1674
- if isinstance(x, str):
1675
- t = internal.Text(text=x)
1676
- elif isinstance(x, TextWithInlineComments):
1677
- t = internal.Text(text=x.text, inline_comments=x._inline_comments)
1678
- elif isinstance(x, Link):
1679
- txt = x.text
1680
- if isinstance(txt, str):
1681
- children = [internal.Text(text=txt)]
1682
- elif isinstance(txt, TextWithInlineComments):
1683
- children = [
1684
- internal.Text(text=txt.text, inline_comments=txt._inline_comments)
1685
- ]
1686
- t = internal.InlineLink(url=x.url, children=children)
1687
- elif isinstance(x, InlineLatex):
1688
- t = internal.InlineLatex(content=x.text)
1689
- elif isinstance(x, InlineCode):
1690
- t = internal.Text(text=x.text, inline_code=True)
1691
- texts.append(t)
1692
- if not all(isinstance(x, str) for x in texts):
1693
- pass
1694
- return texts
1695
-
1696
-
1697
- def _generate_thing(x):
1698
- if isinstance(x, internal.Paragraph):
1699
- return _internal_children_to_text(x.children)
1700
- elif isinstance(x, internal.Text):
1701
- if x.inline_code:
1702
- return InlineCode(x.text)
1703
- elif x.inline_comments:
1704
- return TextWithInlineComments(
1705
- text=x.text, _inline_comments=x.inline_comments
1706
- )
1707
- return x.text
1708
- elif isinstance(x, internal.InlineLink):
1709
- text_obj = x.children[0]
1710
- if text_obj.inline_comments:
1711
- text = TextWithInlineComments(
1712
- text=text_obj.text, _inline_comments=text_obj.inline_comments
1713
- )
1714
- else:
1715
- text = text_obj.text
1716
- return Link(url=x.url, text=text)
1717
- elif isinstance(x, internal.InlineLatex):
1718
- return InlineLatex(text=x.content)
1719
-
1720
-
1721
- def _internal_children_to_text(children):
1722
- pieces = []
1723
- for x in children:
1724
- t = _generate_thing(x)
1725
- if isinstance(t, list):
1726
- for x in t:
1727
- pieces.append(x)
1728
- else:
1729
- pieces.append(t)
1730
-
1731
- if not pieces:
1732
- return ""
1733
-
1734
- if len(pieces) == 1 and isinstance(pieces[0], str):
1735
- return pieces[0]
1736
-
1737
- if len(pieces) == 3 and pieces[0] == "" and pieces[-1] == "":
1738
- return pieces[1]
1739
-
1740
- if len(pieces) >= 3 and pieces[0] == "" and pieces[-1] == "":
1741
- return pieces[1:-1]
1742
-
1743
- if all(x == "" for x in pieces):
1744
- return ""
1745
-
1746
- return pieces
1747
-
1748
-
1749
- def _resolve_collisions(panels: LList[Panel], x_max: int = 24):
1750
- for i, p1 in enumerate(panels):
1751
- for p2 in panels[i + 1 :]:
1752
- l1, l2 = p1.layout, p2.layout
1753
-
1754
- if _collides(p1, p2):
1755
- x = l1.x + l1.w - l2.x
1756
- y = l1.y + l1.h - l2.y
1757
-
1758
- if l2.x + l2.w + x <= x_max:
1759
- l2.x += x
1760
-
1761
- else:
1762
- l2.y += y
1763
- l2.x = 0
1764
- return panels
1765
-
1766
-
1767
- def _collides(p1: Panel, p2: Panel) -> bool:
1768
- l1, l2 = p1.layout, p2.layout
1769
-
1770
- if (
1771
- (p1.id == p2.id)
1772
- or (l1.x + l1.w <= l2.x)
1773
- or (l1.x >= l2.w + l2.x)
1774
- or (l1.y + l1.h <= l2.y)
1775
- or (l1.y >= l2.y + l2.h)
1776
- ):
1777
- return False
1778
-
1779
- return True
1780
-
1781
-
1782
- def _metric_to_backend(x: Optional[MetricType]):
1783
- if x is None:
1784
- return x
1785
- if isinstance(x, str): # Same as Metric
1786
- return expr_parsing.to_backend_name(x)
1787
- if isinstance(x, Metric):
1788
- name = x.name
1789
- return expr_parsing.to_backend_name(name)
1790
- if isinstance(x, Config):
1791
- name, *rest = x.name.split(".")
1792
- rest = "." + ".".join(rest) if rest else ""
1793
- return f"config.{name}.value{rest}"
1794
- if isinstance(x, SummaryMetric):
1795
- name = x.name
1796
- return f"summary_metrics.{name}"
1797
- raise Exception("Unexpected metric type")
1798
-
1799
-
1800
- def _metric_to_frontend(x: str):
1801
- if x is None:
1802
- return x
1803
- if x.startswith("config.") and ".value" in x:
1804
- name = x.replace("config.", "").replace(".value", "")
1805
- return Config(name)
1806
- if x.startswith("summary_metrics."):
1807
- name = x.replace("summary_metrics.", "")
1808
- return SummaryMetric(name)
1809
-
1810
- name = expr_parsing.to_frontend_name(x)
1811
- return Metric(name)
1812
-
1813
-
1814
- def _metric_to_backend_pc(x: Optional[ParallelCoordinatesMetric]):
1815
- if x is None:
1816
- return x
1817
- if isinstance(x, str): # Same as SummaryMetric
1818
- name = x
1819
- return f"summary:{name}"
1820
- if isinstance(x, Config):
1821
- name = x.name
1822
- return f"c::{name}"
1823
- if isinstance(x, SummaryMetric):
1824
- name = x.name
1825
- return f"summary:{name}"
1826
- raise Exception("Unexpected metric type")
1827
-
1828
-
1829
- def _metric_to_frontend_pc(x: str):
1830
- if x is None:
1831
- return x
1832
- if x.startswith("c::"):
1833
- name = x.replace("c::", "")
1834
- return Config(name)
1835
- if x.startswith("summary:"):
1836
- name = x.replace("summary:", "")
1837
- return SummaryMetric(name)
1838
-
1839
- name = expr_parsing.to_frontend_name(x)
1840
- return Metric(name)
1841
-
1842
-
1843
- def _metric_to_backend_panel_grid(x: Optional[MetricType]):
1844
- if isinstance(x, str):
1845
- name, *rest = x.split(".")
1846
- rest = "." + ".".join(rest) if rest else ""
1847
- return f"config:{name}.value{rest}"
1848
- return _metric_to_backend(x)
1849
-
1850
-
1851
- def _metric_to_frontend_panel_grid(x: str):
1852
- if x.startswith("config:") and ".value" in x:
1853
- name = x.replace("config:", "").replace(".value", "")
1854
- return Config(name)
1855
- return _metric_to_frontend(x)
1856
-
1857
-
1858
- def _get_rs_by_name(runsets, name):
1859
- for rs in runsets:
1860
- if rs.name == name:
1861
- return rs
1862
-
1863
-
1864
- def _get_rs_by_id(runsets, id):
1865
- for rs in runsets:
1866
- if rs._id == id:
1867
- return rs
1868
-
1869
-
1870
- def _to_color_dict(custom_run_colors, runsets):
1871
- d = {}
1872
- for k, v in custom_run_colors.items():
1873
- if isinstance(k, RunsetGroup):
1874
- rs = _get_rs_by_name(runsets, k.runset_name)
1875
- if not rs:
1876
- continue
1877
- id = rs._id
1878
- kvs = []
1879
- for keys in k.keys:
1880
- kk = _metric_to_backend_panel_grid(keys.key)
1881
- vv = keys.value
1882
- kv = f"{kk}:{vv}"
1883
- kvs.append(kv)
1884
- linked = "-".join(kvs)
1885
- key = f"{id}-{linked}"
1886
- else:
1887
- key = k
1888
- d[key] = v
1889
-
1890
- return d
1891
-
1892
-
1893
- def _from_color_dict(d, runsets):
1894
- d2 = {}
1895
- for k, v in d.items():
1896
- id, *backend_parts = k.split("-")
1897
-
1898
- if backend_parts:
1899
- groups = []
1900
- for part in backend_parts:
1901
- key, value = part.rsplit(":", 1)
1902
- kkey = _metric_to_frontend_panel_grid(key)
1903
- group = RunsetGroupKey(kkey, value)
1904
- groups.append(group)
1905
- rs = _get_rs_by_id(runsets, id)
1906
- rg = RunsetGroup(runset_name=rs.name, keys=groups)
1907
- new_key = rg
1908
- else:
1909
- new_key = k
1910
- d2[new_key] = v
1911
- return d2