synapse-sdk 1.0.0a37__py3-none-any.whl → 1.0.0a39__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.

@@ -11,6 +11,7 @@ from synapse_sdk.utils.file import files_url_to_path_from_objs
11
11
  class BaseClient:
12
12
  name = None
13
13
  base_url = None
14
+ page_size = 100
14
15
 
15
16
  def __init__(self, base_url):
16
17
  self.base_url = base_url
@@ -146,7 +147,8 @@ class BaseClient:
146
147
  else:
147
148
  return response
148
149
 
149
- def _list_all(self, path, url_conversion=None, params=None, **kwargs):
150
+ def _list_all(self, path, url_conversion=None, params={}, **kwargs):
151
+ params['page_size'] = self.page_size
150
152
  response = self._get(path, url_conversion, params=params, **kwargs)
151
153
  yield from response['results']
152
154
  if response['next']:
@@ -133,6 +133,11 @@ class Action:
133
133
  def plugin_package_manager(self):
134
134
  return self.plugin_config.get('package_manager', 'pip')
135
135
 
136
+ @property
137
+ def package_manager_options(self):
138
+ options = {'pip': {}, 'uv': {'uv_pip_install_options': []}}
139
+ return options[self.plugin_package_manager]
140
+
136
141
  def get_run(self):
