fh-pydantic-form 0.2.5__py3-none-any.whl → 0.3.0__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.

Potentially problematic release.


This version of fh-pydantic-form might be problematic. Click here for more details.

@@ -4,6 +4,15 @@ from typing import Literal, get_origin
4
4
 
5
5
  from pydantic import BaseModel
6
6
 
7
+ from fh_pydantic_form.comparison_form import (
8
+ ComparisonForm,
9
+ comparison_form_js,
10
+ simple_diff_metrics,
11
+ )
12
+ from fh_pydantic_form.defaults import (
13
+ default_dict_for_model,
14
+ default_for_annotation,
15
+ )
7
16
  from fh_pydantic_form.field_renderers import (
8
17
  BaseModelFieldRenderer,
9
18
  BooleanFieldRenderer,
@@ -18,6 +27,8 @@ from fh_pydantic_form.field_renderers import (
18
27
  from fh_pydantic_form.form_renderer import PydanticForm, list_manipulation_js
19
28
  from fh_pydantic_form.registry import FieldRendererRegistry
20
29
  from fh_pydantic_form.type_helpers import (
30
+ MetricEntry,
31
+ MetricsDict,
21
32
  _get_underlying_type_if_optional,
22
33
  )
23
34
  from fh_pydantic_form.ui_style import (
@@ -26,10 +37,6 @@ from fh_pydantic_form.ui_style import (
26
37
  spacing,
27
38
  spacing_many,
28
39
  )
29
- from fh_pydantic_form.defaults import (
30
- default_dict_for_model,
31
- default_for_annotation,
32
- )
33
40
 
34
41
 
35
42
  def register_default_renderers() -> None:
@@ -122,4 +129,9 @@ __all__ = [
122
129
  "spacing_many",
123
130
  "default_dict_for_model",
124
131
  "default_for_annotation",
132
+ "ComparisonForm",
133
+ "MetricEntry",
134
+ "MetricsDict",
135
+ "comparison_form_js",
136
+ "simple_diff_metrics",
125
137
  ]
@@ -0,0 +1,598 @@
1
+ """
2
+ Color utility functions for fh-pydantic-form
3
+
4
+ This module provides robust color parsing and conversion utilities that support
5
+ various color formats including hex, RGB, HSL, named colors, and Tailwind CSS classes.
6
+ """
7
+
8
+ __all__ = ["robust_color_to_rgba", "get_metric_colors", "DEFAULT_METRIC_GREY"]
9
+
10
+ import re
11
+ from typing import Tuple
12
+
13
+ DEFAULT_METRIC_GREY = "rgba(200, 200, 200, 0.5)"
14
+
15
+
16
+ def robust_color_to_rgba(color: str, opacity: float = 1.0) -> str:
17
+ """
18
+ Convert any color format to rgba with specified opacity.
19
+
20
+ Supports:
21
+ - Hex colors: #FF0000, #F00, #ff0000, #f00
22
+ - RGB colors: rgb(255, 0, 0), rgb(255,0,0)
23
+ - RGBA colors: rgba(255, 0, 0, 0.5)
24
+ - HSL colors: hsl(0, 100%, 50%)
25
+ - HSLA colors: hsla(0, 100%, 50%, 0.5)
26
+ - Named colors: red, blue, green, etc.
27
+ - Tailwind classes: text-red-500, bg-blue-600, etc.
28
+
29
+ Args:
30
+ color: The color string in any supported format
31
+ opacity: Opacity value (0.0 to 1.0)
32
+
33
+ Returns:
34
+ RGBA color string in format "rgba(r, g, b, opacity)"
35
+ """
36
+ if not color:
37
+ return f"rgba(128, 128, 128, {opacity})" # Default gray
38
+
39
+ color = color.strip()
40
+
41
+ # Handle hex colors
42
+ if color.startswith("#"):
43
+ hex_color = color.lstrip("#")
44
+ if len(hex_color) == 3:
45
+ # Convert 3-digit hex to 6-digit (e.g., #f00 -> #ff0000)
46
+ hex_color = "".join([c * 2 for c in hex_color])
47
+ elif len(hex_color) == 6:
48
+ pass # Already 6-digit hex
49
+ else:
50
+ return f"rgba(128, 128, 128, {opacity})" # Invalid hex
51
+
52
+ try:
53
+ r = int(hex_color[0:2], 16)
54
+ g = int(hex_color[2:4], 16)
55
+ b = int(hex_color[4:6], 16)
56
+ return f"rgba({r}, {g}, {b}, {opacity})"
57
+ except ValueError:
58
+ return f"rgba(128, 128, 128, {opacity})"
59
+
60
+ # Handle rgb() and rgba() functions
61
+ if color.startswith(("rgb(", "rgba(")):
62
+ # Extract numbers from rgb/rgba string
63
+ nums = re.findall(r"\d*\.?\d+", color)
64
+ if len(nums) >= 3:
65
+ r, g, b = int(float(nums[0])), int(float(nums[1])), int(float(nums[2]))
66
+ return f"rgba({r}, {g}, {b}, {opacity})"
67
+ return f"rgba(128, 128, 128, {opacity})"
68
+
69
+ # Handle hsl() and hsla() functions
70
+ if color.startswith(("hsl(", "hsla(")):
71
+ # For HSL, we'll convert to RGB first
72
+ # Extract numbers from hsl/hsla string
73
+ nums = re.findall(r"\d*\.?\d+", color)
74
+ if len(nums) >= 3:
75
+ h = float(nums[0]) / 360.0 # Convert to 0-1 range
76
+ s = float(nums[1]) / 100.0 # Convert percentage to 0-1
77
+ lightness = float(nums[2]) / 100.0 # Convert percentage to 0-1
78
+
79
+ # Convert HSL to RGB using standard algorithm
80
+ r, g, b = _hsl_to_rgb(h, s, lightness)
81
+ return f"rgba({r}, {g}, {b}, {opacity})"
82
+ return f"rgba(128, 128, 128, {opacity})"
83
+
84
+ # Handle Tailwind CSS classes (e.g., text-red-500, bg-blue-600, border-green-400)
85
+ tailwind_match = re.match(r"(?:text-|bg-|border-)?(\w+)-(\d+)", color)
86
+ if tailwind_match:
87
+ color_name, intensity = tailwind_match.groups()
88
+
89
+ if color_name in TAILWIND_COLORS and intensity in TAILWIND_COLORS[color_name]:
90
+ r, g, b = TAILWIND_COLORS[color_name][intensity]
91
+ return f"rgba({r}, {g}, {b}, {opacity})"
92
+
93
+ # Handle named CSS colors
94
+ if color.lower() in NAMED_COLORS:
95
+ r, g, b = NAMED_COLORS[color.lower()]
96
+ return f"rgba({r}, {g}, {b}, {opacity})"
97
+
98
+ # Fallback for unrecognized colors
99
+ return f"rgba(128, 128, 128, {opacity})"
100
+
101
+
102
+ def _hsl_to_rgb(h: float, s: float, lightness: float) -> Tuple[int, int, int]:
103
+ """
104
+ Convert HSL color values to RGB.
105
+
106
+ Args:
107
+ h: Hue (0.0 to 1.0)
108
+ s: Saturation (0.0 to 1.0)
109
+ lightness: Lightness (0.0 to 1.0)
110
+
111
+ Returns:
112
+ RGB tuple with values 0-255
113
+ """
114
+ if s == 0:
115
+ r = g = b = lightness # Achromatic
116
+ else:
117
+
118
+ def hue_to_rgb(p: float, q: float, t: float) -> float:
119
+ if t < 0:
120
+ t += 1
121
+ if t > 1:
122
+ t -= 1
123
+ if t < 1 / 6:
124
+ return p + (q - p) * 6 * t
125
+ if t < 1 / 2:
126
+ return q
127
+ if t < 2 / 3:
128
+ return p + (q - p) * (2 / 3 - t) * 6
129
+ return p
130
+
131
+ q = lightness * (1 + s) if lightness < 0.5 else lightness + s - lightness * s
132
+ p = 2 * lightness - q
133
+ r = hue_to_rgb(p, q, h + 1 / 3)
134
+ g = hue_to_rgb(p, q, h)
135
+ b = hue_to_rgb(p, q, h - 1 / 3)
136
+
137
+ return int(r * 255), int(g * 255), int(b * 255)
138
+
139
+
140
+ # Tailwind CSS color palette with RGB values
141
+ TAILWIND_COLORS = {
142
+ "red": {
143
+ "50": (254, 242, 242),
144
+ "100": (254, 226, 226),
145
+ "200": (254, 202, 202),
146
+ "300": (252, 165, 165),
147
+ "400": (248, 113, 113),
148
+ "500": (239, 68, 68),
149
+ "600": (220, 38, 38),
150
+ "700": (185, 28, 28),
151
+ "800": (153, 27, 27),
152
+ "900": (127, 29, 29),
153
+ "950": (69, 10, 10),
154
+ },
155
+ "green": {
156
+ "50": (240, 253, 244),
157
+ "100": (220, 252, 231),
158
+ "200": (187, 247, 208),
159
+ "300": (134, 239, 172),
160
+ "400": (74, 222, 128),
161
+ "500": (34, 197, 94),
162
+ "600": (22, 163, 74),
163
+ "700": (21, 128, 61),
164
+ "800": (22, 101, 52),
165
+ "900": (20, 83, 45),
166
+ "950": (5, 46, 22),
167
+ },
168
+ "blue": {
169
+ "50": (239, 246, 255),
170
+ "100": (219, 234, 254),
171
+ "200": (191, 219, 254),
172
+ "300": (147, 197, 253),
173
+ "400": (96, 165, 250),
174
+ "500": (59, 130, 246),
175
+ "600": (37, 99, 235),
176
+ "700": (29, 78, 216),
177
+ "800": (30, 64, 175),
178
+ "900": (30, 58, 138),
179
+ "950": (23, 37, 84),
180
+ },
181
+ "yellow": {
182
+ "50": (254, 252, 232),
183
+ "100": (254, 249, 195),
184
+ "200": (254, 240, 138),
185
+ "300": (253, 224, 71),
186
+ "400": (250, 204, 21),
187
+ "500": (234, 179, 8),
188
+ "600": (202, 138, 4),
189
+ "700": (161, 98, 7),
190
+ "800": (133, 77, 14),
191
+ "900": (113, 63, 18),
192
+ "950": (66, 32, 6),
193
+ },
194
+ "purple": {
195
+ "50": (250, 245, 255),
196
+ "100": (243, 232, 255),
197
+ "200": (233, 213, 255),
198
+ "300": (216, 180, 254),
199
+ "400": (196, 143, 253),
200
+ "500": (168, 85, 247),
201
+ "600": (147, 51, 234),
202
+ "700": (126, 34, 206),
203
+ "800": (107, 33, 168),
204
+ "900": (88, 28, 135),
205
+ "950": (59, 7, 100),
206
+ },
207
+ "orange": {
208
+ "50": (255, 247, 237),
209
+ "100": (255, 237, 213),
210
+ "200": (254, 215, 170),
211
+ "300": (253, 186, 116),
212
+ "400": (251, 146, 60),
213
+ "500": (249, 115, 22),
214
+ "600": (234, 88, 12),
215
+ "700": (194, 65, 12),
216
+ "800": (154, 52, 18),
217
+ "900": (124, 45, 18),
218
+ "950": (67, 20, 7),
219
+ },
220
+ "pink": {
221
+ "50": (253, 242, 248),
222
+ "100": (252, 231, 243),
223
+ "200": (251, 207, 232),
224
+ "300": (249, 168, 212),
225
+ "400": (244, 114, 182),
226
+ "500": (236, 72, 153),
227
+ "600": (219, 39, 119),
228
+ "700": (190, 24, 93),
229
+ "800": (157, 23, 77),
230
+ "900": (131, 24, 67),
231
+ "950": (80, 7, 36),
232
+ },
233
+ "indigo": {
234
+ "50": (238, 242, 255),
235
+ "100": (224, 231, 255),
236
+ "200": (199, 210, 254),
237
+ "300": (165, 180, 252),
238
+ "400": (129, 140, 248),
239
+ "500": (99, 102, 241),
240
+ "600": (79, 70, 229),
241
+ "700": (67, 56, 202),
242
+ "800": (55, 48, 163),
243
+ "900": (49, 46, 129),
244
+ "950": (30, 27, 75),
245
+ },
246
+ "teal": {
247
+ "50": (240, 253, 250),
248
+ "100": (204, 251, 241),
249
+ "200": (153, 246, 228),
250
+ "300": (94, 234, 212),
251
+ "400": (45, 212, 191),
252
+ "500": (20, 184, 166),
253
+ "600": (13, 148, 136),
254
+ "700": (15, 118, 110),
255
+ "800": (17, 94, 89),
256
+ "900": (19, 78, 74),
257
+ "950": (4, 47, 46),
258
+ },
259
+ # Extended Tailwind colors
260
+ "gray": {
261
+ "50": (249, 250, 251),
262
+ "100": (243, 244, 246),
263
+ "200": (229, 231, 235),
264
+ "300": (209, 213, 219),
265
+ "400": (156, 163, 175),
266
+ "500": (107, 114, 128),
267
+ "600": (75, 85, 99),
268
+ "700": (55, 65, 81),
269
+ "800": (31, 41, 55),
270
+ "900": (17, 24, 39),
271
+ "950": (3, 7, 18),
272
+ },
273
+ "slate": {
274
+ "50": (248, 250, 252),
275
+ "100": (241, 245, 249),
276
+ "200": (226, 232, 240),
277
+ "300": (203, 213, 225),
278
+ "400": (148, 163, 184),
279
+ "500": (100, 116, 139),
280
+ "600": (71, 85, 105),
281
+ "700": (51, 65, 85),
282
+ "800": (30, 41, 59),
283
+ "900": (15, 23, 42),
284
+ "950": (2, 6, 23),
285
+ },
286
+ "zinc": {
287
+ "50": (250, 250, 250),
288
+ "100": (244, 244, 245),
289
+ "200": (228, 228, 231),
290
+ "300": (212, 212, 216),
291
+ "400": (161, 161, 170),
292
+ "500": (113, 113, 122),
293
+ "600": (82, 82, 91),
294
+ "700": (63, 63, 70),
295
+ "800": (39, 39, 42),
296
+ "900": (24, 24, 27),
297
+ "950": (9, 9, 11),
298
+ },
299
+ "neutral": {
300
+ "50": (250, 250, 250),
301
+ "100": (245, 245, 245),
302
+ "200": (229, 229, 229),
303
+ "300": (212, 212, 212),
304
+ "400": (163, 163, 163),
305
+ "500": (115, 115, 115),
306
+ "600": (82, 82, 82),
307
+ "700": (64, 64, 64),
308
+ "800": (38, 38, 38),
309
+ "900": (23, 23, 23),
310
+ "950": (10, 10, 10),
311
+ },
312
+ "stone": {
313
+ "50": (250, 250, 249),
314
+ "100": (245, 245, 244),
315
+ "200": (231, 229, 228),
316
+ "300": (214, 211, 209),
317
+ "400": (168, 162, 158),
318
+ "500": (120, 113, 108),
319
+ "600": (87, 83, 78),
320
+ "700": (68, 64, 60),
321
+ "800": (41, 37, 36),
322
+ "900": (28, 25, 23),
323
+ "950": (12, 10, 9),
324
+ },
325
+ # Additional colors
326
+ "emerald": {
327
+ "50": (236, 253, 245),
328
+ "100": (209, 250, 229),
329
+ "200": (167, 243, 208),
330
+ "300": (110, 231, 183),
331
+ "400": (52, 211, 153),
332
+ "500": (16, 185, 129),
333
+ "600": (5, 150, 105),
334
+ "700": (4, 120, 87),
335
+ "800": (6, 95, 70),
336
+ "900": (6, 78, 59),
337
+ "950": (2, 44, 34),
338
+ },
339
+ "lime": {
340
+ "50": (247, 254, 231),
341
+ "100": (236, 252, 203),
342
+ "200": (217, 249, 157),
343
+ "300": (190, 242, 100),
344
+ "400": (163, 230, 53),
345
+ "500": (132, 204, 22),
346
+ "600": (101, 163, 13),
347
+ "700": (77, 124, 15),
348
+ "800": (63, 98, 18),
349
+ "900": (54, 83, 20),
350
+ "950": (26, 46, 5),
351
+ },
352
+ "cyan": {
353
+ "50": (236, 254, 255),
354
+ "100": (207, 250, 254),
355
+ "200": (165, 243, 252),
356
+ "300": (103, 232, 249),
357
+ "400": (34, 211, 238),
358
+ "500": (6, 182, 212),
359
+ "600": (8, 145, 178),
360
+ "700": (14, 116, 144),
361
+ "800": (21, 94, 117),
362
+ "900": (22, 78, 99),
363
+ "950": (8, 51, 68),
364
+ },
365
+ "sky": {
366
+ "50": (240, 249, 255),
367
+ "100": (224, 242, 254),
368
+ "200": (186, 230, 253),
369
+ "300": (125, 211, 252),
370
+ "400": (56, 189, 248),
371
+ "500": (14, 165, 233),
372
+ "600": (2, 132, 199),
373
+ "700": (3, 105, 161),
374
+ "800": (7, 89, 133),
375
+ "900": (12, 74, 110),
376
+ "950": (8, 47, 73),
377
+ },
378
+ "violet": {
379
+ "50": (245, 243, 255),
380
+ "100": (237, 233, 254),
381
+ "200": (221, 214, 254),
382
+ "300": (196, 181, 253),
383
+ "400": (167, 139, 250),
384
+ "500": (139, 92, 246),
385
+ "600": (124, 58, 237),
386
+ "700": (109, 40, 217),
387
+ "800": (91, 33, 182),
388
+ "900": (76, 29, 149),
389
+ "950": (46, 16, 101),
390
+ },
391
+ "fuchsia": {
392
+ "50": (253, 244, 255),
393
+ "100": (250, 232, 255),
394
+ "200": (245, 208, 254),
395
+ "300": (240, 171, 252),
396
+ "400": (232, 121, 249),
397
+ "500": (217, 70, 239),
398
+ "600": (192, 38, 211),
399
+ "700": (162, 28, 175),
400
+ "800": (134, 25, 143),
401
+ "900": (112, 26, 117),
402
+ "950": (74, 4, 78),
403
+ },
404
+ "rose": {
405
+ "50": (255, 241, 242),
406
+ "100": (255, 228, 230),
407
+ "200": (254, 205, 211),
408
+ "300": (253, 164, 175),
409
+ "400": (251, 113, 133),
410
+ "500": (244, 63, 94),
411
+ "600": (225, 29, 72),
412
+ "700": (190, 18, 60),
413
+ "800": (159, 18, 57),
414
+ "900": (136, 19, 55),
415
+ "950": (76, 5, 25),
416
+ },
417
+ "amber": {
418
+ "50": (255, 251, 235),
419
+ "100": (254, 243, 199),
420
+ "200": (253, 230, 138),
421
+ "300": (252, 211, 77),
422
+ "400": (251, 191, 36),
423
+ "500": (245, 158, 11),
424
+ "600": (217, 119, 6),
425
+ "700": (180, 83, 9),
426
+ "800": (146, 64, 14),
427
+ "900": (120, 53, 15),
428
+ "950": (69, 26, 3),
429
+ },
430
+ }
431
+
432
+ # Named CSS colors (cleaned up to remove duplicates)
433
+ NAMED_COLORS = {
434
+ "red": (255, 0, 0),
435
+ "green": (0, 128, 0),
436
+ "blue": (0, 0, 255),
437
+ "yellow": (255, 255, 0),
438
+ "cyan": (0, 255, 255),
439
+ "magenta": (255, 0, 255),
440
+ "black": (0, 0, 0),
441
+ "white": (255, 255, 255),
442
+ "gray": (128, 128, 128),
443
+ "orange": (255, 165, 0),
444
+ "purple": (128, 0, 128),
445
+ "brown": (165, 42, 42),
446
+ "pink": (255, 192, 203),
447
+ "lime": (0, 255, 0),
448
+ "navy": (0, 0, 128),
449
+ "teal": (0, 128, 128),
450
+ "silver": (192, 192, 192),
451
+ "gold": (255, 215, 0),
452
+ "maroon": (128, 0, 0),
453
+ "olive": (128, 128, 0),
454
+ "aqua": (0, 255, 255),
455
+ "fuchsia": (255, 0, 255),
456
+ "coral": (255, 127, 80),
457
+ "crimson": (220, 20, 60),
458
+ "darkblue": (0, 0, 139),
459
+ "darkgreen": (0, 100, 0),
460
+ "darkred": (139, 0, 0),
461
+ "lightblue": (173, 216, 230),
462
+ "lightgreen": (144, 238, 144),
463
+ "lightgray": (211, 211, 211),
464
+ "darkgray": (169, 169, 169),
465
+ "lightcoral": (240, 128, 128),
466
+ "lightyellow": (255, 255, 224),
467
+ "lightpink": (255, 182, 193),
468
+ "lightcyan": (224, 255, 255),
469
+ "darkcyan": (0, 139, 139),
470
+ "darkmagenta": (139, 0, 139),
471
+ "darkorange": (255, 140, 0),
472
+ "darkviolet": (148, 0, 211),
473
+ "indigo": (75, 0, 130),
474
+ "violet": (238, 130, 238),
475
+ "turquoise": (64, 224, 208),
476
+ "slateblue": (106, 90, 205),
477
+ "steelblue": (70, 130, 180),
478
+ "royalblue": (65, 105, 225),
479
+ "mediumblue": (0, 0, 205),
480
+ "dodgerblue": (30, 144, 255),
481
+ "skyblue": (135, 206, 235),
482
+ "paleblue": (175, 238, 238),
483
+ "powderblue": (176, 224, 230),
484
+ "cadetblue": (95, 158, 160),
485
+ "forestgreen": (34, 139, 34),
486
+ "seagreen": (46, 139, 87),
487
+ "mediumseagreen": (60, 179, 113),
488
+ "springgreen": (0, 255, 127),
489
+ "limegreen": (50, 205, 50),
490
+ "yellowgreen": (154, 205, 50),
491
+ "darkolivegreen": (85, 107, 47),
492
+ "olivedrab": (107, 142, 35),
493
+ "lawngreen": (124, 252, 0),
494
+ "chartreuse": (127, 255, 0),
495
+ "greenyellow": (173, 255, 47),
496
+ "darkkhaki": (189, 183, 107),
497
+ "khaki": (240, 230, 140),
498
+ "palegoldenrod": (238, 232, 170),
499
+ "lightgoldenrodyellow": (250, 250, 210),
500
+ "papayawhip": (255, 239, 213),
501
+ "moccasin": (255, 228, 181),
502
+ "peachpuff": (255, 218, 185),
503
+ "sandybrown": (244, 164, 96),
504
+ "navajowhite": (255, 222, 173),
505
+ "wheat": (245, 222, 179),
506
+ "burlywood": (222, 184, 135),
507
+ "tan": (210, 180, 140),
508
+ "rosybrown": (188, 143, 143),
509
+ "darkgoldenrod": (184, 134, 11),
510
+ "goldenrod": (218, 165, 32),
511
+ "salmon": (250, 128, 114),
512
+ "darksalmon": (233, 150, 122),
513
+ "lightsalmon": (255, 160, 122),
514
+ "indianred": (205, 92, 92),
515
+ "firebrick": (178, 34, 34),
516
+ "mediumvioletred": (199, 21, 133),
517
+ "deeppink": (255, 20, 147),
518
+ "hotpink": (255, 105, 180),
519
+ "palevioletred": (219, 112, 147),
520
+ "mediumorchid": (186, 85, 211),
521
+ "darkorchid": (153, 50, 204),
522
+ "blueviolet": (138, 43, 226),
523
+ "mediumpurple": (147, 112, 219),
524
+ "thistle": (216, 191, 216),
525
+ "plum": (221, 160, 221),
526
+ "orchid": (218, 112, 214),
527
+ "mediumslateblue": (123, 104, 238),
528
+ "darkslateblue": (72, 61, 139),
529
+ "lavender": (230, 230, 250),
530
+ "ghostwhite": (248, 248, 255),
531
+ "aliceblue": (240, 248, 255),
532
+ "azure": (240, 255, 255),
533
+ "mintcream": (245, 255, 250),
534
+ "honeydew": (240, 255, 240),
535
+ "ivory": (255, 255, 240),
536
+ "floralwhite": (255, 250, 240),
537
+ "snow": (255, 250, 250),
538
+ "mistyrose": (255, 228, 225),
539
+ "seashell": (255, 245, 238),
540
+ "oldlace": (253, 245, 230),
541
+ "linen": (250, 240, 230),
542
+ "antiquewhite": (250, 235, 215),
543
+ "beige": (245, 245, 220),
544
+ "whitesmoke": (245, 245, 245),
545
+ "lavenderblush": (255, 240, 245),
546
+ "dimgray": (105, 105, 105),
547
+ "gainsboro": (220, 220, 220),
548
+ "lightslategray": (119, 136, 153),
549
+ "slategray": (112, 128, 144),
550
+ "darkslategray": (47, 79, 79),
551
+ "lightsteelblue": (176, 196, 222),
552
+ "cornflowerblue": (100, 149, 237),
553
+ }
554
+
555
+
556
+ def get_metric_colors(metric_value: float | int | str) -> Tuple[str, str]:
557
+ """
558
+ Get background and text colors based on a metric value (0.0 to 1.0).
559
+
560
+ Uses a LangSmith-style color system where:
561
+ - 0.0: Bright red (failure)
562
+ - 0.0 < x < 0.5: Dark red (poor)
563
+ - 0.5 <= x < 0.9: Medium/Forest green (moderate to high)
564
+ - 0.9 <= x < 1.0: Medium green (high)
565
+ - 1.0: Bright green (perfect)
566
+
567
+ Args:
568
+ metric_value: The metric value as a float, int, or string
569
+
570
+ Returns:
571
+ Tuple of (background_color, text_color) as hex color strings
572
+ """
573
+ if not isinstance(metric_value, (float, int, str)):
574
+ # Fallback for non-numeric values
575
+ return DEFAULT_METRIC_GREY, "white" # unified fallback
576
+
577
+ # Try to convert to float if it's a string
578
+ try:
579
+ value = float(metric_value)
580
+ except (ValueError, TypeError):
581
+ # Fallback for non-convertible strings
582
+ return DEFAULT_METRIC_GREY, "white" # unified fallback
583
+
584
+ if value == 0.0:
585
+ # Bright red bullet/white text for failure values
586
+ return "#D32F2F", "white" # Crimson
587
+ elif value > 0.0 and value < 0.5:
588
+ # Dark red bullet/light red text for poor values
589
+ return "#8B0000", "#fca5a5" # Dark Red, light red
590
+ elif value >= 0.5 and value < 1.0:
591
+ # Medium green bullet/light green text for moderate values
592
+ return "#2E7D32", "#86efac" # Forest Green, light green
593
+ elif value == 1.0:
594
+ # Bright green bullet/white text for perfect values
595
+ return "#00C853", "white" # Vivid Green
596
+ else:
597
+ # Fallback for edge cases (negative values, > 1.0, etc.)
598
+ return DEFAULT_METRIC_GREY, "white" # unified fallback