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.
- synapse_sdk/__init__.py +24 -0
- synapse_sdk/cli/__init__.py +310 -5
- synapse_sdk/cli/alias/__init__.py +22 -0
- synapse_sdk/cli/alias/create.py +36 -0
- synapse_sdk/cli/alias/dataclass.py +31 -0
- synapse_sdk/cli/alias/default.py +16 -0
- synapse_sdk/cli/alias/delete.py +15 -0
- synapse_sdk/cli/alias/list.py +19 -0
- synapse_sdk/cli/alias/read.py +15 -0
- synapse_sdk/cli/alias/update.py +17 -0
- synapse_sdk/cli/alias/utils.py +61 -0
- synapse_sdk/cli/code_server.py +687 -0
- synapse_sdk/cli/config.py +440 -0
- synapse_sdk/cli/devtools.py +90 -0
- synapse_sdk/cli/plugin/__init__.py +33 -0
- synapse_sdk/cli/{create_plugin.py → plugin/create.py} +2 -2
- synapse_sdk/{plugins/cli → cli/plugin}/publish.py +23 -15
- synapse_sdk/clients/agent/__init__.py +9 -3
- synapse_sdk/clients/agent/container.py +143 -0
- synapse_sdk/clients/agent/core.py +19 -0
- synapse_sdk/clients/agent/ray.py +298 -9
- synapse_sdk/clients/backend/__init__.py +30 -12
- synapse_sdk/clients/backend/annotation.py +13 -5
- synapse_sdk/clients/backend/core.py +31 -4
- synapse_sdk/clients/backend/data_collection.py +186 -0
- synapse_sdk/clients/backend/hitl.py +17 -0
- synapse_sdk/clients/backend/integration.py +16 -1
- synapse_sdk/clients/backend/ml.py +5 -1
- synapse_sdk/clients/backend/models.py +78 -0
- synapse_sdk/clients/base.py +384 -41
- synapse_sdk/clients/ray/serve.py +2 -0
- synapse_sdk/clients/validators/collections.py +31 -0
- synapse_sdk/devtools/config.py +94 -0
- synapse_sdk/devtools/server.py +41 -0
- synapse_sdk/devtools/streamlit_app/__init__.py +5 -0
- synapse_sdk/devtools/streamlit_app/app.py +128 -0
- synapse_sdk/devtools/streamlit_app/services/__init__.py +11 -0
- synapse_sdk/devtools/streamlit_app/services/job_service.py +233 -0
- synapse_sdk/devtools/streamlit_app/services/plugin_service.py +236 -0
- synapse_sdk/devtools/streamlit_app/services/serve_service.py +95 -0
- synapse_sdk/devtools/streamlit_app/ui/__init__.py +15 -0
- synapse_sdk/devtools/streamlit_app/ui/config_tab.py +76 -0
- synapse_sdk/devtools/streamlit_app/ui/deployment_tab.py +66 -0
- synapse_sdk/devtools/streamlit_app/ui/http_tab.py +125 -0
- synapse_sdk/devtools/streamlit_app/ui/jobs_tab.py +573 -0
- synapse_sdk/devtools/streamlit_app/ui/serve_tab.py +346 -0
- synapse_sdk/devtools/streamlit_app/ui/status_bar.py +118 -0
- synapse_sdk/devtools/streamlit_app/utils/__init__.py +40 -0
- synapse_sdk/devtools/streamlit_app/utils/json_viewer.py +197 -0
- synapse_sdk/devtools/streamlit_app/utils/log_formatter.py +38 -0
- synapse_sdk/devtools/streamlit_app/utils/styles.py +241 -0
- synapse_sdk/devtools/streamlit_app/utils/ui_components.py +289 -0
- synapse_sdk/devtools/streamlit_app.py +10 -0
- synapse_sdk/loggers.py +120 -9
- synapse_sdk/plugins/README.md +1340 -0
- synapse_sdk/plugins/__init__.py +0 -13
- synapse_sdk/plugins/categories/base.py +117 -11
- synapse_sdk/plugins/categories/data_validation/actions/validation.py +72 -0
- synapse_sdk/plugins/categories/data_validation/templates/plugin/validation.py +33 -5
- synapse_sdk/plugins/categories/export/actions/__init__.py +3 -0
- synapse_sdk/plugins/categories/export/actions/export/__init__.py +28 -0
- synapse_sdk/plugins/categories/export/actions/export/action.py +165 -0
- synapse_sdk/plugins/categories/export/actions/export/enums.py +113 -0
- synapse_sdk/plugins/categories/export/actions/export/exceptions.py +53 -0
- synapse_sdk/plugins/categories/export/actions/export/models.py +74 -0
- synapse_sdk/plugins/categories/export/actions/export/run.py +195 -0
- synapse_sdk/plugins/categories/export/actions/export/utils.py +187 -0
- synapse_sdk/plugins/categories/export/templates/config.yaml +21 -0
- synapse_sdk/plugins/categories/export/templates/plugin/__init__.py +390 -0
- synapse_sdk/plugins/categories/export/templates/plugin/export.py +160 -0
- synapse_sdk/plugins/categories/neural_net/actions/deployment.py +13 -12
- synapse_sdk/plugins/categories/neural_net/actions/train.py +1134 -31
- synapse_sdk/plugins/categories/neural_net/actions/tune.py +534 -0
- synapse_sdk/plugins/categories/neural_net/base/inference.py +1 -1
- synapse_sdk/plugins/categories/neural_net/templates/config.yaml +32 -4
- synapse_sdk/plugins/categories/neural_net/templates/plugin/inference.py +26 -10
- synapse_sdk/plugins/categories/pre_annotation/actions/__init__.py +4 -0
- synapse_sdk/plugins/categories/pre_annotation/actions/pre_annotation/__init__.py +3 -0
- synapse_sdk/plugins/categories/{export/actions/export.py → pre_annotation/actions/pre_annotation/action.py} +4 -4
- synapse_sdk/plugins/categories/pre_annotation/actions/to_task/__init__.py +28 -0
- synapse_sdk/plugins/categories/pre_annotation/actions/to_task/action.py +148 -0
- synapse_sdk/plugins/categories/pre_annotation/actions/to_task/enums.py +269 -0
- synapse_sdk/plugins/categories/pre_annotation/actions/to_task/exceptions.py +14 -0
- synapse_sdk/plugins/categories/pre_annotation/actions/to_task/factory.py +76 -0
- synapse_sdk/plugins/categories/pre_annotation/actions/to_task/models.py +100 -0
- synapse_sdk/plugins/categories/pre_annotation/actions/to_task/orchestrator.py +248 -0
- synapse_sdk/plugins/categories/pre_annotation/actions/to_task/run.py +64 -0
- synapse_sdk/plugins/categories/pre_annotation/actions/to_task/strategies/__init__.py +17 -0
- synapse_sdk/plugins/categories/pre_annotation/actions/to_task/strategies/annotation.py +265 -0
- synapse_sdk/plugins/categories/pre_annotation/actions/to_task/strategies/base.py +170 -0
- synapse_sdk/plugins/categories/pre_annotation/actions/to_task/strategies/extraction.py +83 -0
- synapse_sdk/plugins/categories/pre_annotation/actions/to_task/strategies/metrics.py +92 -0
- synapse_sdk/plugins/categories/pre_annotation/actions/to_task/strategies/preprocessor.py +243 -0
- synapse_sdk/plugins/categories/pre_annotation/actions/to_task/strategies/validation.py +143 -0
- synapse_sdk/plugins/categories/pre_annotation/templates/config.yaml +19 -0
- synapse_sdk/plugins/categories/pre_annotation/templates/plugin/to_task.py +40 -0
- synapse_sdk/plugins/categories/smart_tool/templates/config.yaml +2 -0
- synapse_sdk/plugins/categories/upload/__init__.py +0 -0
- synapse_sdk/plugins/categories/upload/actions/__init__.py +0 -0
- synapse_sdk/plugins/categories/upload/actions/upload/__init__.py +19 -0
- synapse_sdk/plugins/categories/upload/actions/upload/action.py +236 -0
- synapse_sdk/plugins/categories/upload/actions/upload/context.py +185 -0
- synapse_sdk/plugins/categories/upload/actions/upload/enums.py +493 -0
- synapse_sdk/plugins/categories/upload/actions/upload/exceptions.py +36 -0
- synapse_sdk/plugins/categories/upload/actions/upload/factory.py +138 -0
- synapse_sdk/plugins/categories/upload/actions/upload/models.py +214 -0
- synapse_sdk/plugins/categories/upload/actions/upload/orchestrator.py +183 -0
- synapse_sdk/plugins/categories/upload/actions/upload/registry.py +113 -0
- synapse_sdk/plugins/categories/upload/actions/upload/run.py +179 -0
- synapse_sdk/plugins/categories/upload/actions/upload/steps/__init__.py +1 -0
- synapse_sdk/plugins/categories/upload/actions/upload/steps/base.py +107 -0
- synapse_sdk/plugins/categories/upload/actions/upload/steps/cleanup.py +62 -0
- synapse_sdk/plugins/categories/upload/actions/upload/steps/collection.py +63 -0
- synapse_sdk/plugins/categories/upload/actions/upload/steps/generate.py +91 -0
- synapse_sdk/plugins/categories/upload/actions/upload/steps/initialize.py +82 -0
- synapse_sdk/plugins/categories/upload/actions/upload/steps/metadata.py +235 -0
- synapse_sdk/plugins/categories/upload/actions/upload/steps/organize.py +201 -0
- synapse_sdk/plugins/categories/upload/actions/upload/steps/upload.py +104 -0
- synapse_sdk/plugins/categories/upload/actions/upload/steps/validate.py +71 -0
- synapse_sdk/plugins/categories/upload/actions/upload/strategies/__init__.py +1 -0
- synapse_sdk/plugins/categories/upload/actions/upload/strategies/base.py +82 -0
- synapse_sdk/plugins/categories/upload/actions/upload/strategies/data_unit/__init__.py +1 -0
- synapse_sdk/plugins/categories/upload/actions/upload/strategies/data_unit/batch.py +39 -0
- synapse_sdk/plugins/categories/upload/actions/upload/strategies/data_unit/single.py +29 -0
- synapse_sdk/plugins/categories/upload/actions/upload/strategies/file_discovery/__init__.py +1 -0
- synapse_sdk/plugins/categories/upload/actions/upload/strategies/file_discovery/flat.py +300 -0
- synapse_sdk/plugins/categories/upload/actions/upload/strategies/file_discovery/recursive.py +287 -0
- synapse_sdk/plugins/categories/upload/actions/upload/strategies/metadata/__init__.py +1 -0
- synapse_sdk/plugins/categories/upload/actions/upload/strategies/metadata/excel.py +174 -0
- synapse_sdk/plugins/categories/upload/actions/upload/strategies/metadata/none.py +16 -0
- synapse_sdk/plugins/categories/upload/actions/upload/strategies/upload/__init__.py +1 -0
- synapse_sdk/plugins/categories/upload/actions/upload/strategies/upload/sync.py +84 -0
- synapse_sdk/plugins/categories/upload/actions/upload/strategies/validation/__init__.py +1 -0
- synapse_sdk/plugins/categories/upload/actions/upload/strategies/validation/default.py +60 -0
- synapse_sdk/plugins/categories/upload/actions/upload/utils.py +250 -0
- synapse_sdk/plugins/categories/upload/templates/README.md +470 -0
- synapse_sdk/plugins/categories/upload/templates/config.yaml +33 -0
- synapse_sdk/plugins/categories/upload/templates/plugin/__init__.py +310 -0
- synapse_sdk/plugins/categories/upload/templates/plugin/upload.py +102 -0
- synapse_sdk/plugins/enums.py +3 -1
- synapse_sdk/plugins/models.py +148 -11
- synapse_sdk/plugins/templates/plugin-config-schema.json +406 -0
- synapse_sdk/plugins/templates/schema.json +491 -0
- synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/config.yaml +1 -0
- synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/requirements.txt +1 -1
- synapse_sdk/plugins/utils/__init__.py +46 -0
- synapse_sdk/plugins/utils/actions.py +119 -0
- synapse_sdk/plugins/utils/config.py +203 -0
- synapse_sdk/plugins/{utils.py → utils/legacy.py} +26 -46
- synapse_sdk/plugins/utils/ray_gcs.py +66 -0
- synapse_sdk/plugins/utils/registry.py +58 -0
- synapse_sdk/shared/__init__.py +25 -0
- synapse_sdk/shared/enums.py +93 -0
- synapse_sdk/types.py +19 -0
- synapse_sdk/utils/converters/__init__.py +240 -0
- synapse_sdk/utils/converters/coco/__init__.py +0 -0
- synapse_sdk/utils/converters/coco/from_dm.py +322 -0
- synapse_sdk/utils/converters/coco/to_dm.py +215 -0
- synapse_sdk/utils/converters/dm/__init__.py +57 -0
- synapse_sdk/utils/converters/dm/base.py +137 -0
- synapse_sdk/utils/converters/dm/from_v1.py +273 -0
- synapse_sdk/utils/converters/dm/to_v1.py +321 -0
- synapse_sdk/utils/converters/dm/tools/__init__.py +214 -0
- synapse_sdk/utils/converters/dm/tools/answer.py +95 -0
- synapse_sdk/utils/converters/dm/tools/bounding_box.py +132 -0
- synapse_sdk/utils/converters/dm/tools/bounding_box_3d.py +121 -0
- synapse_sdk/utils/converters/dm/tools/classification.py +75 -0
- synapse_sdk/utils/converters/dm/tools/keypoint.py +117 -0
- synapse_sdk/utils/converters/dm/tools/named_entity.py +111 -0
- synapse_sdk/utils/converters/dm/tools/polygon.py +122 -0
- synapse_sdk/utils/converters/dm/tools/polyline.py +124 -0
- synapse_sdk/utils/converters/dm/tools/prompt.py +94 -0
- synapse_sdk/utils/converters/dm/tools/relation.py +86 -0
- synapse_sdk/utils/converters/dm/tools/segmentation.py +141 -0
- synapse_sdk/utils/converters/dm/tools/segmentation_3d.py +83 -0
- synapse_sdk/utils/converters/dm/types.py +168 -0
- synapse_sdk/utils/converters/dm/utils.py +162 -0
- synapse_sdk/utils/converters/dm_legacy/__init__.py +56 -0
- synapse_sdk/utils/converters/dm_legacy/from_v1.py +627 -0
- synapse_sdk/utils/converters/dm_legacy/to_v1.py +367 -0
- synapse_sdk/utils/converters/pascal/__init__.py +0 -0
- synapse_sdk/utils/converters/pascal/from_dm.py +244 -0
- synapse_sdk/utils/converters/pascal/to_dm.py +214 -0
- synapse_sdk/utils/converters/yolo/__init__.py +0 -0
- synapse_sdk/utils/converters/yolo/from_dm.py +384 -0
- synapse_sdk/utils/converters/yolo/to_dm.py +267 -0
- synapse_sdk/utils/dataset.py +46 -0
- synapse_sdk/utils/encryption.py +158 -0
- synapse_sdk/utils/file/__init__.py +58 -0
- synapse_sdk/utils/file/archive.py +32 -0
- synapse_sdk/utils/file/checksum.py +56 -0
- synapse_sdk/utils/file/chunking.py +31 -0
- synapse_sdk/utils/file/download.py +385 -0
- synapse_sdk/utils/file/encoding.py +40 -0
- synapse_sdk/utils/file/io.py +22 -0
- synapse_sdk/utils/file/upload.py +165 -0
- synapse_sdk/utils/file/video/__init__.py +29 -0
- synapse_sdk/utils/file/video/transcode.py +307 -0
- synapse_sdk/utils/file.py.backup +301 -0
- synapse_sdk/utils/http.py +138 -0
- synapse_sdk/utils/network.py +309 -0
- synapse_sdk/utils/storage/__init__.py +72 -0
- synapse_sdk/utils/storage/providers/__init__.py +183 -0
- synapse_sdk/utils/storage/providers/file_system.py +134 -0
- synapse_sdk/utils/storage/providers/gcp.py +13 -0
- synapse_sdk/utils/storage/providers/http.py +190 -0
- synapse_sdk/utils/storage/providers/s3.py +91 -0
- synapse_sdk/utils/storage/providers/sftp.py +47 -0
- synapse_sdk/utils/storage/registry.py +17 -0
- synapse_sdk-2025.12.3.dist-info/METADATA +123 -0
- synapse_sdk-2025.12.3.dist-info/RECORD +279 -0
- {synapse_sdk-1.0.0a23.dist-info → synapse_sdk-2025.12.3.dist-info}/WHEEL +1 -1
- synapse_sdk/clients/backend/dataset.py +0 -51
- synapse_sdk/plugins/categories/import/actions/import.py +0 -10
- synapse_sdk/plugins/cli/__init__.py +0 -21
- synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/.env +0 -24
- synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/.env.dist +0 -24
- synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/main.py +0 -4
- synapse_sdk/utils/file.py +0 -168
- synapse_sdk/utils/storage.py +0 -91
- synapse_sdk-1.0.0a23.dist-info/METADATA +0 -44
- synapse_sdk-1.0.0a23.dist-info/RECORD +0 -114
- /synapse_sdk/{plugins/cli → cli/plugin}/run.py +0 -0
- /synapse_sdk/{plugins/categories/import → clients/validators}/__init__.py +0 -0
- /synapse_sdk/{plugins/categories/import/actions → devtools}/__init__.py +0 -0
- {synapse_sdk-1.0.0a23.dist-info → synapse_sdk-2025.12.3.dist-info}/entry_points.txt +0 -0
- {synapse_sdk-1.0.0a23.dist-info → synapse_sdk-2025.12.3.dist-info/licenses}/LICENSE +0 -0
- {synapse_sdk-1.0.0a23.dist-info → synapse_sdk-2025.12.3.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
"""Unified UI components for consistent styling across the application."""
|
|
2
|
+
|
|
3
|
+
from typing import Any, Callable, Optional
|
|
4
|
+
|
|
5
|
+
import streamlit as st
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def render_action_button(
|
|
9
|
+
label: str,
|
|
10
|
+
key: str,
|
|
11
|
+
type: str = 'secondary',
|
|
12
|
+
icon: Optional[str] = None,
|
|
13
|
+
on_click: Optional[Callable] = None,
|
|
14
|
+
args: Optional[tuple] = None,
|
|
15
|
+
disabled: bool = False,
|
|
16
|
+
use_container_width: bool = False,
|
|
17
|
+
help: Optional[str] = None,
|
|
18
|
+
) -> bool:
|
|
19
|
+
"""Render a styled action button with consistent appearance."""
|
|
20
|
+
button_types = {
|
|
21
|
+
'primary': 'primary',
|
|
22
|
+
'secondary': 'secondary',
|
|
23
|
+
'danger': 'secondary', # Streamlit doesn't have danger, use CSS
|
|
24
|
+
'success': 'secondary',
|
|
25
|
+
'minimal': 'secondary',
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
# Add icon to label if provided
|
|
29
|
+
if icon:
|
|
30
|
+
display_label = f'{icon} {label}'
|
|
31
|
+
else:
|
|
32
|
+
display_label = label
|
|
33
|
+
|
|
34
|
+
# Create button with appropriate styling
|
|
35
|
+
clicked = st.button(
|
|
36
|
+
display_label,
|
|
37
|
+
key=key,
|
|
38
|
+
type=button_types.get(type, 'secondary'),
|
|
39
|
+
on_click=on_click,
|
|
40
|
+
args=args,
|
|
41
|
+
disabled=disabled,
|
|
42
|
+
use_container_width=use_container_width,
|
|
43
|
+
help=help,
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
# Apply custom CSS for special button types
|
|
47
|
+
if type == 'danger':
|
|
48
|
+
st.markdown(
|
|
49
|
+
f"""
|
|
50
|
+
<style>
|
|
51
|
+
button[kind="secondary"][key="{key}"] {{
|
|
52
|
+
background-color: #fff;
|
|
53
|
+
color: #dc3545;
|
|
54
|
+
border-color: #dc3545;
|
|
55
|
+
}}
|
|
56
|
+
button[kind="secondary"][key="{key}"]:hover {{
|
|
57
|
+
background-color: #dc3545;
|
|
58
|
+
color: white;
|
|
59
|
+
}}
|
|
60
|
+
</style>
|
|
61
|
+
""",
|
|
62
|
+
unsafe_allow_html=True,
|
|
63
|
+
)
|
|
64
|
+
elif type == 'success':
|
|
65
|
+
st.markdown(
|
|
66
|
+
f"""
|
|
67
|
+
<style>
|
|
68
|
+
button[kind="secondary"][key="{key}"] {{
|
|
69
|
+
background-color: #fff;
|
|
70
|
+
color: #28a745;
|
|
71
|
+
border-color: #28a745;
|
|
72
|
+
}}
|
|
73
|
+
button[kind="secondary"][key="{key}"]:hover {{
|
|
74
|
+
background-color: #28a745;
|
|
75
|
+
color: white;
|
|
76
|
+
}}
|
|
77
|
+
</style>
|
|
78
|
+
""",
|
|
79
|
+
unsafe_allow_html=True,
|
|
80
|
+
)
|
|
81
|
+
elif type == 'minimal':
|
|
82
|
+
st.markdown(
|
|
83
|
+
f"""
|
|
84
|
+
<style>
|
|
85
|
+
button[kind="secondary"][key="{key}"] {{
|
|
86
|
+
background-color: transparent;
|
|
87
|
+
border: none;
|
|
88
|
+
color: #007bff;
|
|
89
|
+
padding: 4px 8px;
|
|
90
|
+
}}
|
|
91
|
+
button[kind="secondary"][key="{key}"]:hover {{
|
|
92
|
+
background-color: #f8f9fa;
|
|
93
|
+
text-decoration: underline;
|
|
94
|
+
}}
|
|
95
|
+
</style>
|
|
96
|
+
""",
|
|
97
|
+
unsafe_allow_html=True,
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
return clicked
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def render_section_header(title: str, subtitle: Optional[str] = None):
|
|
104
|
+
"""Render a consistent section header."""
|
|
105
|
+
subtitle_html = (
|
|
106
|
+
f'<p style="margin-top: 0.25rem; color: #6c757d; font-size: 14px;">{subtitle}</p>' if subtitle else ''
|
|
107
|
+
)
|
|
108
|
+
st.markdown(
|
|
109
|
+
f'<div style="margin-bottom: 1.5rem;">'
|
|
110
|
+
f'<h3 style="margin: 0; font-weight: 600; color: #1a1a1a;">{title}</h3>'
|
|
111
|
+
f'{subtitle_html}'
|
|
112
|
+
f'</div>',
|
|
113
|
+
unsafe_allow_html=True,
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def render_info_card(title: str, content: str, type: str = 'info', icon: Optional[str] = None):
|
|
118
|
+
"""Render an information card with consistent styling."""
|
|
119
|
+
colors = {
|
|
120
|
+
'info': {'bg': '#d1ecf1', 'border': '#bee5eb', 'text': '#0c5460'},
|
|
121
|
+
'success': {'bg': '#d4edda', 'border': '#c3e6cb', 'text': '#155724'},
|
|
122
|
+
'warning': {'bg': '#fff3cd', 'border': '#ffeaa7', 'text': '#856404'},
|
|
123
|
+
'error': {'bg': '#f8d7da', 'border': '#f5c6cb', 'text': '#721c24'},
|
|
124
|
+
'neutral': {'bg': '#f8f9fa', 'border': '#dee2e6', 'text': '#495057'},
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
color_scheme = colors.get(type, colors['info'])
|
|
128
|
+
icon_html = f'<span style="margin-right: 8px;">{icon}</span>' if icon else ''
|
|
129
|
+
|
|
130
|
+
st.markdown(
|
|
131
|
+
f'<div style="'
|
|
132
|
+
f' background-color: {color_scheme["bg"]};'
|
|
133
|
+
f' border: 1px solid {color_scheme["border"]};'
|
|
134
|
+
f' border-radius: 6px;'
|
|
135
|
+
f' padding: 12px 16px;'
|
|
136
|
+
f' margin: 12px 0;'
|
|
137
|
+
f'">'
|
|
138
|
+
f'<div style="color: {color_scheme["text"]}; font-weight: 500; margin-bottom: 4px;">'
|
|
139
|
+
f'{icon_html}{title}'
|
|
140
|
+
f'</div>'
|
|
141
|
+
f'<div style="color: {color_scheme["text"]}; font-size: 14px;">{content}</div>'
|
|
142
|
+
f'</div>',
|
|
143
|
+
unsafe_allow_html=True,
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def render_metric_card(label: str, value: Any, delta: Optional[str] = None, delta_color: str = 'normal'):
|
|
148
|
+
"""Render a metric card with consistent styling."""
|
|
149
|
+
delta_html = ''
|
|
150
|
+
if delta:
|
|
151
|
+
color = {'normal': '#28a745', 'inverse': '#dc3545', 'off': '#6c757d'}.get(delta_color, '#28a745')
|
|
152
|
+
delta_html = f'<div style="color: {color}; font-size: 12px; margin-top: 4px;">{delta}</div>'
|
|
153
|
+
|
|
154
|
+
st.markdown(
|
|
155
|
+
f'<div style="'
|
|
156
|
+
f' background-color: #fff;'
|
|
157
|
+
f' border: 1px solid #e0e0e0;'
|
|
158
|
+
f' border-radius: 8px;'
|
|
159
|
+
f' padding: 16px;'
|
|
160
|
+
f' margin: 8px 0;'
|
|
161
|
+
f'">'
|
|
162
|
+
f'<div style="color: #6c757d; font-size: 12px; font-weight: 500; text-transform: uppercase; letter-spacing: 0.5px;">' # noqa: E501
|
|
163
|
+
f'{label}'
|
|
164
|
+
f'</div>'
|
|
165
|
+
f'<div style="color: #212529; font-size: 24px; font-weight: 600; margin-top: 4px;">'
|
|
166
|
+
f'{value}'
|
|
167
|
+
f'</div>'
|
|
168
|
+
f'{delta_html}'
|
|
169
|
+
f'</div>',
|
|
170
|
+
unsafe_allow_html=True,
|
|
171
|
+
)
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
def render_key_value_pair(key: str, value: Any, monospace_value: bool = False, inline: bool = True):
|
|
175
|
+
"""Render a key-value pair with consistent styling."""
|
|
176
|
+
value_style = 'font-family: monospace;' if monospace_value else ''
|
|
177
|
+
|
|
178
|
+
if inline:
|
|
179
|
+
st.markdown(
|
|
180
|
+
f'<div style="margin-bottom: 8px; font-size: 14px;">'
|
|
181
|
+
f'<span style="color: #495057; font-weight: 500;">{key}:</span> '
|
|
182
|
+
f'<span style="color: #212529; {value_style}">{value}</span>'
|
|
183
|
+
f'</div>',
|
|
184
|
+
unsafe_allow_html=True,
|
|
185
|
+
)
|
|
186
|
+
else:
|
|
187
|
+
st.markdown(
|
|
188
|
+
f'<div style="margin-bottom: 12px;">'
|
|
189
|
+
f'<div style="color: #495057; font-weight: 500; font-size: 12px; text-transform: uppercase; letter-spacing: 0.5px; margin-bottom: 4px;">' # noqa: E501
|
|
190
|
+
f'{key}'
|
|
191
|
+
f'</div>'
|
|
192
|
+
f'<div style="color: #212529; font-size: 14px; {value_style}">{value}</div>'
|
|
193
|
+
f'</div>',
|
|
194
|
+
unsafe_allow_html=True,
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
def render_divider(margin: str = '1.5rem'):
|
|
199
|
+
"""Render a consistent divider."""
|
|
200
|
+
st.markdown(
|
|
201
|
+
f'<hr style="margin: {margin} 0; border: none; border-top: 1px solid #e0e0e0;">', unsafe_allow_html=True
|
|
202
|
+
)
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
def render_table_header(columns: list, column_widths: list = None):
|
|
206
|
+
"""Render a consistent table header."""
|
|
207
|
+
if not column_widths:
|
|
208
|
+
column_widths = [1] * len(columns)
|
|
209
|
+
|
|
210
|
+
header_cols = st.columns(column_widths)
|
|
211
|
+
for i, header in enumerate(columns):
|
|
212
|
+
with header_cols[i]:
|
|
213
|
+
st.markdown(
|
|
214
|
+
f'<div style="'
|
|
215
|
+
f' color: #495057;'
|
|
216
|
+
f' font-size: 12px;'
|
|
217
|
+
f' font-weight: 600;'
|
|
218
|
+
f' text-transform: uppercase;'
|
|
219
|
+
f' letter-spacing: 0.5px;'
|
|
220
|
+
f' padding: 8px 0;'
|
|
221
|
+
f' border-bottom: 2px solid #dee2e6;'
|
|
222
|
+
f'">{header}</div>',
|
|
223
|
+
unsafe_allow_html=True,
|
|
224
|
+
)
|
|
225
|
+
return header_cols
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
def render_empty_state(
|
|
229
|
+
message: str,
|
|
230
|
+
icon: Optional[str] = None,
|
|
231
|
+
action_label: Optional[str] = None,
|
|
232
|
+
action_callback: Optional[Callable] = None,
|
|
233
|
+
):
|
|
234
|
+
"""Render an empty state message with optional action."""
|
|
235
|
+
icon_html = f'<div style="font-size: 48px; margin-bottom: 16px;">{icon}</div>' if icon else ''
|
|
236
|
+
|
|
237
|
+
st.markdown(
|
|
238
|
+
f'<div style="'
|
|
239
|
+
f' text-align: center;'
|
|
240
|
+
f' padding: 48px 24px;'
|
|
241
|
+
f' background-color: #f8f9fa;'
|
|
242
|
+
f' border-radius: 8px;'
|
|
243
|
+
f' margin: 24px 0;'
|
|
244
|
+
f'">'
|
|
245
|
+
f'{icon_html}'
|
|
246
|
+
f'<div style="color: #6c757d; font-size: 16px;">{message}</div>'
|
|
247
|
+
f'</div>',
|
|
248
|
+
unsafe_allow_html=True,
|
|
249
|
+
)
|
|
250
|
+
|
|
251
|
+
if action_label and action_callback:
|
|
252
|
+
col1, col2, col3 = st.columns([1, 1, 1])
|
|
253
|
+
with col2:
|
|
254
|
+
if st.button(action_label, key='empty_state_action', type='primary'):
|
|
255
|
+
action_callback()
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
def render_progress_bar(progress: float, label: Optional[str] = None, show_percentage: bool = True):
|
|
259
|
+
"""Render a custom progress bar with consistent styling."""
|
|
260
|
+
percentage = int(progress * 100)
|
|
261
|
+
|
|
262
|
+
label_html = f'<div style="font-size: 12px; color: #6c757d; margin-bottom: 4px;">{label}</div>' if label else ''
|
|
263
|
+
percentage_html = (
|
|
264
|
+
f'<span style="font-size: 12px; color: #495057; margin-left: 8px;">{percentage}%</span>'
|
|
265
|
+
if show_percentage
|
|
266
|
+
else ''
|
|
267
|
+
)
|
|
268
|
+
|
|
269
|
+
st.markdown(
|
|
270
|
+
f'{label_html}'
|
|
271
|
+
f'<div style="display: flex; align-items: center;">'
|
|
272
|
+
f'<div style="'
|
|
273
|
+
f' flex: 1;'
|
|
274
|
+
f' height: 8px;'
|
|
275
|
+
f' background-color: #e9ecef;'
|
|
276
|
+
f' border-radius: 4px;'
|
|
277
|
+
f' overflow: hidden;'
|
|
278
|
+
f'">'
|
|
279
|
+
f'<div style="'
|
|
280
|
+
f' width: {percentage}%;'
|
|
281
|
+
f' height: 100%;'
|
|
282
|
+
f' background: linear-gradient(90deg, #007bff 0%, #0056b3 100%);'
|
|
283
|
+
f' transition: width 0.3s ease;'
|
|
284
|
+
f'"></div>'
|
|
285
|
+
f'</div>'
|
|
286
|
+
f'{percentage_html}'
|
|
287
|
+
f'</div>',
|
|
288
|
+
unsafe_allow_html=True,
|
|
289
|
+
)
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"""Streamlit-based Synapse DevTools Application.
|
|
2
|
+
|
|
3
|
+
This module imports from the modularized structure.
|
|
4
|
+
The main application logic is now in streamlit_app/app.py
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from synapse_sdk.devtools.streamlit_app import main
|
|
8
|
+
|
|
9
|
+
if __name__ == '__main__':
|
|
10
|
+
main()
|
synapse_sdk/loggers.py
CHANGED
|
@@ -1,21 +1,48 @@
|
|
|
1
1
|
import datetime
|
|
2
2
|
import time
|
|
3
|
+
from typing import Any, Dict
|
|
3
4
|
|
|
4
5
|
from synapse_sdk.clients.exceptions import ClientError
|
|
5
6
|
|
|
6
7
|
|
|
7
8
|
class BaseLogger:
|
|
9
|
+
"""Base class for logging progress and events.
|
|
10
|
+
|
|
11
|
+
Args:
|
|
12
|
+
progress_record(dict): Progress record to track the progress of a task.
|
|
13
|
+
progress_categories: dict | None: List of categories for progress tracking.
|
|
14
|
+
metrics_categories: dict | None: List of categories for metrics tracking.
|
|
15
|
+
current_progress_category: str | None: Current progress category.
|
|
16
|
+
time_begin_per_category(dict): Dictionary to track the start time for each category.
|
|
17
|
+
metrics_record(dict): Dictionary to record metrics.
|
|
18
|
+
"""
|
|
19
|
+
|
|
8
20
|
progress_record = {}
|
|
21
|
+
metrics_record = {}
|
|
9
22
|
progress_categories = None
|
|
10
|
-
|
|
23
|
+
metrics_categories = None
|
|
24
|
+
current_progress_category = None
|
|
11
25
|
time_begin_per_category = {}
|
|
12
26
|
|
|
13
|
-
def __init__(self, progress_categories=None):
|
|
27
|
+
def __init__(self, progress_categories=None, metrics_categories=None):
|
|
28
|
+
# Setup progress categories
|
|
14
29
|
self.progress_categories = progress_categories
|
|
15
30
|
if progress_categories:
|
|
16
31
|
self.progress_record['categories'] = progress_categories
|
|
17
32
|
|
|
18
|
-
|
|
33
|
+
# Setup metrics categories
|
|
34
|
+
self.metrics_categories = metrics_categories
|
|
35
|
+
if metrics_categories:
|
|
36
|
+
self.metrics_record['categories'] = metrics_categories
|
|
37
|
+
|
|
38
|
+
def set_progress(self, current: int, total: int, category: str | None = None):
|
|
39
|
+
"""Set progress for plugin run.
|
|
40
|
+
|
|
41
|
+
Args:
|
|
42
|
+
current(int): current progress value
|
|
43
|
+
total(int): total progress value
|
|
44
|
+
category(str | None): progress category
|
|
45
|
+
"""
|
|
19
46
|
assert 0 <= current <= total and total > 0
|
|
20
47
|
assert category is not None or 'categories' not in self.progress_record
|
|
21
48
|
|
|
@@ -32,11 +59,42 @@ class BaseLogger:
|
|
|
32
59
|
current_progress = {'percent': percent, 'time_remaining': time_remaining}
|
|
33
60
|
|
|
34
61
|
if category:
|
|
35
|
-
self.
|
|
62
|
+
self.current_progress_category = category
|
|
36
63
|
self.progress_record['categories'][category].update(current_progress)
|
|
37
64
|
else:
|
|
38
65
|
self.progress_record.update(current_progress)
|
|
39
66
|
|
|
67
|
+
def set_progress_failed(self, category: str | None = None):
|
|
68
|
+
"""Mark progress as failed with elapsed time but no completion.
|
|
69
|
+
|
|
70
|
+
This method should be called when an operation fails to indicate that
|
|
71
|
+
no progress was made, but still track how long the operation ran before failing.
|
|
72
|
+
|
|
73
|
+
Args:
|
|
74
|
+
category(str | None): progress category
|
|
75
|
+
"""
|
|
76
|
+
assert category is not None or 'categories' not in self.progress_record
|
|
77
|
+
|
|
78
|
+
# Calculate elapsed time if start time was recorded
|
|
79
|
+
elapsed_time = None
|
|
80
|
+
if category in self.time_begin_per_category:
|
|
81
|
+
elapsed_time = time.time() - self.time_begin_per_category[category]
|
|
82
|
+
elapsed_time = round(elapsed_time, 2)
|
|
83
|
+
|
|
84
|
+
# Progress is 0% (not completed), no time remaining, but track elapsed time
|
|
85
|
+
failed_progress = {
|
|
86
|
+
'percent': 0.0,
|
|
87
|
+
'time_remaining': None,
|
|
88
|
+
'elapsed_time': elapsed_time,
|
|
89
|
+
'status': 'failed',
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if category:
|
|
93
|
+
self.current_progress_category = category
|
|
94
|
+
self.progress_record['categories'][category].update(failed_progress)
|
|
95
|
+
else:
|
|
96
|
+
self.progress_record.update(failed_progress)
|
|
97
|
+
|
|
40
98
|
def get_current_progress(self):
|
|
41
99
|
categories = self.progress_record.get('categories')
|
|
42
100
|
|
|
@@ -45,15 +103,15 @@ class BaseLogger:
|
|
|
45
103
|
|
|
46
104
|
overall = 0
|
|
47
105
|
for category, category_record in categories.items():
|
|
48
|
-
if category == self.
|
|
106
|
+
if category == self.current_progress_category:
|
|
49
107
|
break
|
|
50
108
|
overall += category_record['proportion']
|
|
51
109
|
|
|
52
|
-
category_record = categories[self.
|
|
110
|
+
category_record = categories[self.current_progress_category]
|
|
53
111
|
category_percent = category_record.get('percent', 0)
|
|
54
112
|
if not category_progress and 'percent' in category_record:
|
|
55
113
|
category_progress = {
|
|
56
|
-
'category': self.
|
|
114
|
+
'category': self.current_progress_category,
|
|
57
115
|
'percent': category_percent,
|
|
58
116
|
'time_remaining': category_record.get('time_remaining'),
|
|
59
117
|
}
|
|
@@ -68,13 +126,41 @@ class BaseLogger:
|
|
|
68
126
|
|
|
69
127
|
return progress
|
|
70
128
|
|
|
129
|
+
def set_metrics(self, value: Dict[Any, Any], category: str):
|
|
130
|
+
"""Set metrics for plugin run.
|
|
131
|
+
|
|
132
|
+
* Metrics which are representing the progress of the plugin run should be set in the metrics_record.
|
|
133
|
+
|
|
134
|
+
Args:
|
|
135
|
+
value(Dict[Any, Any]): metrics value
|
|
136
|
+
category(str): metrics category
|
|
137
|
+
"""
|
|
138
|
+
assert category is not None and category != '', 'A category argument must be a non-empty string.'
|
|
139
|
+
assert isinstance(value, dict), f'A value argument must be a dictionary, but got {type(value).__name__}.'
|
|
140
|
+
|
|
141
|
+
if 'categories' not in self.metrics_record:
|
|
142
|
+
self.metrics_record['categories'] = {}
|
|
143
|
+
|
|
144
|
+
self.metrics_record['categories'].setdefault(category, {}).update(value)
|
|
145
|
+
|
|
146
|
+
def log(self, action, data, file=None):
|
|
147
|
+
raise NotImplementedError
|
|
148
|
+
|
|
71
149
|
|
|
72
150
|
class ConsoleLogger(BaseLogger):
|
|
73
151
|
def set_progress(self, current, total, category=None):
|
|
74
152
|
super().set_progress(current, total, category=category)
|
|
75
153
|
print(self.get_current_progress())
|
|
76
154
|
|
|
77
|
-
def
|
|
155
|
+
def set_progress_failed(self, category: str | None = None):
|
|
156
|
+
super().set_progress_failed(category=category)
|
|
157
|
+
print(self.get_current_progress())
|
|
158
|
+
|
|
159
|
+
def set_metrics(self, value: Dict[Any, Any], category: str):
|
|
160
|
+
super().set_metrics(value, category)
|
|
161
|
+
print(self.metrics_record)
|
|
162
|
+
|
|
163
|
+
def log(self, action, data, file=None):
|
|
78
164
|
print(action, data)
|
|
79
165
|
|
|
80
166
|
|
|
@@ -99,7 +185,28 @@ class BackendLogger(BaseLogger):
|
|
|
99
185
|
except ClientError:
|
|
100
186
|
pass
|
|
101
187
|
|
|
102
|
-
def
|
|
188
|
+
def set_progress_failed(self, category: str | None = None):
|
|
189
|
+
super().set_progress_failed(category=category)
|
|
190
|
+
try:
|
|
191
|
+
progress_record = {
|
|
192
|
+
'record': self.progress_record,
|
|
193
|
+
'current_progress': self.get_current_progress(),
|
|
194
|
+
}
|
|
195
|
+
self.client.update_job(self.job_id, data={'progress_record': progress_record})
|
|
196
|
+
except ClientError:
|
|
197
|
+
pass
|
|
198
|
+
|
|
199
|
+
def set_metrics(self, value: Dict[Any, Any], category: str):
|
|
200
|
+
super().set_metrics(value, category)
|
|
201
|
+
try:
|
|
202
|
+
metrics_record = {
|
|
203
|
+
'record': self.metrics_record,
|
|
204
|
+
}
|
|
205
|
+
self.client.update_job(self.job_id, data={'metrics_record': metrics_record})
|
|
206
|
+
except ClientError:
|
|
207
|
+
pass
|
|
208
|
+
|
|
209
|
+
def log(self, event, data, file=None):
|
|
103
210
|
print(event, data)
|
|
104
211
|
|
|
105
212
|
log = {
|
|
@@ -108,7 +215,11 @@ class BackendLogger(BaseLogger):
|
|
|
108
215
|
'datetime': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S.%f'),
|
|
109
216
|
'job': self.job_id,
|
|
110
217
|
}
|
|
218
|
+
if file:
|
|
219
|
+
log['file'] = file
|
|
220
|
+
|
|
111
221
|
self.logs_queue.append(log)
|
|
222
|
+
|
|
112
223
|
try:
|
|
113
224
|
self.client.create_logs(self.logs_queue)
|
|
114
225
|
self.logs_queue.clear()
|