137
142
  context = {
138
143
  'plugin_release': self.plugin_release,
@@ -147,13 +152,16 @@ class Action:
147
152
  return {env: os.environ[env] for env in self.REQUIRED_ENVS if env in os.environ}
148
153
 
149
154
  def get_runtime_env(self):
150
- runtime_env = {self.plugin_package_manager: [], 'working_dir': self.plugin_url}
155
+ runtime_env = {self.plugin_package_manager: {'packages': []}, 'working_dir': self.plugin_url}
151
156
 
152
157
  if self.requirements:
153
- runtime_env[self.plugin_package_manager] += self.requirements
158
+ runtime_env[self.plugin_package_manager]['packages'] += self.requirements
154
159
 
155
160
  if self.debug:
156
- runtime_env[self.plugin_package_manager] += self.debug_modules
161
+ runtime_env[self.plugin_package_manager]['packages'] += self.debug_modules
162
+
163
+ for key, value in self.package_manager_options.items():
164
+ runtime_env[self.plugin_package_manager][key] = value
157
165
 
158
166
  # 맨 마지막에 진행되어야 함
159
167
  runtime_env['env_vars'] = self.envs
@@ -1,7 +1,4 @@
1
- from packaging import version
2
-
3
1
  from synapse_sdk.clients.exceptions import ClientError
4
- from synapse_sdk.i18n import gettext as _
5
2
  from synapse_sdk.plugins.categories.base import Action
6
3
  from synapse_sdk.plugins.categories.decorators import register_action
7
4
  from synapse_sdk.plugins.enums import PluginCategory, RunMethod
@@ -30,28 +27,9 @@ class DeploymentAction(Action):
30
27
 
31
28
  ray_actor_options = self.get_actor_options()
32
29
 
33
- if self.is_gradio_deployment:
34
- from ray.serve.gradio_integrations import GradioServer
35
-
36
- self.assert_gradio_version()
37
-
38
- # GradioIngress differs from serve.ingress(app), thus the difference in self.entrypoint callable
39
- try:
40
- entrypoint = self.entrypoint().app
41
- except (TypeError, ImportError):
42
- raise ClientError(
43
- 400,
44
- _(
45
- 'Gradio app is not callable.'
46
- 'Please ensure that your Deployment class defines a callable `app` function'
47
- ),
48
- )
49
-
50
- deployment = GradioServer.options(ray_actor_options=ray_actor_options).bind(entrypoint)
51
- else:
52
- deployment = serve.deployment(ray_actor_options=ray_actor_options)(
53
- serve.ingress(app)(self.entrypoint)
54
- ).bind(self.envs['SYNAPSE_PLUGIN_RUN_HOST'])
30
+ deployment = serve.deployment(ray_actor_options=ray_actor_options)(serve.ingress(app)(self.entrypoint)).bind(
31
+ self.envs['SYNAPSE_PLUGIN_RUN_HOST']
32
+ )
55
33
 
56
34
  serve.delete(self.plugin_release.code)
57
35
 
@@ -78,30 +56,3 @@ class DeploymentAction(Action):
78
56
  except ClientError:
79
57
  pass
80
58
  return None
81
-
82
- @property
83
- def is_gradio_deployment(self):
84
- return self.config.get('gradio_app', False)
85
-
86
- def assert_gradio_version(self):
87
- """Assert gradio version is not greater than 3.50.2.
88
- Ray Serve cannot pickle gradio endpoints, thus gradio version greater than 3.50.2 is not supported (SSE Issues)
89
- """
90
- GRADIO_VERSION_MAX_ALLOWED = '3.50.2'
91
-
92
- gradio_installed = False
93
- gradio_version = None
94
- for req in self.requirements:
95
- if req.startswith('gradio=='):
96
- gradio_installed = True
97
- gradio_version = req.split('==')[1]
98
- break
99
-
100
- assert gradio_installed, _(
101
- 'Gradio is not installed or version is not specified. Please install gradio==3.50.2 to use this feature.'
102
- )
103
-
104
- if version.parse(gradio_version) > version.parse(GRADIO_VERSION_MAX_ALLOWED):
105
- raise AssertionError(
106
- f'Gradio version {gradio_version} is greater than maximum allowed version {GRADIO_VERSION_MAX_ALLOWED}'
107
- )
@@ -0,0 +1,149 @@
1
+ import contextlib
2
+ import subprocess
3
+ from functools import cached_property
4
+ from pathlib import Path
5
+
6
+ from synapse_sdk.plugins.categories.base import Action
7
+ from synapse_sdk.plugins.categories.decorators import register_action
8
+ from synapse_sdk.plugins.enums import PluginCategory, RunMethod
9
+
10
+
11
+ @register_action
12
+ class GradioAction(Action):
13
+ name = 'gradio'
14
+ category = PluginCategory.NEURAL_NET
15
+ method = RunMethod.JOB
16
+
17
+ @property
18
+ def working_directory(self):
19
+ dir = Path.cwd() / self.config['directory'].replace('.', '/')
20
+ assert dir.is_dir(), f'Working directory {dir} does not exist.'
21
+ return dir
22
+
23
+ @property
24
+ def _requirements(self):
25
+ return self.config.get('requirements', ['gradio>=5'])
26
+
27
+ @property
28
+ def tag(self):
29
+ _tag = f'{self.plugin_release.code}-{self.plugin_release.checksum}'
30
+ return _tag.replace('@', '-')
31
+
32
+ @cached_property
33
+ def deploy_port(self):
34
+ return self._get_avail_ports_host()
35
+
36
+ def deploy(self):
37
+ self.run.log('deploy', 'Start deploying')
38
+
39
+ try:
40
+ # Write Dockerfile and requirements.txt
41
+ path_dockerfile = self.write_dockerfile_template()
42
+ self.write_requirements(path_dockerfile.parent / 'requirements.txt')
43
+
44
+ # Build docker image
45
+ self.build_docker_image(path_dockerfile)
46
+
47
+ # Run docker image
48
+ self.run_docker_image()
49
+ except Exception as e:
50
+ self.run.log('deploy', f'Error: {e}')
51
+ raise e
52
+
53
+ def start(self):
54
+ self.deploy()
55
+ return {'endpoint': f'http://localhost:{self.deploy_port}'}
56
+
57
+ def write_dockerfile_template(self):
58
+ dockerfile_path = self.working_directory / 'Dockerfile'
59
+
60
+ with open(dockerfile_path, 'w') as f:
61
+ f.write("""FROM python:3.10
62
+ WORKDIR /home/user/app
63
+
64
+ RUN pip install --no-cache-dir pip -U && \\
65
+ pip install --no-cache-dir uvicorn
66
+
67
+ RUN apt-get update && \\
68
+ apt-get install -y curl && \\
69
+ curl -fsSL https://deb.nodesource.com/setup_22.x | bash - && \\
70
+ apt-get install -y nodejs && \\
71
+ rm -rf /var/lib/apt/lists/* && \\
72
+ apt-get clean
73
+
74
+ COPY . /home/user/app
75
+
76
+ RUN pip install --no-cache-dir -r requirements.txt
77
+
78
+ EXPOSE 7860
79
+
80
+ CMD ["python", "app.py"]
81
+ """)
82
+ return dockerfile_path
83
+
84
+ def write_requirements(self, path):
85
+ with open(path, 'w') as f:
86
+ f.write('\n'.join(self._requirements))
87
+
88
+ def build_docker_image(self, path_dockerfile):
89
+ self.run.log('deploy', 'Start building docker image')
90
+ result = subprocess.run(
91
+ [
92
+ 'docker',
93
+ 'build',
94
+ '-t',
95
+ self.tag,
96
+ '-f',
97
+ str(path_dockerfile),
98
+ '.',
99
+ ],
100
+ cwd=self.working_directory,
101
+ check=True,
102
+ )
103
+ print(result)
104
+
105
+ @staticmethod
106
+ def _get_avail_ports_host(start_port=8900, end_port=8999):
107
+ import nmap
108
+
109
+ nm = nmap.PortScanner()
110
+
111
+ scan_range = f'{start_port}-{end_port}'
112
+ nm.scan(hosts='host.docker.internal', arguments=f'-p {scan_range}')
113
+
114
+ try:
115
+ open_ports = nm['host.docker.internal']['tcp'].keys()
116
+ open_ports = [int(port) for port in open_ports]
117
+ except KeyError:
118
+ open_ports = []
119
+
120
+ for port in range(start_port, end_port + 1):
121
+ if port not in open_ports:
122
+ return port
123
+
124
+ raise IOError(f'No free ports available in range {start_port}-{end_port}')
125
+
126
+ def run_docker_image(self):
127
+ self.run.log('deploy', 'Start running docker image')
128
+
129
+ # Check for existing container
130
+ self.run.log('deploy', 'Check for existing container')
131
+ with contextlib.suppress(subprocess.CalledProcessError):
132
+ subprocess.run(['docker', 'stop', self.tag], check=True)
133
+ subprocess.run(['docker', 'rm', self.tag], check=True)
134
+
135
+ # Run docker image
136
+ self.run.log('deploy', 'Starting docker container')
137
+ subprocess.run(
138
+ [
139
+ 'docker',
140
+ 'run',
141
+ '-d',
142
+ '--name',
143
+ self.tag,
144
+ '-p',
145
+ f'{self.deploy_port}:7860',
146
+ self.tag,
147
+ ],
148
+ check=True,
149
+ )
@@ -0,0 +1,268 @@
1
+ import copy
2
+ import tempfile
3
+ from pathlib import Path
4
+ from typing import Annotated, Optional
5
+
6
+ from pydantic import AfterValidator, BaseModel, field_validator
7
+ from pydantic_core import PydanticCustomError
8
+
9
+ from synapse_sdk.clients.exceptions import ClientError
10
+ from synapse_sdk.plugins.categories.decorators import register_action
11
+ from synapse_sdk.plugins.categories.neural_net.actions.train import TrainAction, TrainRun
12
+ from synapse_sdk.plugins.enums import PluginCategory, RunMethod
13
+ from synapse_sdk.utils.file import archive
14
+ from synapse_sdk.utils.module_loading import import_string
15
+ from synapse_sdk.utils.pydantic.validators import non_blank
16
+
17
+
18
+ class TuneRun(TrainRun):
19
+ is_tune = True
20
+ completed_samples = 0
21
+ num_samples = 0
22
+ checkpoint_output = None
23
+
24
+
25
+ class TuneConfig(BaseModel):
26
+ mode: Optional[str] = None
27
+ metric: Optional[str] = None
28
+ num_samples: int = 1
29
+ max_concurrent_trials: Optional[int] = None
30
+
31
+
32
+ class TuneParams(BaseModel):
33
+ name: Annotated[str, AfterValidator(non_blank)]
34
+ description: str
35
+ checkpoint: int | None
36
+ dataset: int
37
+ tune_config: TuneConfig
38
+
39
+ @field_validator('name')
40
+ @staticmethod
41
+ def unique_name(value, info):
42
+ action = info.context['action']
43
+ client = action.client
44
+ try:
45
+ job_exists = client.exists(
46
+ 'list_jobs',
47
+ params={
48
+ 'ids_ex': action.job_id,
49
+ 'category': 'neural_net',
50
+ 'job__action': 'tune',
51
+ 'is_active': True,
52
+ 'params': f'name:{value}',
53
+ },
54
+ )
55
+ assert not job_exists, '존재하는 튜닝 작업 이름입니다.'
56
+ except ClientError:
57
+ raise PydanticCustomError('client_error', '')
58
+ return value
59
+
60
+
61
+ @register_action
62
+ class TuneAction(TrainAction):
63
+ """
64
+ **Must read** Important notes before using Tune:
65
+
66
+ 1. Path to the model output (which is the return value of your train function)
67
+ should be set to the checkpoint_output attribute of the run object **before**
68
+ starting the training.
69
+ 2. Before exiting the training function, report the results to Tune.
70
+ 3. When using own tune.py, take note of the difference in the order of parameters.
71
+ tune() function starts with hyperparameter, run, dataset, checkpoint, **kwargs
72
+ whereas the train() function starts with run, dataset, hyperparameter, checkpoint, **kwargs.
73
+ ----
74
+ 1)
75
+ Set the output path for the checkpoint to export best model
76
+
77
+ output_path = Path('path/to/your/weights')
78
+ run.checkpoint_output = str(output_path)
79
+
80
+ 2)
81
+ Before exiting the training function, report the results to Tune.
82
+ The results_dict should contain the metrics you want to report.
83
+
84
+ Example: (In train function)
85
+ results_dict = {
86
+ "accuracy": accuracy,
87
+ "loss": loss,
88
+ # Add other metrics as needed
89
+ }
90
+ if hasattr(self.dm_run, 'is_tune') and self.dm_run.is_tune:
91
+ tune.report(results_dict, checkpoint=tune.Checkpoint.from_directory(self.dm_run.checkpoint_output))
92
+
93
+
94
+ 3)
95
+ tune() function takes hyperparameter, run, dataset, checkpoint, **kwargs in that order
96
+ whereas train() function takes run, dataset, hyperparameter, checkpoint, **kwargs in that order.
97
+
98
+ --------------------------------------------------------------------------------------------------------
99
+
100
+ **중요** Tune 사용 전 반드시 읽어야 할 사항들
101
+
102
+ 1. 본 플러그인의 train 함수에서, 학습을 진행하기 코드 전에
103
+ 결과 모델 파일의 경로(train함수의 리턴 값)을 checkpoint_output 속성에 설정해야 합니다.
104
+ 2. 학습이 종료되기 전에, 결과를 Tune에 보고해야 합니다.
105
+ 3. 플러그인에서 tune.py를 직접 생성해서 사용할 시, 매개변수의 순서가 다릅니다.
106
+
107
+ ----
108
+ 1)
109
+ 체크포인트를 설정할 경로를 지정합니다.
110
+ output_path = Path('path/to/your/weights')
111
+ run.checkpoint_output = str(output_path)
112
+
113
+ 2)
114
+ 학습이 종료되기 전에, 결과를 Tune에 보고합니다.
115
+ results_dict = {
116
+ "accuracy": accuracy,
117
+ "loss": loss,
118
+ # 필요한 다른 메트릭 추가
119
+ }
120
+ if hasattr(self.dm_run, 'is_tune') and self.dm_run.is_tune:
121
+ tune.report(results_dict, checkpoint=tune.Checkpoint.from_directory(self.dm_run.checkpoint_output))
122
+
123
+ 3)
124
+ tune() 함수는 hyperparameter, run, dataset, checkpoint, **kwargs 순서이고
125
+ train() 함수는 run, dataset, hyperparameter, checkpoint, **kwargs 순서입니다.
126
+ """
127
+
128
+ name = 'tune'
129
+ category = PluginCategory.NEURAL_NET
130
+ method = RunMethod.JOB
131
+ run_class = TuneRun
132
+ params_model = TuneParams
133
+ progress_categories = {
134
+ 'dataset': {
135
+ 'proportion': 5,
136
+ },
137
+ 'trials': {
138
+ 'proportion': 90,
139
+ },
140
+ 'model_upload': {
141
+ 'proportion': 5,
142
+ },
143
+ }
144
+
145
+ def start(self):
146
+ from ray import tune
147
+
148
+ # download dataset
149
+ self.run.log_message('Preparing dataset for hyperparameter tuning.')
150
+ input_dataset = self.get_dataset()
151
+
152
+ # retrieve checkpoint
153
+ checkpoint = None
154
+ if self.params['checkpoint']:
155
+ self.run.log_message('Retrieving checkpoint.')
156
+ checkpoint = self.get_model(self.params['checkpoint'])
157
+
158
+ # train dataset
159
+ self.run.log_message('Starting training for hyperparameter tuning.')
160
+
161
+ # Save num_samples to TuneRun for logging
162
+ self.run.num_samples = self.params['tune_config']['num_samples']
163
+
164
+ entrypoint = self.entrypoint
165
+ if not self._tune_override_exists():
166
+ # entrypoint must be train entrypoint
167
+ def _tune(param_space, run, dataset, checkpoint=None, **kwargs):
168
+ return entrypoint(run, dataset, param_space, checkpoint, **kwargs)
169
+
170
+ entrypoint = _tune
171
+
172
+ trainable = tune.with_parameters(entrypoint, run=self.run, dataset=input_dataset, checkpoint=checkpoint)
173
+ tune_config = self.params['tune_config']
174
+
175
+ hyperparameter = self.params['hyperparameter']
176
+ param_space = self.convert_tune_params(hyperparameter)
177
+ temp_path = tempfile.TemporaryDirectory()
178
+
179
+ tuner = tune.Tuner(
180
+ tune.with_resources(trainable, resources=self.tune_resources),
181
+ tune_config=tune.TuneConfig(**tune_config),
182
+ run_config=tune.RunConfig(
183
+ name=f'synapse_tune_hpo_{self.job_id}',
184
+ log_to_file=('stdout.log', 'stderr.log'),
185
+ storage_path=temp_path.name,
186
+ ),
187
+ param_space=param_space,
188
+ )
189
+ result = tuner.fit()
190
+
191
+ best_result = result.get_best_result()
192
+
193
+ # upload model_data
194
+ self.run.log_message('Registering best model data.')
195
+ self.run.set_progress(0, 1, category='model_upload')
196
+ self.create_model_from_result(best_result)
197
+ self.run.set_progress(1, 1, category='model_upload')
198
+
199
+ self.run.end_log()
200
+
201
+ return {'best_result': best_result.config}
202
+
203
+ @property
204
+ def tune_resources(self):
205
+ resources = {}
206
+ for option in ['num_cpus', 'num_gpus']:
207
+ option_value = self.params.get(option)
208
+ if option_value:
209
+ # Remove the 'num_' prefix and trailing s from the option name
210
+ resources[(lambda s: s[4:-1])(option)] = option_value
211
+ return resources
212
+
213
+ def create_model_from_result(self, result):
214
+ params = copy.deepcopy(self.params)
215
+ configuration_fields = ['hyperparameter']
216
+ configuration = {field: params.pop(field) for field in configuration_fields}
217
+
218
+ with tempfile.TemporaryDirectory() as temp_path:
219
+ archive_path = Path(temp_path, 'archive.zip')
220
+
221
+ # Archive tune results
222
+ # https://docs.ray.io/en/latest/tune/tutorials/tune_get_data_in_and_out.html#getting-data-out-of-tune-using-checkpoints-other-artifacts
223
+ archive(result.path, archive_path)
224
+
225
+ return self.client.create_model({
226
+ 'plugin': self.plugin_release.plugin,
227
+ 'version': self.plugin_release.version,
228
+ 'file': str(archive_path),
229
+ 'configuration': configuration,
230
+ **params,
231
+ })
232
+
233
+ @staticmethod
234
+ def convert_tune_params(param_list):
235
+ """
236
+ Convert YAML hyperparameter configuration to Ray Tune parameter dictionary.
237
+
238
+ Args:
239
+ param_list (list): List of hyperparameter configurations.
240
+
241
+ Returns:
242
+ dict: Ray Tune parameter dictionary
243
+ """
244
+ from ray import tune
245
+
246
+ param_space = {}
247
+
248
+ for param in param_list:
249
+ name = param['name']
250
+ param_type = param['type']
251
+
252
+ if param_type == 'loguniform':
253
+ param_space[name] = tune.loguniform(param['min'], param['max'])
254
+ elif param_type == 'choice':
255
+ param_space[name] = tune.choice(param['options'])
256
+ elif param_type == 'randint':
257
+ param_space[name] = tune.randint(param['min'], param['max'])
258
+ # Add more type handlers as needed
259
+
260
+ return param_space
261
+
262
+ @staticmethod
263
+ def _tune_override_exists(module_path='plugin.tune') -> bool:
264
+ try:
265
+ import_string(module_path)
266
+ return True
267
+ except ImportError:
268
+ return False
@@ -218,12 +218,14 @@ class UploadAction(Action):
218
218
  organized_files = uploader.handle_upload_files()
219
219
  if not self._validate_organized_files(file_specification_template, organized_files):
220
220
  self.run.log_message('Validate organized files failed.')
221
+ self.run.end_log()
221
222
  return result
222
223
 
223
224
  # Upload files to synapse-backend.
224
225
  organized_files_count = len(organized_files)
225
226
  if not organized_files_count:
226
227
  self.run.log_message('Files not found on the path.', context=Context.WARNING.value)
228
+ self.run.end_log()
227
229
  return result
228
230
  uploaded_files = self._upload_files(organized_files, organized_files_count)
229
231
  result['uploaded_files_count'] = len(uploaded_files)
@@ -232,6 +234,7 @@ class UploadAction(Action):
232
234
  upload_result_count = len(uploaded_files)
233
235
  if not upload_result_count:
234
236
  self.run.log_message('No files were uploaded.', context=Context.WARNING.value)
237
+ self.run.end_log()
235
238
  return result
236
239
  generated_data_units = self._generate_data_units(uploaded_files, upload_result_count)
237
240
  result['generated_data_units_count'] = len(generated_data_units)
@@ -239,6 +242,7 @@ class UploadAction(Action):
239
242
  # Setup task with uploaded synapse-backend data units.
240
243
  if not len(generated_data_units):
241
244
  self.run.log_message('No data units were generated.', context=Context.WARNING.value)
245
+ self.run.end_log()
242
246
  return result
243
247
 
244
248
  if self.config['options']['allow_generate_tasks'] and self.params['is_generate_tasks']:
@@ -255,6 +259,7 @@ class UploadAction(Action):
255
259
  else:
256
260
  self.run.log_message('Generating ground truths process has passed.')
257
261
 
262
+ self.run.end_log()
258
263
  return result
259
264
 
260
265
  def _analyze_collection(self) -> Dict:
@@ -45,6 +45,11 @@ class PluginRelease:
45
45
  def package_manager(self):
46
46
  return self.config.get('package_manager', 'pip')
47
47
 
48
+ @cached_property
49
+ def package_manager_options(self):
50
+ options = {'pip': {}, 'uv': {'uv_pip_install_options': []}}
51
+ return options[self.package_manager]
52
+
48
53
  @cached_property
49
54
  def checksum(self):
50
55
  return hash_text(self.code)
@@ -69,7 +74,10 @@ class PluginRelease:
69
74
 
70
75
  warm_up.options(
71
76
  runtime_env={
72
- self.package_manager: ['-r ${RAY_RUNTIME_ENV_CREATE_WORKING_DIR}/requirements.txt'],
77
+ self.package_manager: {
78
+ 'packages': ['-r ${RAY_RUNTIME_ENV_CREATE_WORKING_DIR}/requirements.txt']
79
+ ** self.package_manager_options
80
+ },
73
81
  'working_dir': self.get_url(self.envs['SYNAPSE_PLUGIN_STORAGE']),
74
82
  },
75
83
  scheduling_strategy=strategy,
synapse_sdk/utils/file.py CHANGED
@@ -4,6 +4,7 @@ import hashlib
4
4
  import json
5
5
  import mimetypes
6
6
  import operator
7
+ import re
7
8
  import zipfile
8
9
  from functools import reduce
9
10
  from pathlib import Path
@@ -150,11 +151,12 @@ def calculate_checksum(file_path, prefix=''):
150
151
  return checksum
151
152
 
152
153
 
153
- def archive(input_path, output_path):
154
+ def archive(input_path, output_path, append=False):
154
155
  input_path = Path(input_path)
155
156
  output_path = Path(output_path)
156
157
 
157
- with zipfile.ZipFile(output_path, mode='w', compression=zipfile.ZIP_DEFLATED) as zipf:
158
+ mode = 'a' if append and output_path.exists() else 'w'
159
+ with zipfile.ZipFile(output_path, mode=mode, compression=zipfile.ZIP_DEFLATED) as zipf:
158
160
  if input_path.is_file():
159
161
  zipf.write(input_path, input_path.name)
160
162
  else:
@@ -196,6 +198,10 @@ def convert_file_to_base64(file_path):
196
198
  Returns:
197
199
  str: Base64 encoded string of the file contents
198
200
  """
201
+ # FIXME base64 is sent sometimes.
202
+ if file_path.startswith('data:'):
203
+ return file_path
204
+
199
205
  # Convert string path to Path object
200
206
  path = Path(file_path)
201
207
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: synapse-sdk
3
- Version: 1.0.0a37
3
+ Version: 1.0.0a39
4
4
  Summary: synapse sdk
5
5
  Author-email: datamaker <developer@datamaker.io>
6
6
  License: MIT
@@ -21,6 +21,7 @@ Requires-Dist: universal-pathlib
21
21
  Requires-Dist: fsspec[gcs,s3,sftp]
22
22
  Provides-Extra: all
23
23
  Requires-Dist: ray[all]; extra == "all"
24
+ Requires-Dist: python-nmap; extra == "all"
24
25
  Dynamic: license-file
25
26
 
26
27
  This is the SDK to develop synapse plugins
@@ -21,7 +21,7 @@ synapse_sdk/cli/plugin/create.py,sha256=HpYTpohV1NbSrULaVUlc4jWLWznPrx7glgydTM3s
21
21
  synapse_sdk/cli/plugin/publish.py,sha256=sIl1wiuSC3lAUpE3rOF4UDKDy2G5EVLlelMjk2aT05g,1221
22
22
  synapse_sdk/cli/plugin/run.py,sha256=xz5LRm3zh8Y9DMjw5FFRFVRWSCWtYfZJskfCmrPikaQ,2598
23
23
  synapse_sdk/clients/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
24
- synapse_sdk/clients/base.py,sha256=RaQN_41NxFKtVsVKB7I7hdKJAR8ScuyCoK_x1D-U0c0,6399
24
+ synapse_sdk/clients/base.py,sha256=emtffTGGtPOSz6tT_NCL9cGE7ZEOv9pTHyc9BRfoe2s,6462
25
25
  synapse_sdk/clients/exceptions.py,sha256=ylv7x10eOp4aA3a48jwonnvqvkiYwzJYXjkVkRTAjwk,220
26
26
  synapse_sdk/clients/utils.py,sha256=8pPJTdzHiRPSbZMoQYHAgR2BAMO6u_R_jMV6a2p34iQ,392
27
27
  synapse_sdk/clients/agent/__init__.py,sha256=Pz8_iTbIbnb7ywGJ3feqoZVmO2I3mEbwpWsISIxh0BU,1968
@@ -44,11 +44,11 @@ synapse_sdk/clients/validators/collections.py,sha256=LtnwvutsScubOUcZ2reGHLCzseX
44
44
  synapse_sdk/plugins/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
45
45
  synapse_sdk/plugins/enums.py,sha256=ibixwqA3sCNSriG1jAtL54JQc_Zwo3MufwYUqGhVncc,523
46
46
  synapse_sdk/plugins/exceptions.py,sha256=Qs7qODp_RRLO9y2otU2T4ryj5LFwIZODvSIXkAh91u0,691
47
- synapse_sdk/plugins/models.py,sha256=njTQIT-c2d7TsqAN__q1aoYm8hLEPC7ludTj665iN-4,4148
47
+ synapse_sdk/plugins/models.py,sha256=FI_6Hr4q4hGj-GwHjucOfX4HYsUpraGd2yeuy4FjjC0,4438
48
48
  synapse_sdk/plugins/upload.py,sha256=VJOotYMayylOH0lNoAGeGHRkLdhP7jnC_A0rFQMvQpQ,3228
49
49
  synapse_sdk/plugins/utils.py,sha256=4_K6jIl0WrsXOEhFp94faMOriSsddOhIiaXcawYYUUA,3300
50
50
  synapse_sdk/plugins/categories/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
51
- synapse_sdk/plugins/categories/base.py,sha256=cfk7mgidCBnldZ8nCo_Nq_qzP9C1LJYgSXWtYT6hJDw,10046
51
+ synapse_sdk/plugins/categories/base.py,sha256=ATI1VjBWm2rimSkNiiCjfZn7FO4x2oltmh81pJJGL0w,10389
52
52
  synapse_sdk/plugins/categories/decorators.py,sha256=Gw6T-UHwpCKrSt596X-g2sZbY_Z1zbbogowClj7Pr5Q,518
53
53
  synapse_sdk/plugins/categories/registry.py,sha256=KdQR8SUlLT-3kgYzDNWawS1uJnAhrcw2j4zFaTpilRs,636
54
54
  synapse_sdk/plugins/categories/templates.py,sha256=FF5FerhkZMeW1YcKLY5cylC0SkWSYdJODA_Qcm4OGYQ,887
@@ -67,10 +67,12 @@ synapse_sdk/plugins/categories/export/templates/plugin/__init__.py,sha256=47DEQp
67
67
  synapse_sdk/plugins/categories/export/templates/plugin/export.py,sha256=39XLGo8ui5FscbwZyX3JwmrJqGGvOYrY3FMYDKXwTOQ,5192
68
68
  synapse_sdk/plugins/categories/neural_net/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
69
69
  synapse_sdk/plugins/categories/neural_net/actions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
70
- synapse_sdk/plugins/categories/neural_net/actions/deployment.py,sha256=oetIwZoee5vxriPX3r1onmxgwojUyaRTlnBIdaQ1zk8,3895
70
+ synapse_sdk/plugins/categories/neural_net/actions/deployment.py,sha256=y2LrS-pwazqRI5O0q1NUy45NQYsBj6ykbrXnDMs_fqE,1987
71
+ synapse_sdk/plugins/categories/neural_net/actions/gradio.py,sha256=Tb9vHZAmUXEi1Hp1CIn_XP4URERVuLGHOiQrlPztjy8,4326
71
72
  synapse_sdk/plugins/categories/neural_net/actions/inference.py,sha256=0a655ELqNVjPFZTJDiw4EUdcMCPGveUEKyoYqpwMFBU,1019
72
73
  synapse_sdk/plugins/categories/neural_net/actions/test.py,sha256=JY25eg-Fo6WbgtMkGoo_qNqoaZkp3AQNEypJmeGzEog,320
73
74
  synapse_sdk/plugins/categories/neural_net/actions/train.py,sha256=kve6iTCg2kUeavMQTR2JFuoYDu-QWZFFlB58ZICQtdM,5406
75
+ synapse_sdk/plugins/categories/neural_net/actions/tune.py,sha256=XJczlLDF8FOJXA-7TXNZa3npWhMsT0wGqQwYW3w5TDo,9475
74
76
  synapse_sdk/plugins/categories/neural_net/base/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
75
77
  synapse_sdk/plugins/categories/neural_net/base/inference.py,sha256=R5DASI6-5vzsjDOYxqeGGMBjnav5qHF4hNJT8zNUR3I,1097
76
78
  synapse_sdk/plugins/categories/neural_net/templates/config.yaml,sha256=uZVuXjIfsd_pTaSKptHeHn1TN2FIiLrvvpkClToc6po,596
@@ -98,7 +100,7 @@ synapse_sdk/plugins/categories/smart_tool/templates/plugin/__init__.py,sha256=47
98
100
  synapse_sdk/plugins/categories/smart_tool/templates/plugin/auto_label.py,sha256=eevNg0nOcYFR4z_L_R-sCvVOYoLWSAH1jwDkAf3YCjY,320
99
101
  synapse_sdk/plugins/categories/upload/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
100
102
  synapse_sdk/plugins/categories/upload/actions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
101
- synapse_sdk/plugins/categories/upload/actions/upload.py,sha256=pWIFi2MC6lk7jn-JflFRq-CBz_-L2tCyV5j5MiUGcy0,14604
103
+ synapse_sdk/plugins/categories/upload/actions/upload.py,sha256=9DIH4Aw70LxDpfhrpD0MfncE1m9oj-v52FpaChkVEnA,14755
102
104
  synapse_sdk/plugins/categories/upload/templates/config.yaml,sha256=0PhB2uD-9ufavZs7EiF6xj4aBgZuif9mFFGGfzG7HuY,147
103
105
  synapse_sdk/plugins/categories/upload/templates/plugin/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
104
106
  synapse_sdk/plugins/categories/upload/templates/plugin/upload.py,sha256=dnK8gy33GjG5ettayawDJv1gM3xCm1K6lM-PfeeTjQw,1163
@@ -117,7 +119,7 @@ synapse_sdk/shared/enums.py,sha256=WMZPag9deVF7VCXaQkLk7ly_uX1KwbNzRx9TdvgaeFE,1
117
119
  synapse_sdk/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
118
120
  synapse_sdk/utils/dataset.py,sha256=zWTzFmv589izFr62BDuApi3r5FpTsdm-5AmriC0AEdM,1865
119
121
  synapse_sdk/utils/debug.py,sha256=F7JlUwYjTFZAMRbBqKm6hxOIz-_IXYA8lBInOS4jbS4,100
120
- synapse_sdk/utils/file.py,sha256=zP8eOZifGiYP9PyC4ivQwxs-ljbtXRtbWN4yOjZF6tc,6658
122
+ synapse_sdk/utils/file.py,sha256=Wu8ixvEHglNXCVxT-vZTt7vUFkAAK1M1EJSk89P_3FQ,6844
121
123
  synapse_sdk/utils/module_loading.py,sha256=chHpU-BZjtYaTBD_q0T7LcKWtqKvYBS4L0lPlKkoMQ8,1020
122
124
  synapse_sdk/utils/network.py,sha256=wg-oFM0gKK5REqIUO8d-x9yXJfqbnkSbbF0_qyxpwz4,412
123
125
  synapse_sdk/utils/string.py,sha256=rEwuZ9SAaZLcQ8TYiwNKr1h2u4CfnrQx7SUL8NWmChg,216
@@ -131,9 +133,9 @@ synapse_sdk/utils/storage/providers/__init__.py,sha256=x7RGwZryT2FpVxS7fGWryRVpq
131
133
  synapse_sdk/utils/storage/providers/gcp.py,sha256=i2BQCu1Kej1If9SuNr2_lEyTcr5M_ncGITZrL0u5wEA,363
132
134
  synapse_sdk/utils/storage/providers/s3.py,sha256=W94rQvhGRXti3R4mYP7gmU5pcyCQpGFIBLvxxqLVdRM,2231
133
135
  synapse_sdk/utils/storage/providers/sftp.py,sha256=_8s9hf0JXIO21gvm-JVS00FbLsbtvly4c-ETLRax68A,1426
134
- synapse_sdk-1.0.0a37.dist-info/licenses/LICENSE,sha256=bKzmC5YAg4V1Fhl8OO_tqY8j62hgdncAkN7VrdjmrGk,1101
135
- synapse_sdk-1.0.0a37.dist-info/METADATA,sha256=cF_kPT0_20rYN69skm1lRu3JCmMK9AtI-_yBQv8wQbg,1160
136
- synapse_sdk-1.0.0a37.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
137
- synapse_sdk-1.0.0a37.dist-info/entry_points.txt,sha256=VNptJoGoNJI8yLXfBmhgUefMsmGI0m3-0YoMvrOgbxo,48
138
- synapse_sdk-1.0.0a37.dist-info/top_level.txt,sha256=ytgJMRK1slVOKUpgcw3LEyHHP7S34J6n_gJzdkcSsw8,12
139
- synapse_sdk-1.0.0a37.dist-info/RECORD,,
136
+ synapse_sdk-1.0.0a39.dist-info/licenses/LICENSE,sha256=bKzmC5YAg4V1Fhl8OO_tqY8j62hgdncAkN7VrdjmrGk,1101
137
+ synapse_sdk-1.0.0a39.dist-info/METADATA,sha256=KiYa0ezmc90xuDLUMsnMSGoLQoskOtwsKBR1Fp4dbDY,1203
138
+ synapse_sdk-1.0.0a39.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
139
+ synapse_sdk-1.0.0a39.dist-info/entry_points.txt,sha256=VNptJoGoNJI8yLXfBmhgUefMsmGI0m3-0YoMvrOgbxo,48
140
+ synapse_sdk-1.0.0a39.dist-info/top_level.txt,sha256=ytgJMRK1slVOKUpgcw3LEyHHP7S34J6n_gJzdkcSsw8,12
141
+ synapse_sdk-1.0.0a39.dist-info/RECORD,,