synapse-sdk 1.0.0a23__py3-none-any.whl → 2025.12.3__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 (228) hide show
  1. synapse_sdk/__init__.py +24 -0
  2. synapse_sdk/cli/__init__.py +310 -5
  3. synapse_sdk/cli/alias/__init__.py +22 -0
  4. synapse_sdk/cli/alias/create.py +36 -0
  5. synapse_sdk/cli/alias/dataclass.py +31 -0
  6. synapse_sdk/cli/alias/default.py +16 -0
  7. synapse_sdk/cli/alias/delete.py +15 -0
  8. synapse_sdk/cli/alias/list.py +19 -0
  9. synapse_sdk/cli/alias/read.py +15 -0
  10. synapse_sdk/cli/alias/update.py +17 -0
  11. synapse_sdk/cli/alias/utils.py +61 -0
  12. synapse_sdk/cli/code_server.py +687 -0
  13. synapse_sdk/cli/config.py +440 -0
  14. synapse_sdk/cli/devtools.py +90 -0
  15. synapse_sdk/cli/plugin/__init__.py +33 -0
  16. synapse_sdk/cli/{create_plugin.py → plugin/create.py} +2 -2
  17. synapse_sdk/{plugins/cli → cli/plugin}/publish.py +23 -15
  18. synapse_sdk/clients/agent/__init__.py +9 -3
  19. synapse_sdk/clients/agent/container.py +143 -0
  20. synapse_sdk/clients/agent/core.py +19 -0
  21. synapse_sdk/clients/agent/ray.py +298 -9
  22. synapse_sdk/clients/backend/__init__.py +30 -12
  23. synapse_sdk/clients/backend/annotation.py +13 -5
  24. synapse_sdk/clients/backend/core.py +31 -4
  25. synapse_sdk/clients/backend/data_collection.py +186 -0
  26. synapse_sdk/clients/backend/hitl.py +17 -0
  27. synapse_sdk/clients/backend/integration.py +16 -1
  28. synapse_sdk/clients/backend/ml.py +5 -1
  29. synapse_sdk/clients/backend/models.py +78 -0
  30. synapse_sdk/clients/base.py +384 -41
  31. synapse_sdk/clients/ray/serve.py +2 -0
  32. synapse_sdk/clients/validators/collections.py +31 -0
  33. synapse_sdk/devtools/config.py +94 -0
  34. synapse_sdk/devtools/server.py +41 -0
  35. synapse_sdk/devtools/streamlit_app/__init__.py +5 -0
  36. synapse_sdk/devtools/streamlit_app/app.py +128 -0
  37. synapse_sdk/devtools/streamlit_app/services/__init__.py +11 -0
  38. synapse_sdk/devtools/streamlit_app/services/job_service.py +233 -0
  39. synapse_sdk/devtools/streamlit_app/services/plugin_service.py +236 -0
  40. synapse_sdk/devtools/streamlit_app/services/serve_service.py +95 -0
  41. synapse_sdk/devtools/streamlit_app/ui/__init__.py +15 -0
  42. synapse_sdk/devtools/streamlit_app/ui/config_tab.py +76 -0
  43. synapse_sdk/devtools/streamlit_app/ui/deployment_tab.py +66 -0
  44. synapse_sdk/devtools/streamlit_app/ui/http_tab.py +125 -0
  45. synapse_sdk/devtools/streamlit_app/ui/jobs_tab.py +573 -0
  46. synapse_sdk/devtools/streamlit_app/ui/serve_tab.py +346 -0
  47. synapse_sdk/devtools/streamlit_app/ui/status_bar.py +118 -0
  48. synapse_sdk/devtools/streamlit_app/utils/__init__.py +40 -0
  49. synapse_sdk/devtools/streamlit_app/utils/json_viewer.py +197 -0
  50. synapse_sdk/devtools/streamlit_app/utils/log_formatter.py +38 -0
  51. synapse_sdk/devtools/streamlit_app/utils/styles.py +241 -0
  52. synapse_sdk/devtools/streamlit_app/utils/ui_components.py +289 -0
  53. synapse_sdk/devtools/streamlit_app.py +10 -0
  54. synapse_sdk/loggers.py +120 -9
  55. synapse_sdk/plugins/README.md +1340 -0
  56. synapse_sdk/plugins/__init__.py +0 -13
  57. synapse_sdk/plugins/categories/base.py +117 -11
  58. synapse_sdk/plugins/categories/data_validation/actions/validation.py +72 -0
  59. synapse_sdk/plugins/categories/data_validation/templates/plugin/validation.py +33 -5
  60. synapse_sdk/plugins/categories/export/actions/__init__.py +3 -0
  61. synapse_sdk/plugins/categories/export/actions/export/__init__.py +28 -0
  62. synapse_sdk/plugins/categories/export/actions/export/action.py +165 -0
  63. synapse_sdk/plugins/categories/export/actions/export/enums.py +113 -0
  64. synapse_sdk/plugins/categories/export/actions/export/exceptions.py +53 -0
  65. synapse_sdk/plugins/categories/export/actions/export/models.py +74 -0
  66. synapse_sdk/plugins/categories/export/actions/export/run.py +195 -0
  67. synapse_sdk/plugins/categories/export/actions/export/utils.py +187 -0
  68. synapse_sdk/plugins/categories/export/templates/config.yaml +21 -0
  69. synapse_sdk/plugins/categories/export/templates/plugin/__init__.py +390 -0
  70. synapse_sdk/plugins/categories/export/templates/plugin/export.py +160 -0
  71. synapse_sdk/plugins/categories/neural_net/actions/deployment.py +13 -12
  72. synapse_sdk/plugins/categories/neural_net/actions/train.py +1134 -31
  73. synapse_sdk/plugins/categories/neural_net/actions/tune.py +534 -0
  74. synapse_sdk/plugins/categories/neural_net/base/inference.py +1 -1
  75. synapse_sdk/plugins/categories/neural_net/templates/config.yaml +32 -4
  76. synapse_sdk/plugins/categories/neural_net/templates/plugin/inference.py +26 -10
  77. synapse_sdk/plugins/categories/pre_annotation/actions/__init__.py +4 -0
  78. synapse_sdk/plugins/categories/pre_annotation/actions/pre_annotation/__init__.py +3 -0
  79. synapse_sdk/plugins/categories/{export/actions/export.py → pre_annotation/actions/pre_annotation/action.py} +4 -4
  80. synapse_sdk/plugins/categories/pre_annotation/actions/to_task/__init__.py +28 -0
  81. synapse_sdk/plugins/categories/pre_annotation/actions/to_task/action.py +148 -0
  82. synapse_sdk/plugins/categories/pre_annotation/actions/to_task/enums.py +269 -0
  83. synapse_sdk/plugins/categories/pre_annotation/actions/to_task/exceptions.py +14 -0
  84. synapse_sdk/plugins/categories/pre_annotation/actions/to_task/factory.py +76 -0
  85. synapse_sdk/plugins/categories/pre_annotation/actions/to_task/models.py +100 -0
  86. synapse_sdk/plugins/categories/pre_annotation/actions/to_task/orchestrator.py +248 -0
  87. synapse_sdk/plugins/categories/pre_annotation/actions/to_task/run.py +64 -0
  88. synapse_sdk/plugins/categories/pre_annotation/actions/to_task/strategies/__init__.py +17 -0
  89. synapse_sdk/plugins/categories/pre_annotation/actions/to_task/strategies/annotation.py +265 -0
  90. synapse_sdk/plugins/categories/pre_annotation/actions/to_task/strategies/base.py +170 -0
  91. synapse_sdk/plugins/categories/pre_annotation/actions/to_task/strategies/extraction.py +83 -0
  92. synapse_sdk/plugins/categories/pre_annotation/actions/to_task/strategies/metrics.py +92 -0
  93. synapse_sdk/plugins/categories/pre_annotation/actions/to_task/strategies/preprocessor.py +243 -0
  94. synapse_sdk/plugins/categories/pre_annotation/actions/to_task/strategies/validation.py +143 -0
  95. synapse_sdk/plugins/categories/pre_annotation/templates/config.yaml +19 -0
  96. synapse_sdk/plugins/categories/pre_annotation/templates/plugin/to_task.py +40 -0
  97. synapse_sdk/plugins/categories/smart_tool/templates/config.yaml +2 -0
  98. synapse_sdk/plugins/categories/upload/__init__.py +0 -0
  99. synapse_sdk/plugins/categories/upload/actions/__init__.py +0 -0
  100. synapse_sdk/plugins/categories/upload/actions/upload/__init__.py +19 -0
  101. synapse_sdk/plugins/categories/upload/actions/upload/action.py +236 -0
  102. synapse_sdk/plugins/categories/upload/actions/upload/context.py +185 -0
  103. synapse_sdk/plugins/categories/upload/actions/upload/enums.py +493 -0
  104. synapse_sdk/plugins/categories/upload/actions/upload/exceptions.py +36 -0
  105. synapse_sdk/plugins/categories/upload/actions/upload/factory.py +138 -0
  106. synapse_sdk/plugins/categories/upload/actions/upload/models.py +214 -0
  107. synapse_sdk/plugins/categories/upload/actions/upload/orchestrator.py +183 -0
  108. synapse_sdk/plugins/categories/upload/actions/upload/registry.py +113 -0
  109. synapse_sdk/plugins/categories/upload/actions/upload/run.py +179 -0
  110. synapse_sdk/plugins/categories/upload/actions/upload/steps/__init__.py +1 -0
  111. synapse_sdk/plugins/categories/upload/actions/upload/steps/base.py +107 -0
  112. synapse_sdk/plugins/categories/upload/actions/upload/steps/cleanup.py +62 -0
  113. synapse_sdk/plugins/categories/upload/actions/upload/steps/collection.py +63 -0
  114. synapse_sdk/plugins/categories/upload/actions/upload/steps/generate.py +91 -0
  115. synapse_sdk/plugins/categories/upload/actions/upload/steps/initialize.py +82 -0
  116. synapse_sdk/plugins/categories/upload/actions/upload/steps/metadata.py +235 -0
  117. synapse_sdk/plugins/categories/upload/actions/upload/steps/organize.py +201 -0
  118. synapse_sdk/plugins/categories/upload/actions/upload/steps/upload.py +104 -0
  119. synapse_sdk/plugins/categories/upload/actions/upload/steps/validate.py +71 -0
  120. synapse_sdk/plugins/categories/upload/actions/upload/strategies/__init__.py +1 -0
  121. synapse_sdk/plugins/categories/upload/actions/upload/strategies/base.py +82 -0
  122. synapse_sdk/plugins/categories/upload/actions/upload/strategies/data_unit/__init__.py +1 -0
  123. synapse_sdk/plugins/categories/upload/actions/upload/strategies/data_unit/batch.py +39 -0
  124. synapse_sdk/plugins/categories/upload/actions/upload/strategies/data_unit/single.py +29 -0
  125. synapse_sdk/plugins/categories/upload/actions/upload/strategies/file_discovery/__init__.py +1 -0
  126. synapse_sdk/plugins/categories/upload/actions/upload/strategies/file_discovery/flat.py +300 -0
  127. synapse_sdk/plugins/categories/upload/actions/upload/strategies/file_discovery/recursive.py +287 -0
  128. synapse_sdk/plugins/categories/upload/actions/upload/strategies/metadata/__init__.py +1 -0
  129. synapse_sdk/plugins/categories/upload/actions/upload/strategies/metadata/excel.py +174 -0
  130. synapse_sdk/plugins/categories/upload/actions/upload/strategies/metadata/none.py +16 -0
  131. synapse_sdk/plugins/categories/upload/actions/upload/strategies/upload/__init__.py +1 -0
  132. synapse_sdk/plugins/categories/upload/actions/upload/strategies/upload/sync.py +84 -0
  133. synapse_sdk/plugins/categories/upload/actions/upload/strategies/validation/__init__.py +1 -0
  134. synapse_sdk/plugins/categories/upload/actions/upload/strategies/validation/default.py +60 -0
  135. synapse_sdk/plugins/categories/upload/actions/upload/utils.py +250 -0
  136. synapse_sdk/plugins/categories/upload/templates/README.md +470 -0
  137. synapse_sdk/plugins/categories/upload/templates/config.yaml +33 -0
  138. synapse_sdk/plugins/categories/upload/templates/plugin/__init__.py +310 -0
  139. synapse_sdk/plugins/categories/upload/templates/plugin/upload.py +102 -0
  140. synapse_sdk/plugins/enums.py +3 -1
  141. synapse_sdk/plugins/models.py +148 -11
  142. synapse_sdk/plugins/templates/plugin-config-schema.json +406 -0
  143. synapse_sdk/plugins/templates/schema.json +491 -0
  144. synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/config.yaml +1 -0
  145. synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/requirements.txt +1 -1
  146. synapse_sdk/plugins/utils/__init__.py +46 -0
  147. synapse_sdk/plugins/utils/actions.py +119 -0
  148. synapse_sdk/plugins/utils/config.py +203 -0
  149. synapse_sdk/plugins/{utils.py → utils/legacy.py} +26 -46
  150. synapse_sdk/plugins/utils/ray_gcs.py +66 -0
  151. synapse_sdk/plugins/utils/registry.py +58 -0
  152. synapse_sdk/shared/__init__.py +25 -0
  153. synapse_sdk/shared/enums.py +93 -0
  154. synapse_sdk/types.py +19 -0
  155. synapse_sdk/utils/converters/__init__.py +240 -0
  156. synapse_sdk/utils/converters/coco/__init__.py +0 -0
  157. synapse_sdk/utils/converters/coco/from_dm.py +322 -0
  158. synapse_sdk/utils/converters/coco/to_dm.py +215 -0
  159. synapse_sdk/utils/converters/dm/__init__.py +57 -0
  160. synapse_sdk/utils/converters/dm/base.py +137 -0
  161. synapse_sdk/utils/converters/dm/from_v1.py +273 -0
  162. synapse_sdk/utils/converters/dm/to_v1.py +321 -0
  163. synapse_sdk/utils/converters/dm/tools/__init__.py +214 -0
  164. synapse_sdk/utils/converters/dm/tools/answer.py +95 -0
  165. synapse_sdk/utils/converters/dm/tools/bounding_box.py +132 -0
  166. synapse_sdk/utils/converters/dm/tools/bounding_box_3d.py +121 -0
  167. synapse_sdk/utils/converters/dm/tools/classification.py +75 -0
  168. synapse_sdk/utils/converters/dm/tools/keypoint.py +117 -0
  169. synapse_sdk/utils/converters/dm/tools/named_entity.py +111 -0
  170. synapse_sdk/utils/converters/dm/tools/polygon.py +122 -0
  171. synapse_sdk/utils/converters/dm/tools/polyline.py +124 -0
  172. synapse_sdk/utils/converters/dm/tools/prompt.py +94 -0
  173. synapse_sdk/utils/converters/dm/tools/relation.py +86 -0
  174. synapse_sdk/utils/converters/dm/tools/segmentation.py +141 -0
  175. synapse_sdk/utils/converters/dm/tools/segmentation_3d.py +83 -0
  176. synapse_sdk/utils/converters/dm/types.py +168 -0
  177. synapse_sdk/utils/converters/dm/utils.py +162 -0
  178. synapse_sdk/utils/converters/dm_legacy/__init__.py +56 -0
  179. synapse_sdk/utils/converters/dm_legacy/from_v1.py +627 -0
  180. synapse_sdk/utils/converters/dm_legacy/to_v1.py +367 -0
  181. synapse_sdk/utils/converters/pascal/__init__.py +0 -0
  182. synapse_sdk/utils/converters/pascal/from_dm.py +244 -0
  183. synapse_sdk/utils/converters/pascal/to_dm.py +214 -0
  184. synapse_sdk/utils/converters/yolo/__init__.py +0 -0
  185. synapse_sdk/utils/converters/yolo/from_dm.py +384 -0
  186. synapse_sdk/utils/converters/yolo/to_dm.py +267 -0
  187. synapse_sdk/utils/dataset.py +46 -0
  188. synapse_sdk/utils/encryption.py +158 -0
  189. synapse_sdk/utils/file/__init__.py +58 -0
  190. synapse_sdk/utils/file/archive.py +32 -0
  191. synapse_sdk/utils/file/checksum.py +56 -0
  192. synapse_sdk/utils/file/chunking.py +31 -0
  193. synapse_sdk/utils/file/download.py +385 -0
  194. synapse_sdk/utils/file/encoding.py +40 -0
  195. synapse_sdk/utils/file/io.py +22 -0
  196. synapse_sdk/utils/file/upload.py +165 -0
  197. synapse_sdk/utils/file/video/__init__.py +29 -0
  198. synapse_sdk/utils/file/video/transcode.py +307 -0
  199. synapse_sdk/utils/file.py.backup +301 -0
  200. synapse_sdk/utils/http.py +138 -0
  201. synapse_sdk/utils/network.py +309 -0
  202. synapse_sdk/utils/storage/__init__.py +72 -0
  203. synapse_sdk/utils/storage/providers/__init__.py +183 -0
  204. synapse_sdk/utils/storage/providers/file_system.py +134 -0
  205. synapse_sdk/utils/storage/providers/gcp.py +13 -0
  206. synapse_sdk/utils/storage/providers/http.py +190 -0
  207. synapse_sdk/utils/storage/providers/s3.py +91 -0
  208. synapse_sdk/utils/storage/providers/sftp.py +47 -0
  209. synapse_sdk/utils/storage/registry.py +17 -0
  210. synapse_sdk-2025.12.3.dist-info/METADATA +123 -0
  211. synapse_sdk-2025.12.3.dist-info/RECORD +279 -0
  212. {synapse_sdk-1.0.0a23.dist-info → synapse_sdk-2025.12.3.dist-info}/WHEEL +1 -1
  213. synapse_sdk/clients/backend/dataset.py +0 -51
  214. synapse_sdk/plugins/categories/import/actions/import.py +0 -10
  215. synapse_sdk/plugins/cli/__init__.py +0 -21
  216. synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/.env +0 -24
  217. synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/.env.dist +0 -24
  218. synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/main.py +0 -4
  219. synapse_sdk/utils/file.py +0 -168
  220. synapse_sdk/utils/storage.py +0 -91
  221. synapse_sdk-1.0.0a23.dist-info/METADATA +0 -44
  222. synapse_sdk-1.0.0a23.dist-info/RECORD +0 -114
  223. /synapse_sdk/{plugins/cli → cli/plugin}/run.py +0 -0
  224. /synapse_sdk/{plugins/categories/import → clients/validators}/__init__.py +0 -0
  225. /synapse_sdk/{plugins/categories/import/actions → devtools}/__init__.py +0 -0
  226. {synapse_sdk-1.0.0a23.dist-info → synapse_sdk-2025.12.3.dist-info}/entry_points.txt +0 -0
  227. {synapse_sdk-1.0.0a23.dist-info → synapse_sdk-2025.12.3.dist-info/licenses}/LICENSE +0 -0
  228. {synapse_sdk-1.0.0a23.dist-info → synapse_sdk-2025.12.3.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,197 @@
1
+ """Professional JSON viewer component."""
2
+
3
+ import json
4
+ from typing import Any, Dict, List, Union
5
+
6
+ import streamlit as st
7
+ from streamlit_ace import st_ace
8
+
9
+
10
+ def format_json_value(value: Any, indent: int = 0) -> str:
11
+ """Format a JSON value for display."""
12
+ if isinstance(value, (dict, list)):
13
+ return json.dumps(value, indent=2)
14
+ elif isinstance(value, str):
15
+ return f'"{value}"'
16
+ elif value is None:
17
+ return 'null'
18
+ elif isinstance(value, bool):
19
+ return 'true' if value else 'false'
20
+ else:
21
+ return str(value)
22
+
23
+
24
+ def render_json_compact(data: Union[Dict, List], title: str = None, expanded: bool = True):
25
+ """Render JSON data in a compact, professional format."""
26
+ if not data:
27
+ return
28
+
29
+ if title:
30
+ st.markdown(f'### {title}')
31
+
32
+ # Use code editor for better JSON display
33
+ json_str = json.dumps(data, indent=2, ensure_ascii=False)
34
+
35
+ # Calculate appropriate height based on content
36
+ lines = json_str.count('\n') + 1
37
+ height = min(max(100, lines * 20), 600) # Min 100px, max 600px
38
+
39
+ st_ace(
40
+ value=json_str,
41
+ language='json',
42
+ theme='monokai',
43
+ key=f'json_viewer_{title}_{hash(json_str)}',
44
+ height=height,
45
+ auto_update=False,
46
+ font_size=13,
47
+ show_gutter=True,
48
+ show_print_margin=False,
49
+ wrap=False,
50
+ annotations=None,
51
+ readonly=True,
52
+ )
53
+
54
+
55
+ def render_json_as_table(data: Dict, title: str = None):
56
+ """Render JSON data as a formatted table for key-value pairs."""
57
+ if not data:
58
+ return
59
+
60
+ if title:
61
+ st.markdown(f'### {title}')
62
+
63
+ # Create HTML table - use compact string to avoid rendering issues
64
+ html = ( # noqa: E501
65
+ '<style>'
66
+ '.json-table { width: 100%; border-collapse: collapse; font-family: "SF Mono", Monaco, "Cascadia Code", "Roboto Mono", monospace; font-size: 13px; margin: 10px 0; }' # noqa: E501
67
+ '.json-table th { background-color: #2d2d2d; color: #f0f0f0; text-align: left; padding: 10px 15px; border-bottom: 2px solid #444; font-weight: 600; }' # noqa: E501
68
+ '.json-table td { padding: 8px 15px; border-bottom: 1px solid #e0e0e0; vertical-align: top; }'
69
+ '.json-table tr:hover { background-color: #f8f9fa; }'
70
+ '.json-key { color: #0066cc; font-weight: 500; white-space: nowrap; width: 30%; }'
71
+ '.json-value { color: #333; word-break: break-word; }'
72
+ '.json-value-null { color: #999; font-style: italic; }'
73
+ '.json-value-bool { color: #d73a49; }'
74
+ '.json-value-number { color: #005cc5; }'
75
+ '.json-value-string { color: #032f62; }'
76
+ '.json-value-object { color: #6f42c1; font-family: "SF Mono", Monaco, monospace; font-size: 12px; background-color: #f6f8fa; padding: 4px 8px; border-radius: 3px; display: inline-block; max-width: 500px; overflow-x: auto; }' # noqa: E501
77
+ '</style>'
78
+ '<table class="json-table">'
79
+ '<thead><tr><th>Key</th><th>Value</th></tr></thead>'
80
+ '<tbody>'
81
+ )
82
+
83
+ for key, value in data.items():
84
+ value_class = 'json-value'
85
+ value_str = ''
86
+
87
+ if value is None:
88
+ value_class = 'json-value-null'
89
+ value_str = 'null'
90
+ elif isinstance(value, bool):
91
+ value_class = 'json-value-bool'
92
+ value_str = str(value).lower()
93
+ elif isinstance(value, (int, float)):
94
+ value_class = 'json-value-number'
95
+ value_str = str(value)
96
+ elif isinstance(value, str):
97
+ value_class = 'json-value-string'
98
+ value_str = value
99
+ elif isinstance(value, (dict, list)):
100
+ value_class = 'json-value-object'
101
+ value_str = json.dumps(value, ensure_ascii=False)
102
+ if len(value_str) > 100:
103
+ value_str = json.dumps(value, indent=2, ensure_ascii=False)
104
+ else:
105
+ value_str = str(value)
106
+
107
+ html += f'<tr><td class="json-key">{key}</td><td class="{value_class}">{value_str}</td></tr>'
108
+
109
+ html += '</tbody></table>'
110
+
111
+ st.markdown(html, unsafe_allow_html=True)
112
+
113
+
114
+ def render_json_tree(data: Union[Dict, List], title: str = None, max_depth: int = 3):
115
+ """Render JSON data as an expandable tree view."""
116
+ if not data:
117
+ return
118
+
119
+ if title:
120
+ st.markdown(f'### {title}')
121
+
122
+ def render_node(obj: Any, key: str = '', depth: int = 0):
123
+ """Recursively render JSON nodes."""
124
+ indent = '&nbsp;' * (depth * 20)
125
+
126
+ if isinstance(obj, dict):
127
+ if depth < max_depth:
128
+ with st.expander(f'{key or "Object"} ({len(obj)} items)', expanded=depth == 0):
129
+ for k, v in obj.items():
130
+ if isinstance(v, (dict, list)):
131
+ render_node(v, k, depth + 1)
132
+ else:
133
+ st.markdown(
134
+ f"<div style='font-family: monospace; font-size: 13px; padding: 2px 0;'>"
135
+ f"<span style='color: #0066cc; font-weight: 500;'>{k}:</span> "
136
+ f"<span style='color: #333;'>{format_json_value(v)}</span></div>",
137
+ unsafe_allow_html=True,
138
+ )
139
+ else:
140
+ st.code(json.dumps(obj, indent=2), language='json')
141
+
142
+ elif isinstance(obj, list):
143
+ if depth < max_depth:
144
+ with st.expander(f'{key or "Array"} [{len(obj)} items]', expanded=depth == 0):
145
+ for i, item in enumerate(obj):
146
+ render_node(item, f'[{i}]', depth + 1)
147
+ else:
148
+ st.code(json.dumps(obj, indent=2), language='json')
149
+
150
+ else:
151
+ st.markdown(
152
+ f"<div style='font-family: monospace; font-size: 13px; padding: 2px 0;'>"
153
+ f"{indent}<span style='color: #333;'>{format_json_value(obj)}</span></div>",
154
+ unsafe_allow_html=True,
155
+ )
156
+
157
+ render_node(data)
158
+
159
+
160
+ def render_metrics_grid(metrics: Dict, title: str = 'Metrics'):
161
+ """Render metrics in a professional grid layout."""
162
+ if not metrics:
163
+ return
164
+
165
+ st.markdown(f'### {title}')
166
+
167
+ # Create columns based on number of metrics
168
+ num_metrics = len(metrics)
169
+ if num_metrics <= 3:
170
+ cols = st.columns(num_metrics)
171
+ elif num_metrics <= 6:
172
+ cols = st.columns(3)
173
+ else:
174
+ cols = st.columns(4)
175
+
176
+ for i, (key, value) in enumerate(metrics.items()):
177
+ col_idx = i % len(cols)
178
+ with cols[col_idx]:
179
+ # Format the value
180
+ if isinstance(value, float):
181
+ if value < 1:
182
+ formatted_value = f'{value:.4f}'
183
+ else:
184
+ formatted_value = f'{value:.2f}'
185
+ elif isinstance(value, dict):
186
+ formatted_value = f'{len(value)} items'
187
+ elif isinstance(value, list):
188
+ formatted_value = f'{len(value)} items'
189
+ else:
190
+ formatted_value = str(value)
191
+
192
+ # Display as metric
193
+ st.metric(
194
+ label=key.replace('_', ' ').title(),
195
+ value=formatted_value,
196
+ delta=None,
197
+ )
@@ -0,0 +1,38 @@
1
+ """Log formatting utilities."""
2
+
3
+ import html
4
+ import re
5
+
6
+
7
+ def format_log_line(line: str, line_number: int) -> str:
8
+ """Format a log line with syntax highlighting and line numbers."""
9
+ # Clean the line
10
+ line = line.strip()
11
+ if not line:
12
+ return (
13
+ f'<div class="log-line"><span class="log-line-number">{line_number}</span>'
14
+ f'<div class="log-line-content"></div></div>'
15
+ )
16
+
17
+ # Escape HTML first
18
+ line = html.escape(line)
19
+
20
+ # Check for different log levels and patterns
21
+ if re.search(r'\b(error|exception|fail|fatal)\b', line, re.IGNORECASE):
22
+ css_class = 'log-error'
23
+ elif re.search(r'\b(warn|warning)\b', line, re.IGNORECASE):
24
+ css_class = 'log-warning'
25
+ elif re.search(r'\b(info|information)\b', line, re.IGNORECASE):
26
+ css_class = 'log-info'
27
+ elif re.search(r'\b(debug|trace)\b', line, re.IGNORECASE):
28
+ css_class = 'log-debug'
29
+ else:
30
+ css_class = ''
31
+
32
+ # Highlight timestamps (common patterns)
33
+ line = re.sub(r'(\d{4}-\d{2}-\d{2}[\sT]\d{2}:\d{2}:\d{2})', r'<span class="log-timestamp">\1</span>', line)
34
+
35
+ return (
36
+ f'<div class="log-line"><span class="log-line-number">{line_number}</span>'
37
+ f'<div class="log-line-content {css_class}">{line}</div></div>'
38
+ )
@@ -0,0 +1,241 @@
1
+ """Styling constants and CSS for the Streamlit app."""
2
+
3
+ CUSTOM_CSS = """
4
+ <style>
5
+ /* Global typography and spacing */
6
+ * {
7
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Helvetica Neue', Arial, sans-serif;
8
+ }
9
+
10
+ /* Consistent button styling */
11
+ .stButton > button {
12
+ border-radius: 6px;
13
+ font-weight: 500;
14
+ transition: all 0.2s ease;
15
+ padding: 0.5rem 1rem;
16
+ }
17
+
18
+ .stButton > button:hover {
19
+ transform: translateY(-1px);
20
+ box-shadow: 0 2px 4px rgba(0,0,0,0.1);
21
+ }
22
+
23
+ /* Tab styling */
24
+ .stTabs [data-baseweb="tab-list"] button [data-testid="stMarkdownContainer"] p {
25
+ font-size: 14px;
26
+ font-weight: 500;
27
+ letter-spacing: 0.3px;
28
+ }
29
+ .success-box {
30
+ padding: 1rem;
31
+ border-radius: 0.5rem;
32
+ background-color: #d4edda;
33
+ border: 1px solid #c3e6cb;
34
+ color: #155724;
35
+ margin: 1rem 0;
36
+ }
37
+ .error-box {
38
+ padding: 1rem;
39
+ border-radius: 0.5rem;
40
+ background-color: #f8d7da;
41
+ border: 1px solid #f5c6cb;
42
+ color: #721c24;
43
+ margin: 1rem 0;
44
+ }
45
+ .info-box {
46
+ padding: 1rem;
47
+ border-radius: 0.5rem;
48
+ background-color: #d1ecf1;
49
+ border: 1px solid #bee5eb;
50
+ color: #0c5460;
51
+ margin: 1rem 0;
52
+ }
53
+ .status-bar {
54
+ background-color: #f8f9fa;
55
+ border-radius: 0.5rem;
56
+ padding: 1rem;
57
+ margin: 1rem 0;
58
+ border: 1px solid #dee2e6;
59
+ }
60
+ .log-container {
61
+ background-color: #1e1e1e;
62
+ color: #d4d4d4;
63
+ font-family: 'Fira Code', 'Consolas', 'Monaco', 'Courier New', monospace;
64
+ font-size: 14px;
65
+ line-height: 1.4;
66
+ padding: 0;
67
+ border-radius: 0.5rem;
68
+ overflow-x: auto;
69
+ overflow-y: auto;
70
+ max-height: 400px;
71
+ white-space: pre-wrap;
72
+ word-wrap: break-word;
73
+ border: 1px solid #3c3c3c;
74
+ display: flex;
75
+ flex-direction: column;
76
+ }
77
+ .log-line {
78
+ margin: 0;
79
+ padding: 0.2rem 1rem;
80
+ display: flex;
81
+ align-items: flex-start;
82
+ border-bottom: 1px solid #2a2a2a;
83
+ }
84
+ .log-line:hover {
85
+ background-color: #2a2a2a;
86
+ }
87
+ .log-line-number {
88
+ color: #858585;
89
+ font-size: 12px;
90
+ min-width: 40px;
91
+ text-align: right;
92
+ padding-right: 12px;
93
+ user-select: none;
94
+ flex-shrink: 0;
95
+ }
96
+ .log-line-content {
97
+ flex: 1;
98
+ word-break: break-word;
99
+ }
100
+ .log-error {
101
+ color: #f48771;
102
+ font-weight: bold;
103
+ }
104
+ .log-warning {
105
+ color: #dcdcaa;
106
+ }
107
+ .log-info {
108
+ color: #9cdcfe;
109
+ }
110
+ .log-debug {
111
+ color: #608b4e;
112
+ }
113
+ .log-timestamp {
114
+ color: #569cd6;
115
+ }
116
+ .stJson {
117
+ max-width: 100%;
118
+ overflow-x: auto;
119
+ }
120
+ /* Professional styling for code blocks */
121
+ .stCodeBlock {
122
+ background-color: #1e1e1e !important;
123
+ border: 1px solid #3c3c3c !important;
124
+ border-radius: 6px !important;
125
+ }
126
+ /* Enhanced headers */
127
+ h1, h2, h3 {
128
+ font-weight: 600 !important;
129
+ color: #1a1a1a !important;
130
+ }
131
+ /* Professional section spacing */
132
+ .section-divider {
133
+ margin: 2rem 0;
134
+ border-bottom: 1px solid #e0e0e0;
135
+ }
136
+ /* Hide the Streamlit toolbar */
137
+ .stAppToolbar {
138
+ visibility: hidden;
139
+ }
140
+ /* Alternative method to hide toolbar */
141
+ [data-testid="stToolbar"] {
142
+ display: none !important;
143
+ }
144
+ /* Reduce top padding of main container */
145
+ .stMainBlockContainer,
146
+ .block-container {
147
+ padding-top: 1rem !important;
148
+ max-width: 100%;
149
+ }
150
+ /* Better tab styling */
151
+ .stTabs [data-baseweb="tab-list"] {
152
+ gap: 4px;
153
+ background-color: transparent;
154
+ border-bottom: 2px solid #e9ecef;
155
+ padding: 0;
156
+ }
157
+ .stTabs [data-baseweb="tab"] {
158
+ padding: 10px 20px;
159
+ border-radius: 0;
160
+ background-color: transparent;
161
+ border-bottom: 3px solid transparent;
162
+ margin-bottom: -2px;
163
+ }
164
+ .stTabs [data-baseweb="tab-list"] button[aria-selected="true"] {
165
+ background-color: transparent;
166
+ border-bottom: 3px solid #007bff;
167
+ color: #007bff;
168
+ }
169
+ .stTabs [data-baseweb="tab"]:hover {
170
+ background-color: #f8f9fa;
171
+ }
172
+
173
+ /* Form styling */
174
+ .stSelectbox > div > div {
175
+ border-radius: 6px;
176
+ }
177
+ .stTextInput > div > div > input {
178
+ border-radius: 6px;
179
+ }
180
+ .stTextArea > div > div > textarea {
181
+ border-radius: 6px;
182
+ }
183
+
184
+ /* Checkbox and radio styling */
185
+ .stCheckbox > label > span {
186
+ font-size: 14px;
187
+ }
188
+ .stRadio > label {
189
+ font-size: 14px;
190
+ }
191
+
192
+ /* Metrics styling */
193
+ .metric-container .metric-label {
194
+ font-size: 12px;
195
+ font-weight: 500;
196
+ text-transform: uppercase;
197
+ letter-spacing: 0.5px;
198
+ color: #6c757d;
199
+ }
200
+
201
+ /* Consistent spacing */
202
+ .row-widget.stButton {
203
+ margin-top: 0.5rem;
204
+ margin-bottom: 0.5rem;
205
+ }
206
+ /* Also adjust the header spacing */
207
+ .stAppHeader {
208
+ height: 0rem !important;
209
+ }
210
+ </style>
211
+ """
212
+
213
+
214
+ def get_status_badge_html(status: str) -> str:
215
+ """Generate HTML for status badge with outline style."""
216
+ status = status.upper() if status else 'UNKNOWN'
217
+
218
+ color_map = {
219
+ 'SUCCEEDED': '#28a745',
220
+ 'RUNNING': '#007bff',
221
+ 'PENDING': '#6c757d',
222
+ 'FAILED': '#dc3545',
223
+ 'STOPPED': '#6c757d',
224
+ 'UNKNOWN': '#6c757d',
225
+ }
226
+
227
+ color = color_map.get(status, '#6c757d')
228
+
229
+ return f"""
230
+ <span style="
231
+ border: 1px solid {color};
232
+ color: {color};
233
+ padding: 4px 10px;
234
+ border-radius: 4px;
235
+ font-size: 12px;
236
+ font-weight: 500;
237
+ display: inline-block;
238
+ text-align: center;
239
+ min-width: 70px;
240
+ ">{status}</span>
241
+ """