synapse-sdk 1.0.0a75__py3-none-any.whl → 1.0.0a76__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 synapse-sdk might be problematic. Click here for more details.
- synapse_sdk/devtools/docs/sidebars.ts +1 -1
- synapse_sdk/plugins/utils/__init__.py +43 -0
- synapse_sdk/plugins/utils/actions.py +119 -0
- synapse_sdk/plugins/utils/config.py +203 -0
- synapse_sdk/plugins/utils/legacy.py +95 -0
- synapse_sdk/plugins/utils/registry.py +58 -0
- synapse_sdk/plugins/utils.py +27 -0
- {synapse_sdk-1.0.0a75.dist-info → synapse_sdk-1.0.0a76.dist-info}/METADATA +36 -1
- {synapse_sdk-1.0.0a75.dist-info → synapse_sdk-1.0.0a76.dist-info}/RECORD +13 -8
- {synapse_sdk-1.0.0a75.dist-info → synapse_sdk-1.0.0a76.dist-info}/WHEEL +0 -0
- {synapse_sdk-1.0.0a75.dist-info → synapse_sdk-1.0.0a76.dist-info}/entry_points.txt +0 -0
- {synapse_sdk-1.0.0a75.dist-info → synapse_sdk-1.0.0a76.dist-info}/licenses/LICENSE +0 -0
- {synapse_sdk-1.0.0a75.dist-info → synapse_sdk-1.0.0a76.dist-info}/top_level.txt +0 -0
|
@@ -22,6 +22,7 @@ const sidebars: SidebarsConfig = {
|
|
|
22
22
|
label: 'Features',
|
|
23
23
|
items: [
|
|
24
24
|
'features/features',
|
|
25
|
+
'features/plugins/plugins',
|
|
25
26
|
'features/converters/converters',
|
|
26
27
|
],
|
|
27
28
|
},
|
|
@@ -36,7 +37,6 @@ const sidebars: SidebarsConfig = {
|
|
|
36
37
|
'configuration',
|
|
37
38
|
'troubleshooting',
|
|
38
39
|
'faq',
|
|
39
|
-
'changelog',
|
|
40
40
|
'contributing',
|
|
41
41
|
],
|
|
42
42
|
};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# New utilities
|
|
2
|
+
from .actions import (
|
|
3
|
+
get_action,
|
|
4
|
+
get_action_class,
|
|
5
|
+
get_available_actions,
|
|
6
|
+
is_action_available,
|
|
7
|
+
)
|
|
8
|
+
from .config import (
|
|
9
|
+
get_action_config,
|
|
10
|
+
get_plugin_actions,
|
|
11
|
+
get_plugin_metadata,
|
|
12
|
+
read_plugin_config,
|
|
13
|
+
validate_plugin_config,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
# Import legacy functions for backward compatibility
|
|
17
|
+
from .legacy import read_requirements, run_plugin
|
|
18
|
+
from .registry import (
|
|
19
|
+
get_category_display_name,
|
|
20
|
+
get_plugin_categories,
|
|
21
|
+
is_valid_category,
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
__all__ = [
|
|
25
|
+
# Config utilities
|
|
26
|
+
'get_plugin_actions',
|
|
27
|
+
'get_action_config',
|
|
28
|
+
'read_plugin_config',
|
|
29
|
+
'validate_plugin_config',
|
|
30
|
+
'get_plugin_metadata',
|
|
31
|
+
# Action utilities
|
|
32
|
+
'get_action',
|
|
33
|
+
'get_action_class',
|
|
34
|
+
'get_available_actions',
|
|
35
|
+
'is_action_available',
|
|
36
|
+
# Registry utilities
|
|
37
|
+
'get_plugin_categories',
|
|
38
|
+
'is_valid_category',
|
|
39
|
+
'get_category_display_name',
|
|
40
|
+
# Legacy utilities for backward compatibility
|
|
41
|
+
'read_requirements',
|
|
42
|
+
'run_plugin',
|
|
43
|
+
]
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
"""Plugin action utilities."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
from typing import Any, Dict, Union
|
|
5
|
+
|
|
6
|
+
from synapse_sdk.plugins.categories.registry import _REGISTERED_ACTIONS, register_actions
|
|
7
|
+
from synapse_sdk.utils.file import get_dict_from_file
|
|
8
|
+
|
|
9
|
+
from .config import read_plugin_config
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def get_action(action: str, params_data: Union[str, Dict[str, Any]], *args, **kwargs):
|
|
13
|
+
"""Get a plugin action instance with validated parameters.
|
|
14
|
+
|
|
15
|
+
Args:
|
|
16
|
+
action: Name of the action to get.
|
|
17
|
+
params_data: Parameters as string (JSON/file path) or dictionary.
|
|
18
|
+
*args: Additional positional arguments.
|
|
19
|
+
**kwargs: Additional keyword arguments including 'config'.
|
|
20
|
+
|
|
21
|
+
Returns:
|
|
22
|
+
Configured action instance ready for execution.
|
|
23
|
+
|
|
24
|
+
Raises:
|
|
25
|
+
ActionError: If parameters are invalid or action not found.
|
|
26
|
+
"""
|
|
27
|
+
if isinstance(params_data, str):
|
|
28
|
+
try:
|
|
29
|
+
params = json.loads(params_data)
|
|
30
|
+
except json.JSONDecodeError:
|
|
31
|
+
params = get_dict_from_file(params_data)
|
|
32
|
+
else:
|
|
33
|
+
params = params_data
|
|
34
|
+
|
|
35
|
+
config_data = kwargs.pop('config', False)
|
|
36
|
+
if config_data:
|
|
37
|
+
if isinstance(config_data, str):
|
|
38
|
+
config = read_plugin_config(plugin_path=config_data)
|
|
39
|
+
else:
|
|
40
|
+
config = config_data
|
|
41
|
+
else:
|
|
42
|
+
config = read_plugin_config()
|
|
43
|
+
|
|
44
|
+
category = config['category']
|
|
45
|
+
return get_action_class(category, action)(params, config, *args, **kwargs)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def get_action_class(category: str, action: str):
|
|
49
|
+
"""Get action class by category and action name.
|
|
50
|
+
|
|
51
|
+
Args:
|
|
52
|
+
category: Plugin category (e.g., 'neural_net', 'export').
|
|
53
|
+
action: Action name (e.g., 'train', 'inference').
|
|
54
|
+
|
|
55
|
+
Returns:
|
|
56
|
+
Action class ready for instantiation.
|
|
57
|
+
|
|
58
|
+
Raises:
|
|
59
|
+
KeyError: If category or action not found in registry.
|
|
60
|
+
"""
|
|
61
|
+
register_actions()
|
|
62
|
+
try:
|
|
63
|
+
return _REGISTERED_ACTIONS[category][action]
|
|
64
|
+
except KeyError as e:
|
|
65
|
+
if category not in _REGISTERED_ACTIONS:
|
|
66
|
+
available_categories = list(_REGISTERED_ACTIONS.keys())
|
|
67
|
+
raise KeyError(f"Category '{category}' not found. Available categories: {available_categories}") from e
|
|
68
|
+
else:
|
|
69
|
+
available_actions = list(_REGISTERED_ACTIONS[category].keys())
|
|
70
|
+
raise KeyError(
|
|
71
|
+
f"Action '{action}' not found in category '{category}'. Available actions: {available_actions}"
|
|
72
|
+
) from e
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def get_available_actions(category: str) -> list:
|
|
76
|
+
"""Get list of available actions for a plugin category.
|
|
77
|
+
|
|
78
|
+
Args:
|
|
79
|
+
category: Plugin category to get actions for.
|
|
80
|
+
|
|
81
|
+
Returns:
|
|
82
|
+
List of available action names.
|
|
83
|
+
|
|
84
|
+
Raises:
|
|
85
|
+
KeyError: If category not found in registry.
|
|
86
|
+
|
|
87
|
+
Examples:
|
|
88
|
+
>>> get_available_actions('neural_net')
|
|
89
|
+
['train', 'inference', 'test', 'deployment', 'gradio', 'tune']
|
|
90
|
+
"""
|
|
91
|
+
register_actions()
|
|
92
|
+
if category not in _REGISTERED_ACTIONS:
|
|
93
|
+
available_categories = list(_REGISTERED_ACTIONS.keys())
|
|
94
|
+
raise KeyError(f"Category '{category}' not found. Available categories: {available_categories}")
|
|
95
|
+
|
|
96
|
+
return list(_REGISTERED_ACTIONS[category].keys())
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def is_action_available(category: str, action: str) -> bool:
|
|
100
|
+
"""Check if an action is available in a given category.
|
|
101
|
+
|
|
102
|
+
Args:
|
|
103
|
+
category: Plugin category to check.
|
|
104
|
+
action: Action name to check.
|
|
105
|
+
|
|
106
|
+
Returns:
|
|
107
|
+
True if action is available, False otherwise.
|
|
108
|
+
|
|
109
|
+
Examples:
|
|
110
|
+
>>> is_action_available('neural_net', 'train')
|
|
111
|
+
True
|
|
112
|
+
>>> is_action_available('neural_net', 'nonexistent')
|
|
113
|
+
False
|
|
114
|
+
"""
|
|
115
|
+
try:
|
|
116
|
+
available_actions = get_available_actions(category)
|
|
117
|
+
return action in available_actions
|
|
118
|
+
except KeyError:
|
|
119
|
+
return False
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
"""Plugin configuration utilities."""
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from typing import Any, Dict, List, Optional, Union
|
|
5
|
+
|
|
6
|
+
from synapse_sdk.utils.file import get_dict_from_file
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def read_plugin_config(plugin_path: Optional[Union[str, Path]] = None) -> Dict[str, Any]:
|
|
10
|
+
"""Read plugin configuration from config.yaml file.
|
|
11
|
+
|
|
12
|
+
Args:
|
|
13
|
+
plugin_path: Path to plugin directory. If None, looks for config.yaml in current directory.
|
|
14
|
+
|
|
15
|
+
Returns:
|
|
16
|
+
Dict containing plugin configuration.
|
|
17
|
+
|
|
18
|
+
Raises:
|
|
19
|
+
FileNotFoundError: If config.yaml file is not found.
|
|
20
|
+
ValueError: If config.yaml is invalid.
|
|
21
|
+
"""
|
|
22
|
+
config_file_name = 'config.yaml'
|
|
23
|
+
if plugin_path:
|
|
24
|
+
config_path = Path(plugin_path) / config_file_name
|
|
25
|
+
else:
|
|
26
|
+
config_path = config_file_name
|
|
27
|
+
|
|
28
|
+
try:
|
|
29
|
+
return get_dict_from_file(config_path)
|
|
30
|
+
except FileNotFoundError:
|
|
31
|
+
raise FileNotFoundError(f'Plugin config file not found: {config_path}')
|
|
32
|
+
except Exception as e:
|
|
33
|
+
raise ValueError(f'Invalid plugin config file: {e}')
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def get_plugin_actions(
|
|
37
|
+
config: Optional[Dict[str, Any]] = None, plugin_path: Optional[Union[str, Path]] = None
|
|
38
|
+
) -> List[str]:
|
|
39
|
+
"""Get list of action names from plugin configuration.
|
|
40
|
+
|
|
41
|
+
Args:
|
|
42
|
+
config: Plugin configuration dictionary. If None, reads from plugin_path.
|
|
43
|
+
plugin_path: Path to plugin directory. Used if config is None.
|
|
44
|
+
|
|
45
|
+
Returns:
|
|
46
|
+
List of action names defined in the plugin.
|
|
47
|
+
|
|
48
|
+
Raises:
|
|
49
|
+
ValueError: If neither config nor plugin_path is provided.
|
|
50
|
+
KeyError: If 'actions' key is missing from config.
|
|
51
|
+
|
|
52
|
+
Examples:
|
|
53
|
+
>>> get_plugin_actions(plugin_path="./my-plugin")
|
|
54
|
+
['train', 'inference', 'test']
|
|
55
|
+
|
|
56
|
+
>>> config = {'actions': {'train': {...}, 'test': {...}}}
|
|
57
|
+
>>> get_plugin_actions(config=config)
|
|
58
|
+
['train', 'test']
|
|
59
|
+
"""
|
|
60
|
+
if config is None:
|
|
61
|
+
if plugin_path is None:
|
|
62
|
+
raise ValueError('Either config or plugin_path must be provided')
|
|
63
|
+
config = read_plugin_config(plugin_path)
|
|
64
|
+
|
|
65
|
+
if 'actions' not in config:
|
|
66
|
+
raise KeyError("'actions' key not found in plugin configuration")
|
|
67
|
+
|
|
68
|
+
actions = config['actions']
|
|
69
|
+
if not isinstance(actions, dict):
|
|
70
|
+
raise ValueError("'actions' must be a dictionary")
|
|
71
|
+
|
|
72
|
+
return list(actions.keys())
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def get_action_config(
|
|
76
|
+
action_name: str, config: Optional[Dict[str, Any]] = None, plugin_path: Optional[Union[str, Path]] = None
|
|
77
|
+
) -> Dict[str, Any]:
|
|
78
|
+
"""Get configuration for a specific action.
|
|
79
|
+
|
|
80
|
+
Args:
|
|
81
|
+
action_name: Name of the action to get config for.
|
|
82
|
+
config: Plugin configuration dictionary. If None, reads from plugin_path.
|
|
83
|
+
plugin_path: Path to plugin directory. Used if config is None.
|
|
84
|
+
|
|
85
|
+
Returns:
|
|
86
|
+
Dictionary containing action configuration.
|
|
87
|
+
|
|
88
|
+
Raises:
|
|
89
|
+
ValueError: If neither config nor plugin_path is provided.
|
|
90
|
+
KeyError: If action is not found in plugin configuration.
|
|
91
|
+
|
|
92
|
+
Examples:
|
|
93
|
+
>>> get_action_config('train', plugin_path="./my-plugin")
|
|
94
|
+
{'entrypoint': 'plugin.train.TrainAction', 'method': 'job'}
|
|
95
|
+
"""
|
|
96
|
+
if config is None:
|
|
97
|
+
if plugin_path is None:
|
|
98
|
+
raise ValueError('Either config or plugin_path must be provided')
|
|
99
|
+
config = read_plugin_config(plugin_path)
|
|
100
|
+
|
|
101
|
+
if 'actions' not in config:
|
|
102
|
+
raise KeyError("'actions' key not found in plugin configuration")
|
|
103
|
+
|
|
104
|
+
actions = config['actions']
|
|
105
|
+
if action_name not in actions:
|
|
106
|
+
available_actions = list(actions.keys())
|
|
107
|
+
raise KeyError(f"Action '{action_name}' not found. Available actions: {available_actions}")
|
|
108
|
+
|
|
109
|
+
return actions[action_name]
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def validate_plugin_config(config: Dict[str, Any]) -> bool:
|
|
113
|
+
"""Validate plugin configuration structure.
|
|
114
|
+
|
|
115
|
+
Args:
|
|
116
|
+
config: Plugin configuration dictionary to validate.
|
|
117
|
+
|
|
118
|
+
Returns:
|
|
119
|
+
True if configuration is valid.
|
|
120
|
+
|
|
121
|
+
Raises:
|
|
122
|
+
ValueError: If configuration is invalid with detailed error message.
|
|
123
|
+
|
|
124
|
+
Examples:
|
|
125
|
+
>>> config = {
|
|
126
|
+
... 'name': 'My Plugin',
|
|
127
|
+
... 'code': 'my-plugin',
|
|
128
|
+
... 'version': '1.0.0',
|
|
129
|
+
... 'category': 'neural_net',
|
|
130
|
+
... 'actions': {'train': {'entrypoint': 'plugin.train.TrainAction'}}
|
|
131
|
+
... }
|
|
132
|
+
>>> validate_plugin_config(config)
|
|
133
|
+
True
|
|
134
|
+
"""
|
|
135
|
+
required_fields = ['name', 'code', 'version', 'category', 'actions']
|
|
136
|
+
|
|
137
|
+
# Check required fields
|
|
138
|
+
for field in required_fields:
|
|
139
|
+
if field not in config:
|
|
140
|
+
raise ValueError(f"Required field '{field}' missing from plugin configuration")
|
|
141
|
+
|
|
142
|
+
# Validate actions structure
|
|
143
|
+
actions = config['actions']
|
|
144
|
+
if not isinstance(actions, dict):
|
|
145
|
+
raise ValueError("'actions' must be a dictionary")
|
|
146
|
+
|
|
147
|
+
if not actions:
|
|
148
|
+
raise ValueError('Plugin must define at least one action')
|
|
149
|
+
|
|
150
|
+
# Validate each action
|
|
151
|
+
for action_name, action_config in actions.items():
|
|
152
|
+
if not isinstance(action_config, dict):
|
|
153
|
+
raise ValueError(f"Action '{action_name}' configuration must be a dictionary")
|
|
154
|
+
|
|
155
|
+
# Check for entrypoint (required for most actions)
|
|
156
|
+
if 'entrypoint' not in action_config and action_config.get('method') != 'restapi':
|
|
157
|
+
raise ValueError(f"Action '{action_name}' missing required 'entrypoint' field")
|
|
158
|
+
|
|
159
|
+
# Validate category
|
|
160
|
+
from synapse_sdk.plugins.enums import PluginCategory
|
|
161
|
+
|
|
162
|
+
valid_categories = [cat.value for cat in PluginCategory]
|
|
163
|
+
if config['category'] not in valid_categories:
|
|
164
|
+
raise ValueError(f"Invalid category '{config['category']}'. Must be one of: {valid_categories}")
|
|
165
|
+
|
|
166
|
+
return True
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def get_plugin_metadata(
|
|
170
|
+
config: Optional[Dict[str, Any]] = None, plugin_path: Optional[Union[str, Path]] = None
|
|
171
|
+
) -> Dict[str, Any]:
|
|
172
|
+
"""Get plugin metadata (name, version, description, etc.).
|
|
173
|
+
|
|
174
|
+
Args:
|
|
175
|
+
config: Plugin configuration dictionary. If None, reads from plugin_path.
|
|
176
|
+
plugin_path: Path to plugin directory. Used if config is None.
|
|
177
|
+
|
|
178
|
+
Returns:
|
|
179
|
+
Dictionary containing plugin metadata.
|
|
180
|
+
|
|
181
|
+
Examples:
|
|
182
|
+
>>> get_plugin_metadata(plugin_path="./my-plugin")
|
|
183
|
+
{
|
|
184
|
+
'name': 'My Plugin',
|
|
185
|
+
'code': 'my-plugin',
|
|
186
|
+
'version': '1.0.0',
|
|
187
|
+
'category': 'neural_net',
|
|
188
|
+
'description': 'A custom ML plugin'
|
|
189
|
+
}
|
|
190
|
+
"""
|
|
191
|
+
if config is None:
|
|
192
|
+
if plugin_path is None:
|
|
193
|
+
raise ValueError('Either config or plugin_path must be provided')
|
|
194
|
+
config = read_plugin_config(plugin_path)
|
|
195
|
+
|
|
196
|
+
metadata_fields = ['name', 'code', 'version', 'category', 'description']
|
|
197
|
+
metadata = {}
|
|
198
|
+
|
|
199
|
+
for field in metadata_fields:
|
|
200
|
+
if field in config:
|
|
201
|
+
metadata[field] = config[field]
|
|
202
|
+
|
|
203
|
+
return metadata
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
"""Legacy utility functions for backward compatibility."""
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
from synapse_sdk.i18n import gettext as _
|
|
6
|
+
from synapse_sdk.plugins.exceptions import ActionError
|
|
7
|
+
|
|
8
|
+
from .actions import get_action
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def read_requirements(file_path):
|
|
12
|
+
"""Read and parse a requirements.txt file.
|
|
13
|
+
|
|
14
|
+
Args:
|
|
15
|
+
file_path: Path to the requirements.txt file
|
|
16
|
+
|
|
17
|
+
Returns:
|
|
18
|
+
List of requirement strings, or None if file doesn't exist
|
|
19
|
+
"""
|
|
20
|
+
file_path = Path(file_path)
|
|
21
|
+
if not file_path.exists():
|
|
22
|
+
return None
|
|
23
|
+
|
|
24
|
+
requirements = []
|
|
25
|
+
for line in file_path.read_text().splitlines():
|
|
26
|
+
stripped_line = line.strip()
|
|
27
|
+
if stripped_line and not stripped_line.startswith('#'):
|
|
28
|
+
requirements.append(stripped_line)
|
|
29
|
+
return requirements
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def run_plugin(
|
|
33
|
+
action,
|
|
34
|
+
params,
|
|
35
|
+
plugin_config=None,
|
|
36
|
+
plugin_path=None,
|
|
37
|
+
modules=None,
|
|
38
|
+
requirements=None,
|
|
39
|
+
envs=None,
|
|
40
|
+
debug=False,
|
|
41
|
+
**kwargs,
|
|
42
|
+
):
|
|
43
|
+
"""Execute a plugin action with the specified parameters.
|
|
44
|
+
|
|
45
|
+
Args:
|
|
46
|
+
action: The action name to execute
|
|
47
|
+
params: Parameters for the action
|
|
48
|
+
plugin_config: Plugin configuration dictionary
|
|
49
|
+
plugin_path: Path to the plugin directory
|
|
50
|
+
modules: List of modules for debugging
|
|
51
|
+
requirements: List of requirements
|
|
52
|
+
envs: Environment variables dictionary
|
|
53
|
+
debug: Whether to run in debug mode
|
|
54
|
+
**kwargs: Additional keyword arguments
|
|
55
|
+
|
|
56
|
+
Returns:
|
|
57
|
+
Result of the action execution
|
|
58
|
+
"""
|
|
59
|
+
from synapse_sdk.plugins.models import PluginRelease
|
|
60
|
+
|
|
61
|
+
if not envs:
|
|
62
|
+
envs = {}
|
|
63
|
+
|
|
64
|
+
if debug:
|
|
65
|
+
if plugin_path and plugin_path.startswith('http'):
|
|
66
|
+
if not plugin_config:
|
|
67
|
+
raise ActionError({'config': _('"plugin_path"가 url인 경우에는 "config"가 필수입니다.')})
|
|
68
|
+
plugin_release = PluginRelease(config=plugin_config)
|
|
69
|
+
else:
|
|
70
|
+
plugin_release = PluginRelease(plugin_path=plugin_path)
|
|
71
|
+
plugin_config = plugin_release.config
|
|
72
|
+
|
|
73
|
+
if action not in plugin_release.actions:
|
|
74
|
+
raise ActionError({'action': _('해당 액션은 존재하지 않습니다.')})
|
|
75
|
+
|
|
76
|
+
if plugin_path:
|
|
77
|
+
envs['SYNAPSE_DEBUG_PLUGIN_PATH'] = plugin_path
|
|
78
|
+
|
|
79
|
+
if modules:
|
|
80
|
+
envs['SYNAPSE_DEBUG_MODULES'] = ','.join(modules)
|
|
81
|
+
|
|
82
|
+
else:
|
|
83
|
+
if plugin_config is None:
|
|
84
|
+
raise ActionError({'config': _('플러그인 설정은 필수입니다.')})
|
|
85
|
+
|
|
86
|
+
action = get_action(
|
|
87
|
+
action,
|
|
88
|
+
params,
|
|
89
|
+
config=plugin_config,
|
|
90
|
+
requirements=requirements,
|
|
91
|
+
envs=envs,
|
|
92
|
+
debug=debug,
|
|
93
|
+
**kwargs,
|
|
94
|
+
)
|
|
95
|
+
return action.run_action()
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"""Plugin registry utilities."""
|
|
2
|
+
|
|
3
|
+
from typing import List
|
|
4
|
+
|
|
5
|
+
from synapse_sdk.plugins.enums import PluginCategory
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def get_plugin_categories() -> List[str]:
|
|
9
|
+
"""Get list of all available plugin categories.
|
|
10
|
+
|
|
11
|
+
Returns:
|
|
12
|
+
List of plugin category names.
|
|
13
|
+
|
|
14
|
+
Examples:
|
|
15
|
+
>>> get_plugin_categories()
|
|
16
|
+
['neural_net', 'export', 'upload', 'smart_tool', 'post_annotation', 'pre_annotation', 'data_validation']
|
|
17
|
+
"""
|
|
18
|
+
return [plugin_category.value for plugin_category in PluginCategory]
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def is_valid_category(category: str) -> bool:
|
|
22
|
+
"""Check if a category is valid.
|
|
23
|
+
|
|
24
|
+
Args:
|
|
25
|
+
category: Category name to validate.
|
|
26
|
+
|
|
27
|
+
Returns:
|
|
28
|
+
True if category is valid, False otherwise.
|
|
29
|
+
|
|
30
|
+
Examples:
|
|
31
|
+
>>> is_valid_category('neural_net')
|
|
32
|
+
True
|
|
33
|
+
>>> is_valid_category('invalid_category')
|
|
34
|
+
False
|
|
35
|
+
"""
|
|
36
|
+
return category in get_plugin_categories()
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def get_category_display_name(category: str) -> str:
|
|
40
|
+
"""Get human-readable display name for a category.
|
|
41
|
+
|
|
42
|
+
Args:
|
|
43
|
+
category: Category name.
|
|
44
|
+
|
|
45
|
+
Returns:
|
|
46
|
+
Human-readable category name.
|
|
47
|
+
|
|
48
|
+
Examples:
|
|
49
|
+
>>> get_category_display_name('neural_net')
|
|
50
|
+
'Neural Net'
|
|
51
|
+
>>> get_category_display_name('data_validation')
|
|
52
|
+
'Data Validation'
|
|
53
|
+
"""
|
|
54
|
+
try:
|
|
55
|
+
plugin_category = PluginCategory(category)
|
|
56
|
+
return plugin_category.name.replace('_', ' ').title()
|
|
57
|
+
except ValueError:
|
|
58
|
+
return category.replace('_', ' ').title()
|
synapse_sdk/plugins/utils.py
CHANGED
|
@@ -43,7 +43,34 @@ def get_plugin_categories():
|
|
|
43
43
|
return [plugin_category.value for plugin_category in PluginCategory]
|
|
44
44
|
|
|
45
45
|
|
|
46
|
+
def get_plugin_actions(config=None, plugin_path=None):
|
|
47
|
+
"""Get list of action names from plugin configuration.
|
|
48
|
+
|
|
49
|
+
Args:
|
|
50
|
+
config: Plugin configuration dictionary. If None, reads from plugin_path.
|
|
51
|
+
plugin_path: Path to plugin directory. Used if config is None.
|
|
52
|
+
|
|
53
|
+
Returns:
|
|
54
|
+
List of action names defined in the plugin.
|
|
55
|
+
"""
|
|
56
|
+
if config is None:
|
|
57
|
+
if plugin_path is None:
|
|
58
|
+
config = read_plugin_config()
|
|
59
|
+
else:
|
|
60
|
+
config = read_plugin_config(plugin_path)
|
|
61
|
+
|
|
62
|
+
if 'actions' not in config:
|
|
63
|
+
raise KeyError("'actions' key not found in plugin configuration")
|
|
64
|
+
|
|
65
|
+
actions = config['actions']
|
|
66
|
+
if not isinstance(actions, dict):
|
|
67
|
+
raise ValueError("'actions' must be a dictionary")
|
|
68
|
+
|
|
69
|
+
return list(actions.keys())
|
|
70
|
+
|
|
71
|
+
|
|
46
72
|
def read_plugin_config(plugin_path=None):
|
|
73
|
+
"""Legacy function for backward compatibility."""
|
|
47
74
|
config_file_name = 'config.yaml'
|
|
48
75
|
if plugin_path:
|
|
49
76
|
config_path = Path(plugin_path) / config_file_name
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: synapse-sdk
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.0a76
|
|
4
4
|
Summary: synapse sdk
|
|
5
5
|
Author-email: datamaker <developer@datamaker.io>
|
|
6
6
|
License: MIT
|
|
@@ -67,6 +67,41 @@ pip install synapse-sdk
|
|
|
67
67
|
synapse --help
|
|
68
68
|
```
|
|
69
69
|
|
|
70
|
+
## 🔍 Code Review
|
|
71
|
+
|
|
72
|
+
This repository uses systematic code review with P1-P4 priority rules:
|
|
73
|
+
|
|
74
|
+
### Using the Review-PR Command
|
|
75
|
+
|
|
76
|
+
Review pull requests using the integrated review system:
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
# Review a PR with English comments
|
|
80
|
+
/review-pr 123
|
|
81
|
+
|
|
82
|
+
# Review a PR with Korean comments
|
|
83
|
+
/review-pr 123 ko
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### Code Review Priority Levels
|
|
87
|
+
|
|
88
|
+
- **[P1_rules.md](P1_rules.md)** - Security and Stability (Critical) 🔴
|
|
89
|
+
- **[P2_rules.md](P2_rules.md)** - Core Functionality (High Priority) 🟡
|
|
90
|
+
- **[P3_rules.md](P3_rules.md)** - Best Practices (Medium Priority) 🟠
|
|
91
|
+
- **[P4_rules.md](P4_rules.md)** - Code Style (Low Priority) 🔵
|
|
92
|
+
|
|
93
|
+
### Review Process
|
|
94
|
+
|
|
95
|
+
1. **Automated Analysis**: The review-pr command systematically applies P1-P4 rules
|
|
96
|
+
2. **Priority-Based Feedback**: Issues are categorized by severity and impact
|
|
97
|
+
3. **Actionable Comments**: Each issue includes specific recommendations and rule references
|
|
98
|
+
4. **Language Support**: Comments can be generated in English or Korean
|
|
99
|
+
5. **Decision Logic**:
|
|
100
|
+
- P1, P2, or P3 violations → Request Changes
|
|
101
|
+
- Only P4 violations or no issues → Approve
|
|
102
|
+
|
|
103
|
+
See [AGENT.md](AGENT.md) for complete development guidelines and code review rules.
|
|
104
|
+
|
|
70
105
|
## 📚 Documentation
|
|
71
106
|
|
|
72
107
|
*Docs [https://docs.synapse.sh](https://docs.synapse.sh)*
|
|
@@ -53,7 +53,7 @@ synapse_sdk/devtools/docs/README.md,sha256=yBzWf0K1ef4oymFXDaHo0nYWEgMQJqsOyrkNh
|
|
|
53
53
|
synapse_sdk/devtools/docs/docusaurus.config.ts,sha256=zH1dxP4hmKsYWTo1OYMBIeE0Cj5dC8KChVtkORNQB5E,3129
|
|
54
54
|
synapse_sdk/devtools/docs/package-lock.json,sha256=FfI_0BxNDB3tupLYxsH2PY4ogaBL6p22ujFWy8rRu88,653289
|
|
55
55
|
synapse_sdk/devtools/docs/package.json,sha256=9UcQc49bh7CM1e8Cc4XYPWxRvHaZSXkecheWIC8Aohk,1144
|
|
56
|
-
synapse_sdk/devtools/docs/sidebars.ts,sha256=
|
|
56
|
+
synapse_sdk/devtools/docs/sidebars.ts,sha256=Yd6ccVA8HaNaUuQa8H37BPu5PDw-bmhbiZJnv4bmQz4,996
|
|
57
57
|
synapse_sdk/devtools/docs/tsconfig.json,sha256=O9BNlRPjPiaVHW2_boShMbmTnh0Z2k0KQO6Alf9FMVY,215
|
|
58
58
|
synapse_sdk/devtools/docs/blog/2019-05-28-first-blog-post.md,sha256=iP7gl_FPqo-qX13lkSRcRoT6ayJNmCkXoyvlm7GH248,312
|
|
59
59
|
synapse_sdk/devtools/docs/blog/2019-05-29-long-blog-post.md,sha256=cM-dhhTeurEWMcdn0Kx-NpNts2YUUraSI_XFk_gVHEE,3122
|
|
@@ -110,7 +110,7 @@ synapse_sdk/plugins/enums.py,sha256=ibixwqA3sCNSriG1jAtL54JQc_Zwo3MufwYUqGhVncc,
|
|
|
110
110
|
synapse_sdk/plugins/exceptions.py,sha256=Qs7qODp_RRLO9y2otU2T4ryj5LFwIZODvSIXkAh91u0,691
|
|
111
111
|
synapse_sdk/plugins/models.py,sha256=AKZfVT6hsVEklcEDnHwoVAwvLxydMibfeJetug3Qk0U,4738
|
|
112
112
|
synapse_sdk/plugins/upload.py,sha256=VJOotYMayylOH0lNoAGeGHRkLdhP7jnC_A0rFQMvQpQ,3228
|
|
113
|
-
synapse_sdk/plugins/utils.py,sha256=
|
|
113
|
+
synapse_sdk/plugins/utils.py,sha256=039LZ1KjrBwGV-PnCnvbUr6ek6CNg8X67ccPuiQmVJg,4142
|
|
114
114
|
synapse_sdk/plugins/categories/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
115
115
|
synapse_sdk/plugins/categories/base.py,sha256=ZBgRh3tTdSnqBrzjux8oi1_3PidHMmpmLClPXiTmU3Q,10690
|
|
116
116
|
synapse_sdk/plugins/categories/decorators.py,sha256=Gw6T-UHwpCKrSt596X-g2sZbY_Z1zbbogowClj7Pr5Q,518
|
|
@@ -182,6 +182,11 @@ synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/config
|
|
|
182
182
|
synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/pyproject.toml,sha256=Usgd80tHZAD1Ug5MAjPfETUZxtKKgZW-xovFEAEbQDo,317
|
|
183
183
|
synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/requirements.txt,sha256=F5UwinpTLQFfyakFGTFxgBOo4H-EKD9d4e77WKOPHhk,17
|
|
184
184
|
synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/plugin/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
185
|
+
synapse_sdk/plugins/utils/__init__.py,sha256=h868T8mxZDgo746W5u0YUB_biHhPov0sr4usFjefYJw,988
|
|
186
|
+
synapse_sdk/plugins/utils/actions.py,sha256=QjzISxUMyrcFtOphaYP8tdAkAAzOUZlfnMbVwR_zt2I,3736
|
|
187
|
+
synapse_sdk/plugins/utils/config.py,sha256=uyGp9GhphQE-b6sla3NwMUH0DeBunvi7szycRj-TMsE,6735
|
|
188
|
+
synapse_sdk/plugins/utils/legacy.py,sha256=UWEk5FHk_AqU4GxhfyKJ76VgBUHS-ktKV6_jTJCgT8k,2689
|
|
189
|
+
synapse_sdk/plugins/utils/registry.py,sha256=HKALzYcPQSFsdLAzodYXMdfFnKOcg6oHYBrx7EwVqNU,1484
|
|
185
190
|
synapse_sdk/shared/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
186
191
|
synapse_sdk/shared/enums.py,sha256=j90R9JblcBjaG1OPIVKiHPH_J2UvsBNSPc8Mp4Agv78,158
|
|
187
192
|
synapse_sdk/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -216,9 +221,9 @@ synapse_sdk/utils/storage/providers/gcp.py,sha256=i2BQCu1Kej1If9SuNr2_lEyTcr5M_n
|
|
|
216
221
|
synapse_sdk/utils/storage/providers/http.py,sha256=2DhIulND47JOnS5ZY7MZUex7Su3peAPksGo1Wwg07L4,5828
|
|
217
222
|
synapse_sdk/utils/storage/providers/s3.py,sha256=ZmqekAvIgcQBdRU-QVJYv1Rlp6VHfXwtbtjTSphua94,2573
|
|
218
223
|
synapse_sdk/utils/storage/providers/sftp.py,sha256=_8s9hf0JXIO21gvm-JVS00FbLsbtvly4c-ETLRax68A,1426
|
|
219
|
-
synapse_sdk-1.0.
|
|
220
|
-
synapse_sdk-1.0.
|
|
221
|
-
synapse_sdk-1.0.
|
|
222
|
-
synapse_sdk-1.0.
|
|
223
|
-
synapse_sdk-1.0.
|
|
224
|
-
synapse_sdk-1.0.
|
|
224
|
+
synapse_sdk-1.0.0a76.dist-info/licenses/LICENSE,sha256=bKzmC5YAg4V1Fhl8OO_tqY8j62hgdncAkN7VrdjmrGk,1101
|
|
225
|
+
synapse_sdk-1.0.0a76.dist-info/METADATA,sha256=As3anxeM7NReBSvXnTprXQG-yqgVpYhRlZL95bWMzeM,3805
|
|
226
|
+
synapse_sdk-1.0.0a76.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
227
|
+
synapse_sdk-1.0.0a76.dist-info/entry_points.txt,sha256=VNptJoGoNJI8yLXfBmhgUefMsmGI0m3-0YoMvrOgbxo,48
|
|
228
|
+
synapse_sdk-1.0.0a76.dist-info/top_level.txt,sha256=ytgJMRK1slVOKUpgcw3LEyHHP7S34J6n_gJzdkcSsw8,12
|
|
229
|
+
synapse_sdk-1.0.0a76.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|