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

@@ -0,0 +1,21 @@
1
+ from synapse_sdk.clients.agent.service import ServiceClientMixin
2
+
3
+
4
+ class AgentClient(ServiceClientMixin):
5
+ name = 'Agent'
6
+ agent_token = None
7
+ user_token = None
8
+ tenant = None
9
+
10
+ def __init__(self, base_url, agent_token, user_token, tenant):
11
+ super().__init__(base_url)
12
+ self.agent_token = agent_token
13
+ self.user_token = user_token
14
+ self.tenant = tenant
15
+
16
+ def _get_headers(self):
17
+ return {
18
+ 'Authorization': self.agent_token,
19
+ 'SYNAPSE-User': f'Token {self.user_token}',
20
+ 'SYNAPSE-Tenant': f'Token {self.tenant}',
21
+ }
@@ -0,0 +1,54 @@
1
+ from synapse_sdk.clients.base import BaseClient
2
+
3
+
4
+ class ServiceClientMixin(BaseClient):
5
+ def run_plugin_release(self, code, data):
6
+ path = f'plugin_releases/{code}/run/'
7
+ return self._post(path, data=data)
8
+
9
+ def run_debug_plugin_release(self, data):
10
+ path = 'plugin_releases/run_debug/'
11
+ return self._post(path, data=data)
12
+
13
+ def create_plugin_release(self, data):
14
+ path = 'plugin_releases/'
15
+ return self._post(path, data=data)
16
+
17
+ def get_job(self, pk):
18
+ path = f'jobs/{pk}/'
19
+ return self._get(path)
20
+
21
+ def list_jobs(self):
22
+ path = 'jobs/'
23
+ return self._get(path)
24
+
25
+ def list_job_logs(self, pk):
26
+ path = f'jobs/{pk}/logs/'
27
+ return self._get(path)
28
+
29
+ def tail_job_logs(self, pk):
30
+ path = f'jobs/{pk}/tail_logs/'
31
+
32
+ url = self._get_url(path)
33
+ headers = self._get_headers()
34
+
35
+ response = self.requests_session.get(url, headers=headers, stream=True)
36
+ for line in response.iter_lines():
37
+ if line:
38
+ yield line.decode('utf-8')
39
+
40
+ def get_node(self, pk):
41
+ path = f'nodes/{pk}/'
42
+ return self._get(path)
43
+
44
+ def list_nodes(self):
45
+ path = 'nodes/'
46
+ return self._get(path)
47
+
48
+ def get_task(self, pk):
49
+ path = f'tasks/{pk}/'
50
+ return self._get(path)
51
+
52
+ def list_tasks(self):
53
+ path = 'tasks/'
54
+ return self._get(path)
@@ -0,0 +1,22 @@
1
+ from synapse_sdk.clients.backend.annotation import AnnotationClientMixin
2
+ from synapse_sdk.clients.backend.dataset import DatasetClientMixin
3
+ from synapse_sdk.clients.backend.integration import IntegrationClientMixin
4
+ from synapse_sdk.clients.backend.ml import MLClientMixin
5
+
6
+
7
+ class BackendClient(AnnotationClientMixin, DatasetClientMixin, IntegrationClientMixin, MLClientMixin):
8
+ name = 'Backend'
9
+ token = None
10
+ tenant = None
11
+
12
+ def __init__(self, base_url, token, tenant=None):
13
+ super().__init__(base_url)
14
+ self.token = token
15
+ if tenant:
16
+ self.tenant = tenant
17
+
18
+ def _get_headers(self):
19
+ headers = {'Authorization': f'Token {self.token}'}
20
+ if self.tenant:
21
+ headers['SYNAPSE-Tenant'] = f'Token {self.tenant}'
22
+ return headers
@@ -0,0 +1,29 @@
1
+ from synapse_sdk.clients.base import BaseClient
2
+ from synapse_sdk.clients.utils import get_default_url_conversion
3
+
4
+
5
+ class AnnotationClientMixin(BaseClient):
6
+ def get_project(self, pk):
7
+ path = f'projects/{pk}/'
8
+ return self._get(path)
9
+
10
+ def get_label_tag(self, pk):
11
+ path = f'label_tags/{pk}/'
12
+ return self._get(path)
13
+
14
+ def list_label_tags(self, data):
15
+ path = 'label_tags/'
16
+ return self._list(path, data=data)
17
+
18
+ def list_labels(self, data, url_conversion=None, list_all=False):
19
+ path = 'labels/'
20
+ url_conversion = get_default_url_conversion(url_conversion, files_fields=['files'])
21
+ return self._list(path, data=data, url_conversion=url_conversion, list_all=list_all)
22
+
23
+ def create_labels(self, data):
24
+ path = 'labels/'
25
+ return self._post(path, data=data)
26
+
27
+ def set_tags_labels(self, data, params=None):
28
+ path = 'labels/set_tags/'
29
+ return self._post(path, data=data, params=params)
@@ -1,9 +1,11 @@
1
1
  from multiprocessing import Pool
2
2
  from tqdm import tqdm
3
- from ..utils import get_batched_list
4
3
 
4
+ from synapse_sdk.clients.base import BaseClient
5
+ from synapse_sdk.clients.utils import get_batched_list
5
6
 
6
- class DatasetClientMixin:
7
+
8
+ class DatasetClientMixin(BaseClient):
7
9
  def list_dataset(self):
8
10
  path = 'datasets/'
9
11
  return self._get(path)
@@ -14,10 +16,10 @@ class DatasetClientMixin:
14
16
 
15
17
  def create_data_units(self, data):
16
18
  path = 'data_units/'
17
- return self._post(path, payload=data)
19
+ return self._post(path, data=data)
18
20
 
19
21
  def import_dataset(self, dataset_id, dataset, project_id=None, batch_size=1000, process_pool=10):
20
- # TODO validate datset with schema
22
+ # TODO validate dataset with schema
21
23
 
22
24
  params = [(data, dataset_id) for data in dataset]
23
25
 
@@ -0,0 +1,47 @@
1
+ from synapse_sdk.clients.base import BaseClient
2
+
3
+
4
+ class IntegrationClientMixin(BaseClient):
5
+ def get_plugin(self, pk):
6
+ path = f'plugins/{pk}/'
7
+ return self._get(path)
8
+
9
+ def create_plugin(self, data):
10
+ path = 'plugins/'
11
+ return self._post(path, data=data)
12
+
13
+ def update_plugin(self, pk, data):
14
+ path = f'plugins/{pk}/'
15
+ return self._put(path, data=data)
16
+
17
+ def run_plugin(self, pk, data):
18
+ path = f'plugins/{pk}/run/'
19
+ return self._post(path, data=data)
20
+
21
+ def get_plugin_release(self, pk, params=None):
22
+ path = f'plugin_releases/{pk}/'
23
+ return self._get(path, params=params)
24
+
25
+ def create_plugin_release(self, data):
26
+ path = 'plugin_releases/'
27
+ files = {'file': data.pop('file')}
28
+ return self._post(path, data=data, files=files)
29
+
30
+ def list_job_console_logs(self, pk):
31
+ path = f'jobs/{pk}/console_logs/'
32
+ return self._get(path)
33
+
34
+ def tail_job_console_logs(self, pk):
35
+ path = f'jobs/{pk}/tail_console_logs/'
36
+
37
+ url = self._get_url(path)
38
+ headers = self._get_headers()
39
+
40
+ response = self.requests_session.get(url, headers=headers, stream=True)
41
+ for line in response.iter_lines():
42
+ if line:
43
+ yield line.decode('utf-8')
44
+
45
+ def create_logs(self, data):
46
+ path = 'logs/'
47
+ return self._post(path, data=data)
@@ -0,0 +1,24 @@
1
+ from synapse_sdk.clients.base import BaseClient
2
+ from synapse_sdk.clients.utils import get_default_url_conversion
3
+
4
+
5
+ class MLClientMixin(BaseClient):
6
+ def get_model(self, pk, params=None, url_conversion=None):
7
+ path = f'models/{pk}/'
8
+ url_conversion = get_default_url_conversion(
9
+ url_conversion, files_fields=['files', 'parent.files'], is_list=False
10
+ )
11
+ return self._get(path, params=params, url_conversion=url_conversion)
12
+
13
+ def create_model(self, data):
14
+ path = 'models/'
15
+ return self._post(path, data=data)
16
+
17
+ def update_model(self, pk, data, files=None):
18
+ path = f'models/{pk}/'
19
+ return self._patch(path, data=data, files=files)
20
+
21
+ def list_train_dataset(self, params=None, url_conversion=None, list_all=False):
22
+ path = 'train_dataset/'
23
+ url_conversion = get_default_url_conversion(url_conversion, files_fields=['files'])
24
+ return self._list(path, params=params, url_conversion=url_conversion, list_all=list_all)
@@ -4,24 +4,16 @@ from pathlib import Path
4
4
 
5
5
  import requests
6
6
 
7
- from .exceptions import ClientError
8
- from .mixins.annotation import AnnotationClientMixin
9
- from .mixins.dataset import DatasetClientMixin
10
- from .mixins.integration import IntegrationClientMixin
11
- from .mixins.ml import MLClientMixin
12
- from ..utils.file import files_url_to_path_from_objs
7
+ from synapse_sdk.clients.exceptions import ClientError
8
+ from synapse_sdk.utils.file import files_url_to_path_from_objs
13
9
 
14
10
 
15
- class Client(AnnotationClientMixin, DatasetClientMixin, IntegrationClientMixin, MLClientMixin):
11
+ class BaseClient:
12
+ name = None
16
13
  base_url = None
17
- token = None
18
- tenant = None
19
14
 
20
- def __init__(self, base_url, token, tenant=None):
15
+ def __init__(self, base_url):
21
16
  self.base_url = base_url
22
- self.token = token
23
- if tenant:
24
- self.tenant = tenant
25
17
  requests_session = requests.Session()
26
18
  self.requests_session = requests_session
27
19
 
@@ -31,10 +23,7 @@ class Client(AnnotationClientMixin, DatasetClientMixin, IntegrationClientMixin,
31
23
  return path
32
24
 
33
25
  def _get_headers(self):
34
- headers = {'Authorization': f'Token {self.token}'}
35
- if self.tenant:
36
- headers['SYNAPSE-Tenant'] = f'Token {self.tenant}'
37
- return headers
26
+ return {}
38
27
 
39
28
  def _request(self, method, path, **kwargs):
40
29
  url = self._get_url(path)
@@ -56,12 +45,12 @@ class Client(AnnotationClientMixin, DatasetClientMixin, IntegrationClientMixin,
56
45
  response.status_code, response.json() if response.status_code == 400 else response.reason
57
46
  )
58
47
  except requests.ConnectionError:
59
- raise ClientError(408, 'Server is not responding')
48
+ raise ClientError(408, f'{self.name} is not responding')
60
49
 
61
50
  return response.json()
62
51
 
63
- def _get(self, path, payload=None, url_conversion=None):
64
- response = self._request('get', path, params=payload)
52
+ def _get(self, path, url_conversion=None, **kwargs):
53
+ response = self._request('get', path, **kwargs)
65
54
  if url_conversion:
66
55
  if url_conversion['is_list']:
67
56
  files_url_to_path_from_objs(response['results'], **url_conversion)
@@ -69,21 +58,24 @@ class Client(AnnotationClientMixin, DatasetClientMixin, IntegrationClientMixin,
69
58
  files_url_to_path_from_objs(response, **url_conversion)
70
59
  return response
71
60
 
72
- def _post(self, path, payload=None, files=None, params=None):
73
- return self._request('post', path, data=payload, files=files, params=params)
61
+ def _post(self, path, **kwargs):
62
+ return self._request('post', path, **kwargs)
74
63
 
75
- def _patch(self, path, payload=None, files=None, params=None):
76
- return self._request('patch', path, data=payload, files=files, params=params)
64
+ def _put(self, path, **kwargs):
65
+ return self._request('put', path, **kwargs)
77
66
 
78
- def _list(self, path, payload=None, url_conversion=None, list_all=False):
79
- response = self._get(path, payload, url_conversion)
67
+ def _patch(self, path, **kwargs):
68
+ return self._request('patch', path, **kwargs)
69
+
70
+ def _list(self, path, url_conversion=None, list_all=False, **kwargs):
71
+ response = self._get(path, url_conversion, **kwargs)
80
72
  if list_all:
81
- return self._list_all(path, payload, url_conversion), response['count']
73
+ return self._list_all(path, url_conversion, **kwargs), response['count']
82
74
  else:
83
75
  return response
84
76
 
85
- def _list_all(self, path, payload=None, url_conversion=None):
86
- response = self._get(path, payload, url_conversion)
77
+ def _list_all(self, path, url_conversion=None, **kwargs):
78
+ response = self._get(path, url_conversion, **kwargs)
87
79
  yield from response['results']
88
80
  if response['next']:
89
- yield from self._list_all(response['next'], payload, url_conversion)
81
+ yield from self._list_all(response['next'], url_conversion, **kwargs)
@@ -6,11 +6,3 @@ class ClientError(Exception):
6
6
  self.status = status
7
7
  self.reason = reason
8
8
  super().__init__(status, reason, *args)
9
-
10
- def as_validation_error(self):
11
- if self.status == 400:
12
- error = self.reason
13
- else:
14
- error = str(self)
15
-
16
- return {'backend_errors': error}
synapse_sdk/loggers.py CHANGED
@@ -1,16 +1,10 @@
1
1
  import datetime
2
2
 
3
- from synapse_sdk.client import ClientError
3
+ from synapse_sdk.clients.exceptions import ClientError
4
4
 
5
5
 
6
6
  class BaseLogger:
7
7
  progress_records = {}
8
- logs_queue = []
9
- client = None
10
-
11
- def __init__(self, client=None, task=None):
12
- self.client = client
13
- self.task = task
14
8
 
15
9
  def set_progress(self, current, total, category=''):
16
10
  percent = 0
@@ -19,30 +13,40 @@ class BaseLogger:
19
13
  percent = float(round(percent, 2))
20
14
 
21
15
  self.progress_records[category] = {'current': current, 'total': total, 'percent': percent}
22
- if self.task:
23
- self.task.update_state(state='PROGRESS', meta=self.progress_records)
24
- else:
25
- print(self.progress_records)
26
-
27
- def log(self, action, data):
28
- log = {'action': action, 'data': data, 'datetime': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S.%f')}
29
-
30
- if self.client and self.task:
31
- log['task_id'] = self.task.request.id
32
- self.logs_queue.append(log)
33
- try:
34
- self.client.create_logs(self.logs_queue)
35
- self.logs_queue.clear()
36
- except ClientError as e:
37
- print(e)
38
- else:
39
- print(log)
40
16
 
41
17
 
42
18
  class ConsoleLogger(BaseLogger):
19
+ def set_progress(self, current, total, category=''):
20
+ super().set_progress(current, total, category=category)
21
+ print(self.progress_records)
22
+
43
23
  def log(self, action, data):
44
24
  print(action, data)
45
25
 
46
26
 
47
- class SynapseLogger(BaseLogger):
48
- pass
27
+ class BackendLogger(BaseLogger):
28
+ logs_queue = []
29
+ client = None
30
+ job_id = None
31
+
32
+ def __init__(self, client, job_id):
33
+ self.client = client
34
+ self.job_id = job_id
35
+
36
+ def set_progress(self, current, total, category=''):
37
+ super().set_progress(current, total, category=category)
38
+ # TODO set_progress to the job
39
+
40
+ def log(self, action, data):
41
+ log = {
42
+ 'action': action,
43
+ 'data': data,
44
+ 'datetime': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S.%f'),
45
+ 'job': self.job_id,
46
+ }
47
+ self.logs_queue.append(log)
48
+ try:
49
+ self.client.create_logs(self.logs_queue)
50
+ self.logs_queue.clear()
51
+ except ClientError as e:
52
+ print(e)
@@ -8,11 +8,13 @@ import ray
8
8
  import requests
9
9
  from ray.dashboard.modules.job.sdk import JobSubmissionClient
10
10
 
11
- from synapse_sdk.loggers import ConsoleLogger
11
+ from synapse_sdk.clients.backend import BackendClient
12
+ from synapse_sdk.loggers import ConsoleLogger, BackendLogger
12
13
  from synapse_sdk.plugins.enums import RunMethod
13
- from synapse_sdk.plugins.upload import build_and_upload, archive_and_upload
14
+ from synapse_sdk.plugins.upload import build_and_upload, archive_and_upload, download_and_upload
14
15
  from synapse_sdk.plugins.utils import get_plugin_checksum
15
16
  from synapse_sdk.utils.module_loading import import_string
17
+ from synapse_sdk.utils.storage import get_storage
16
18
 
17
19
 
18
20
  class Action:
@@ -29,11 +31,12 @@ class Action:
29
31
  default_envs = [
30
32
  'RAY_DASHBOARD_URL',
31
33
  'RAY_SERVE_ADDRESS',
32
- 'SYNAPSE_PLUGIN_URL',
33
- 'SYNAPSE_PLUGIN_PATH',
34
- 'SYNAPSE_PLUGIN_BASE_URL',
35
34
  'SYNAPSE_PLUGIN_STORAGE',
35
+ 'SYNAPSE_DEBUG_PLUGIN_PATH',
36
36
  'SYNAPSE_DEBUG_MODULES',
37
+ 'SYNAPSE_PLUGIN_RUN_HOST',
38
+ 'SYNAPSE_PLUGIN_RUN_USER_TOKEN',
39
+ 'SYNAPSE_PLUGIN_RUN_TENANT',
37
40
  ]
38
41
 
39
42
  def __init__(self, params, plugin_config, envs=None, job_id=None, direct=False, debug=False):
@@ -43,13 +46,8 @@ class Action:
43
46
  self.job_id = job_id
44
47
  self.direct = direct
45
48
  self.debug = debug
46
- if envs:
47
- self.envs = {**envs, **self.get_default_envs()}
48
- else:
49
- self.envs = self.get_default_envs()
50
-
51
- # TODO logger 지정 방식 개선
52
- self.logger = ConsoleLogger()
49
+ self.envs = {**envs, **self.get_default_envs()} if envs else self.get_default_envs()
50
+ self.set_logger()
53
51
 
54
52
  @cached_property
55
53
  def plugin_id(self):
@@ -61,38 +59,55 @@ class Action:
61
59
  def plugin_checksum(self):
62
60
  return get_plugin_checksum(self.plugin_id)
63
61
 
64
- @cached_property
65
- def plugin_url(self):
66
- if self.debug:
67
- plugin_url = self.envs.get('SYNAPSE_PLUGIN_URL')
68
- if not plugin_url:
69
- plugin_url = archive_and_upload(
70
- self.envs.get('SYNAPSE_PLUGIN_PATH', '.'),
71
- self.envs['SYNAPSE_PLUGIN_STORAGE'],
72
- )
73
- self.envs['SYNAPSE_PLUGIN_URL'] = plugin_url
74
- return plugin_url
75
- base_url = self.envs['SYNAPSE_PLUGIN_BASE_URL']
76
- return str(os.path.join(base_url, f'{self.plugin_checksum}.zip'))
77
-
78
62
  @cached_property
79
63
  def entrypoint(self):
80
64
  return import_string(self.config['entrypoint'])
81
65
 
82
- def get_default_envs(self):
83
- return {env: os.environ[env] for env in self.default_envs if env in os.environ}
66
+ @property
67
+ def plugin_storage_url(self):
68
+ return self.envs['SYNAPSE_PLUGIN_STORAGE']
69
+
70
+ @property
71
+ def plugin_url(self):
72
+ if self.debug:
73
+ plugin_path = self.envs.get('SYNAPSE_DEBUG_PLUGIN_PATH', '.')
74
+ if plugin_path.startswith('https://'): # TODO ray에서 지원하는 remote uri 형식 (https, s3, gs) 모두 지원
75
+ plugin_url = plugin_path
76
+ elif plugin_path.startswith('http://'):
77
+ plugin_url = download_and_upload(plugin_path, self.plugin_storage_url)
78
+ else:
79
+ plugin_url = archive_and_upload(plugin_path, self.plugin_storage_url)
80
+ self.envs['SYNAPSE_DEBUG_PLUGIN_PATH'] = plugin_url
81
+ return plugin_url
82
+ storage = get_storage(self.plugin_storage_url)
83
+ return storage.get_url(f'{self.plugin_checksum}.zip')
84
84
 
85
- def get_debug_modules(self):
85
+ @property
86
+ def debug_modules(self):
86
87
  debug_modules = []
87
88
  for module_path in self.envs.get('SYNAPSE_DEBUG_MODULES', '').split(','):
88
- if module_path.startswith('http'):
89
+ if module_path.startswith('https://'): # TODO ray에서 지원하는 remote uri 형식 (https, s3, gs) 모두 지원
89
90
  module_url = module_path
90
91
  else:
91
- module_url = build_and_upload(module_path, self.envs['SYNAPSE_PLUGIN_STORAGE'])
92
+ module_url = build_and_upload(module_path, self.plugin_storage_url)
92
93
  debug_modules.append(module_url)
93
94
  self.envs['SYNAPSE_DEBUG_MODULES'] = ','.join(debug_modules)
94
95
  return debug_modules
95
96
 
97
+ def set_logger(self):
98
+ if self.job_id:
99
+ client = BackendClient(
100
+ self.envs['SYNAPSE_PLUGIN_RUN_HOST'],
101
+ self.envs['SYNAPSE_PLUGIN_RUN_USER_TOKEN'],
102
+ self.envs['SYNAPSE_PLUGIN_RUN_TENANT'],
103
+ )
104
+ self.logger = BackendLogger(client, self.job_id)
105
+ else:
106
+ self.logger = ConsoleLogger()
107
+
108
+ def get_default_envs(self):
109
+ return {env: os.environ[env] for env in self.default_envs if env in os.environ}
110
+
96
111
  def get_runtime_env(self):
97
112
  runtime_env = {
98
113
  'pip': ['-r ${RAY_RUNTIME_ENV_CREATE_WORKING_DIR}/requirements.txt'],
@@ -100,7 +115,7 @@ class Action:
100
115
  }
101
116
 
102
117
  if self.debug:
103
- runtime_env['pip'] += self.get_debug_modules()
118
+ runtime_env['pip'] += self.debug_modules
104
119
 
105
120
  # 맨 마지막에 진행되어야 함
106
121
  runtime_env['env_vars'] = self.envs
@@ -145,14 +160,22 @@ class Action:
145
160
  return ray.get(run_task.remote(self.category.value, self.name, *args, **kwargs))
146
161
 
147
162
  def run_by_job(self):
148
- entrypoint_args = ['run', self.name, f'"{json.dumps(self.params)}"', '--direct']
163
+ main_options = []
164
+ options = ['run', '--direct']
165
+ arguments = [self.name, f'{json.dumps(json.dumps(self.params))}']
166
+
149
167
  if self.debug:
150
- entrypoint_args.insert(0, '--debug')
168
+ main_options.append('--debug')
169
+
170
+ if self.job_id:
171
+ options.append(f'--job-id={self.job_id}')
172
+
173
+ cmd = ' '.join(main_options + options + arguments)
151
174
 
152
175
  client = JobSubmissionClient(address=self.envs.get('RAY_DASHBOARD_URL'))
153
176
  return client.submit_job(
154
177
  submission_id=self.job_id,
155
- entrypoint=f'python main.py {" ".join(entrypoint_args)}',
178
+ entrypoint=f'python main.py {cmd}',
156
179
  runtime_env=self.get_runtime_env(),
157
180
  )
158
181
 
@@ -1,8 +1,6 @@
1
1
  from synapse_sdk.plugins.categories.base import Action
2
2
  from synapse_sdk.plugins.categories.decorators import register_action
3
3
  from synapse_sdk.plugins.enums import RunMethod, PluginCategory
4
- from synapse_sdk.utils.file import get_dict_from_file, files_url_to_path_from_objs
5
- from synapse_sdk.utils.module_loading import import_string
6
4
 
7
5
 
8
6
  @register_action
@@ -11,73 +9,20 @@ class TrainAction(Action):
11
9
  category = PluginCategory.NEURAL_NET
12
10
  method = RunMethod.JOB
13
11
 
14
- def get_input_dataset_for_training(self, model_id=None):
15
- """
16
- :return:
17
- {
18
- "train": [
19
- {
20
- "files": {
21
- "image": {
22
- "path": "/path/to/image.jpg",
23
- "meta": {
24
- "width": 265,
25
- "height": 190,
26
- "created": 1651563526.0277045,
27
- "file_size": 5191,
28
- "last_modified": 1651563526.0277045
29
- }
30
- }
31
- },
32
- "ground_truth": {
33
- ...label_data
34
- }
35
- },
36
- ...
37
- ],
38
- "validation": ...,
39
- "test": ...
40
- }
41
- """
12
+ def get_dataset(self):
13
+ return {}
42
14
 
43
- client = self.logger.client
44
- input_dataset = {}
45
- category_int_to_str = {1: 'train', 2: 'validation', 3: 'test'}
46
-
47
- if client:
48
- train_dataset, count_dataset = client.list_train_dataset(
49
- payload={'fields': ['category', 'files', 'ground_truth'], 'model': model_id}, list_all=True
50
- )
51
-
52
- for i, train_data in enumerate(train_dataset, start=1):
53
- self.set_progress(i, count_dataset, category='dataset_download')
54
- category = category_int_to_str[train_data.pop('category')]
55
- try:
56
- input_dataset[category].append(train_data)
57
- except KeyError:
58
- input_dataset[category] = [train_data]
59
-
60
- else:
61
- for category in category_int_to_str.values():
62
- dataset_path = self.task['dataset'].get(category)
63
- if dataset_path:
64
- input_dataset[category] = get_dict_from_file(dataset_path)
65
- files_url_to_path_from_objs(input_dataset[category], ['files'], is_list=True)
66
-
67
- return input_dataset
68
-
69
- def run_train(self):
70
- hyperparameter = self.task['hyperparameter']
71
- train = import_string(self.plugin['train']['entrypoint'])
15
+ def run(self):
16
+ hyperparameter = self.params['hyperparameter']
72
17
 
73
18
  # download dataset
74
19
  self.log_event('Preparing dataset for training.')
75
- input_dataset = self.get_input_dataset_for_training()
20
+ input_dataset = self.get_dataset()
76
21
 
77
22
  # train dataset
78
23
  self.log_event('Starting model training.')
79
24
 
80
- model_files = train(self, input_dataset, hyperparameter)
25
+ model_files = self.entrypoint(self, input_dataset, hyperparameter)
81
26
 
82
27
  # upload model_data
83
28
  self.log_event('Registering model data.')
@@ -85,10 +30,6 @@ class TrainAction(Action):
85
30
  self.end_log()
86
31
  return model_files
87
32
 
88
- def start(self):
89
- action = self.task['action']
90
- getattr(self, f'run_{action}')()
91
-
92
33
  def log_metric(self, x, i, **kwargs):
93
34
  self.log(x, {x: i, **kwargs})
94
35
 
@@ -1,24 +1,36 @@
1
+ import json
1
2
  from pathlib import Path
2
3
 
3
4
  import click
4
5
 
5
- from synapse_sdk.client import Client
6
+ from synapse_sdk.clients.backend import BackendClient
7
+ from synapse_sdk.plugins.upload import archive
6
8
  from synapse_sdk.plugins.utils import read_config
7
9
 
8
- from ..upload import archive
9
-
10
10
 
11
11
  @click.command()
12
- @click.option('-h', '--host', required=True)
13
- @click.option('-t', '--token', required=True)
14
- @click.option('-w', '--workspace', required=True)
15
- def publish(host, token, workspace):
16
- client = Client(host, token, tenant=workspace)
12
+ @click.option('--host', required=True)
13
+ @click.option('--user_token', required=True)
14
+ @click.option('--tenant', required=True)
15
+ @click.option('--debug_modules', default='', envvar='SYNAPSE_DEBUG_MODULES')
16
+ @click.pass_context
17
+ def publish(ctx, host, user_token, tenant, debug_modules):
18
+ debug = ctx.obj['DEBUG']
19
+
17
20
  config = read_config()
18
21
 
19
22
  source_path = Path('./')
20
23
  archive_path = source_path / 'dist' / 'archive.zip'
21
24
  archive(source_path, archive_path)
22
25
 
23
- data = {'plugin': config['code'], 'file': str(archive_path)}
26
+ data = {'plugin': config['code'], 'file': str(archive_path), 'debug': debug}
27
+ if debug:
28
+ data['debug_meta'] = json.dumps({'modules': debug_modules.split(',')})
29
+
30
+ client = BackendClient(host, user_token, tenant=tenant)
24
31
  client.create_plugin_release(data)
32
+ click.secho(
33
+ f'Successfully published "{config["name"]}" ({config["code"]}@{config["version"]}) to synapse backend!',
34
+ fg='green',
35
+ bold=True,
36
+ )
@@ -1,18 +1,64 @@
1
+ import os
2
+
1
3
  import click
2
4
 
3
- from synapse_sdk.plugins.utils import get_action
5
+ from synapse_sdk.clients.agent import AgentClient
6
+ from synapse_sdk.clients.backend import BackendClient
7
+ from synapse_sdk.plugins.utils import get_action, read_config
4
8
 
5
9
 
6
10
  @click.command()
7
11
  @click.argument('action')
8
12
  @click.argument('params')
13
+ @click.option('--job-id')
9
14
  @click.option('--direct/--no-direct', default=False)
15
+ @click.option('--run-by', type=click.Choice(['script', 'agent', 'backend']), default='script')
16
+ @click.option('--agent-host')
17
+ @click.option('--agent-token')
18
+ @click.option('--host')
19
+ @click.option('--agent')
20
+ @click.option('--user-token')
21
+ @click.option('--tenant')
10
22
  @click.pass_context
11
- def run(ctx, action, params, direct):
23
+ def run(ctx, action, params, job_id, direct, run_by, agent_host, agent_token, host, agent, user_token, tenant):
12
24
  debug = ctx.obj['DEBUG']
13
25
 
14
- action = get_action(action, params, direct=direct, debug=debug)
26
+ if run_by == 'script':
27
+ run_by_script(action, params, job_id, direct, debug)
28
+ elif run_by == 'agent':
29
+ run_by_agent(action, params, job_id, agent_host, agent_token, user_token, tenant, debug)
30
+ elif run_by == 'backend':
31
+ run_by_backend(action, params, agent, host, user_token, tenant)
32
+
33
+
34
+ def run_by_script(action, params, job_id, direct, debug):
35
+ action = get_action(action, params, job_id=job_id, direct=direct, debug=debug)
15
36
  result = action.run_action()
16
37
 
17
38
  if debug:
18
39
  click.echo(result)
40
+
41
+
42
+ def run_by_agent(action, params, job_id, agent_host, agent_token, user_token, tenant, debug):
43
+ client = AgentClient(agent_host, agent_token, user_token, tenant)
44
+ data = {'action': action, 'params': params, 'job_id': job_id}
45
+ if debug:
46
+ data.update({
47
+ 'plugin_path': os.getcwd(),
48
+ 'modules': os.getenv('SYNAPSE_DEBUG_MODULES', '').split(','),
49
+ })
50
+ result = client.run_debug_plugin_release(data=data)
51
+ else:
52
+ config = read_config()
53
+ result = client.run_plugin_release(code=f'{config["code"]}@{config["version"]}', data=data)
54
+
55
+ click.echo(result)
56
+
57
+
58
+ def run_by_backend(action, params, agent, host, user_token, tenant):
59
+ client = BackendClient(host, user_token, tenant=tenant)
60
+ config = read_config()
61
+ data = {'agent': agent, 'version': config['version'], 'action': action, 'params': params}
62
+ result = client.run_plugin(config['code'], data=data)
63
+
64
+ click.echo(result)
@@ -1,8 +1,9 @@
1
1
  import re
2
2
  import subprocess
3
+ import tempfile
3
4
  from pathlib import Path
4
5
 
5
- from synapse_sdk.utils.file import calculate_checksum
6
+ from synapse_sdk.utils.file import calculate_checksum, download_file
6
7
  from synapse_sdk.utils.storage import get_storage
7
8
 
8
9
 
@@ -12,6 +13,15 @@ def archive(source_path, archive_path):
12
13
  subprocess.run(command, cwd=source_path, shell=True, check=True, stdout=subprocess.DEVNULL)
13
14
 
14
15
 
16
+ def download_and_upload(source_url, url):
17
+ storage = get_storage(url)
18
+ with tempfile.TemporaryDirectory() as temp_path:
19
+ file_path = str(download_file(source_url, temp_path))
20
+ checksum = calculate_checksum(file_path, prefix='dev')
21
+ # TODO 중복 체크
22
+ return storage.upload(file_path, f'{checksum}.zip')
23
+
24
+
15
25
  def archive_and_upload(source_path, url):
16
26
  storage = get_storage(url)
17
27
  dist_path = Path(source_path, 'dist')
@@ -78,8 +78,7 @@ class S3Storage(BaseStorage):
78
78
  return False
79
79
 
80
80
  def get_url(self, target):
81
- object_name = os.path.join(self.options['location'], target)
82
- return f'{self.options["base_url"]}/{self.options["bucket_name"]}/{object_name}'
81
+ return os.path.join(self.options['base_url'], self.options['bucket_name'], self.options['location'], target)
83
82
 
84
83
 
85
84
  STORAGE_STORAGES = {
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: synapse-sdk
3
- Version: 1.0.0a1
3
+ Version: 1.0.0a3
4
4
  Summary: synapse sdk
5
5
  Author-email: datamaker <developer@datamaker.io>
6
6
  License: MIT
@@ -1,21 +1,23 @@
1
1
  synapse_sdk/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- synapse_sdk/config.py,sha256=AHyTHtzkkCZb9DLu1_R99VOwPgWATZj4K7yzHaPijfA,149
3
- synapse_sdk/loggers.py,sha256=W4KPq-QOK4ECBL0HxxGUGWCNR3FKbHOUMqnVrwz-Ihg,1331
4
- synapse_sdk/client/__init__.py,sha256=STq4STOQC5hhxKsAxhMYP6q3f4J1yJd1JUlz_db_130,3339
5
- synapse_sdk/client/exceptions.py,sha256=LzgzPKRPhNUpLh-jBQHKze8c3CQoVA22jSfjUMBZCuU,405
6
- synapse_sdk/client/utils.py,sha256=8pPJTdzHiRPSbZMoQYHAgR2BAMO6u_R_jMV6a2p34iQ,392
7
- synapse_sdk/client/mixins/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
- synapse_sdk/client/mixins/annotation.py,sha256=69xblc0RLokKlpSfIzoJ5dVRvw3VpPIVgbQKV3L5Xx4,905
9
- synapse_sdk/client/mixins/dataset.py,sha256=KXsen7-vdSzdR09uxD88lT604ItJKuCOSLR3fEU5ud0,1666
10
- synapse_sdk/client/mixins/integration.py,sha256=iCBjoz7GjzerhxGM0cPunauohs9Vq4_98EtTwxBMdYs,880
11
- synapse_sdk/client/mixins/ml.py,sha256=xkfO95k6p_nQVyai2zBO-z4b9nh6YIryN4iakDGiPsE,891
2
+ synapse_sdk/loggers.py,sha256=Xl_oge_xYJ44cUoh9xBPKrNvDtK2W3Tzh8PKqrSROmQ,1439
3
+ synapse_sdk/clients/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
+ synapse_sdk/clients/base.py,sha256=IuZY9-me62lPRgmMjXPhqy8cVLbtVSyT9acj19KMQkU,2788
5
+ synapse_sdk/clients/exceptions.py,sha256=ylv7x10eOp4aA3a48jwonnvqvkiYwzJYXjkVkRTAjwk,220
6
+ synapse_sdk/clients/utils.py,sha256=8pPJTdzHiRPSbZMoQYHAgR2BAMO6u_R_jMV6a2p34iQ,392
7
+ synapse_sdk/clients/agent/__init__.py,sha256=n4exZwc8gwuH6zqZUhstcD_sqIdm-ZoPvUcAxnCrnOQ,609
8
+ synapse_sdk/clients/agent/service.py,sha256=esZcipWJkIN-SE279set9PjWrn0923_-zznxkD5hcXg,1423
9
+ synapse_sdk/clients/backend/__init__.py,sha256=850EjxNoGWXJpVZLRIaajrymBuW0MXiP3M3onRHDGbQ,800
10
+ synapse_sdk/clients/backend/annotation.py,sha256=Zt1VA3fScYCxy_Ss1TEzqu7jYdNxlNBRYAjZfuEWOSI,989
11
+ synapse_sdk/clients/backend/dataset.py,sha256=abZr9FyVzgnEpk1POK6m1FfbzWKnqWTo7wyQwwS5VvM,1743
12
+ synapse_sdk/clients/backend/integration.py,sha256=_SO9dyC1dOcYiMMl7e8CIMFm1k1hx6UPT9lzfvWnZqw,1400
13
+ synapse_sdk/clients/backend/ml.py,sha256=l4rGLBZgLUYQOBePvWAoNyz-yZgJuhC-1KCFeZOYDuQ,1012
12
14
  synapse_sdk/plugins/__init__.py,sha256=9vsbYhxah4_ofTaG0x0qLFID_raHNkO57Y8A31Ws-lU,222
13
15
  synapse_sdk/plugins/enums.py,sha256=lQZqO2bEeBKdk6q-SMjfOLDlgxv7BuIPk3fXeUFfHRs,327
14
16
  synapse_sdk/plugins/job.py,sha256=UzFKA8o_F6RzY_PwyI4dlF3kSfmMG0xEYIyKLfdqSP8,91
15
- synapse_sdk/plugins/upload.py,sha256=lImaecAz5NG8puT5PwVNkV3kT0d6b7DNBD0jUMaVcBI,2600
17
+ synapse_sdk/plugins/upload.py,sha256=9DF-f0Or6ea4mJxuCmPlpYOG4EGeGqIRALc4ulrnQd4,2973
16
18
  synapse_sdk/plugins/utils.py,sha256=RFxFtmjj-uBK03wUwLhtUecfn_IOKRJupudmsguc2Sc,1212
17
19
  synapse_sdk/plugins/categories/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
18
- synapse_sdk/plugins/categories/base.py,sha256=4pltsAR7RkCm9vaV-1TkVmJSi9w78KsBlVv9v7fFO6U,5823
20
+ synapse_sdk/plugins/categories/base.py,sha256=PxA8xfy46rT_zvlI1WZjuRsxqa2Mx6TtKVXyolNqjfk,6831
19
21
  synapse_sdk/plugins/categories/decorators.py,sha256=Gw6T-UHwpCKrSt596X-g2sZbY_Z1zbbogowClj7Pr5Q,518
20
22
  synapse_sdk/plugins/categories/registry.py,sha256=KdQR8SUlLT-3kgYzDNWawS1uJnAhrcw2j4zFaTpilRs,636
21
23
  synapse_sdk/plugins/categories/data_validation/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -32,7 +34,7 @@ synapse_sdk/plugins/categories/neural_net/actions/__init__.py,sha256=47DEQpj8HBS
32
34
  synapse_sdk/plugins/categories/neural_net/actions/deployment.py,sha256=afqgD_mzErcWdW3xdbrKIOL13fUPO0biZk8_l4l2j84,767
33
35
  synapse_sdk/plugins/categories/neural_net/actions/inference.py,sha256=erM2z7aUTwyzJZqWBlxhTP8dm8cOraI_vUYAqcXkdSY,334
34
36
  synapse_sdk/plugins/categories/neural_net/actions/test.py,sha256=dAW1zfodlUhoL-sD17tG-CQT0RBxIcHWJ8f1eeZ00M4,321
35
- synapse_sdk/plugins/categories/neural_net/actions/train.py,sha256=sg-PDU_RnAtQGCwI6XnfC63lyPE2fRY6qEUv-_VKu8A,3283
37
+ synapse_sdk/plugins/categories/neural_net/actions/train.py,sha256=ASbqY7ZB0-bVrEOH1uvOkhrYKNt7ZC1aIbPZPfKXZGc,994
36
38
  synapse_sdk/plugins/categories/post_annotation/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
37
39
  synapse_sdk/plugins/categories/post_annotation/actions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
38
40
  synapse_sdk/plugins/categories/post_annotation/actions/post_annotation.py,sha256=1tutwNDHpnrCPHzMTsMEk29WPajnZikjBE83j7Z-Xt0,347
@@ -40,16 +42,16 @@ synapse_sdk/plugins/categories/pre_annotation/__init__.py,sha256=47DEQpj8HBSa-_T
40
42
  synapse_sdk/plugins/categories/pre_annotation/actions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
41
43
  synapse_sdk/plugins/categories/pre_annotation/actions/pre_annotation.py,sha256=YYQt9HsgXlBclE4Sn0c7p1zqCxWHkIHAwyA-tbqrmPQ,344
42
44
  synapse_sdk/plugins/cli/__init__.py,sha256=8ogaOhN-RbDNYHqziW8nLsNUxKkZwGkHBdKxTahcm3U,334
43
- synapse_sdk/plugins/cli/publish.py,sha256=ws4vuXPWiwKq6czIjg__eWE2-vBgquXnsYFdGyI42vs,663
44
- synapse_sdk/plugins/cli/run.py,sha256=_Lb-Jeh3NHUwui_y34-7lRXp7Ek_rP8BgBM5wqxLdeU,418
45
+ synapse_sdk/plugins/cli/publish.py,sha256=v9aMMMyZgftSLW63uO9ZKeGBJEBeVCfOSmsSrfpKOP4,1135
46
+ synapse_sdk/plugins/cli/run.py,sha256=2_iwDCGbPNMCsEWzbeZIo4EGW-cssp3UVZNfkq6ZbHo,2249
45
47
  synapse_sdk/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
46
48
  synapse_sdk/utils/debug.py,sha256=46sMFQLg_JSRUCymnT3wgszG1QsgrocRGiBjVX38r50,53
47
49
  synapse_sdk/utils/file.py,sha256=Iptk_DCPsmJzqAABCD3vC6z1yG74fKb5x81LnUCZzYo,1916
48
50
  synapse_sdk/utils/module_loading.py,sha256=chHpU-BZjtYaTBD_q0T7LcKWtqKvYBS4L0lPlKkoMQ8,1020
49
- synapse_sdk/utils/storage.py,sha256=6L3pgno3Luw6GuSRPdiIvS4ZFzjwGP6sLDKMy719pBw,2622
51
+ synapse_sdk/utils/storage.py,sha256=U3TScqQNgHQ89s0kUqQ8hm3npQAznIyRqzWDKR0YA3E,2581
50
52
  synapse_sdk/utils/string.py,sha256=rEwuZ9SAaZLcQ8TYiwNKr1h2u4CfnrQx7SUL8NWmChg,216
51
- synapse_sdk-1.0.0a1.dist-info/LICENSE,sha256=bKzmC5YAg4V1Fhl8OO_tqY8j62hgdncAkN7VrdjmrGk,1101
52
- synapse_sdk-1.0.0a1.dist-info/METADATA,sha256=zGbciSdYPvJ1Vl_sk5D0pmTNMqTuemSgkF9QbaVBXec,503
53
- synapse_sdk-1.0.0a1.dist-info/WHEEL,sha256=a7TGlA-5DaHMRrarXjVbQagU3Man_dCnGIWMJr5kRWo,91
54
- synapse_sdk-1.0.0a1.dist-info/top_level.txt,sha256=ytgJMRK1slVOKUpgcw3LEyHHP7S34J6n_gJzdkcSsw8,12
55
- synapse_sdk-1.0.0a1.dist-info/RECORD,,
53
+ synapse_sdk-1.0.0a3.dist-info/LICENSE,sha256=bKzmC5YAg4V1Fhl8OO_tqY8j62hgdncAkN7VrdjmrGk,1101
54
+ synapse_sdk-1.0.0a3.dist-info/METADATA,sha256=hfVF3hav09XmhhOM7v7GibfROb0hdiDFz3E_sm55EO0,503
55
+ synapse_sdk-1.0.0a3.dist-info/WHEEL,sha256=R06PA3UVYHThwHvxuRWMqaGcr-PuniXahwjmQRFMEkY,91
56
+ synapse_sdk-1.0.0a3.dist-info/top_level.txt,sha256=ytgJMRK1slVOKUpgcw3LEyHHP7S34J6n_gJzdkcSsw8,12
57
+ synapse_sdk-1.0.0a3.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (75.4.0)
2
+ Generator: setuptools (75.5.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1,28 +0,0 @@
1
- from ..utils import get_default_url_conversion
2
-
3
-
4
- class AnnotationClientMixin:
5
- def get_project(self, pk):
6
- path = f'projects/{pk}/'
7
- return self._get(path)
8
-
9
- def get_label_tag(self, pk):
10
- path = f'label_tags/{pk}/'
11
- return self._get(path)
12
-
13
- def list_label_tags(self, payload=None):
14
- path = 'label_tags/'
15
- return self._list(path, payload)
16
-
17
- def list_labels(self, payload=None, url_conversion=None, list_all=False):
18
- path = 'labels/'
19
- url_conversion = get_default_url_conversion(url_conversion, files_fields=['files'])
20
- return self._list(path, payload, url_conversion, list_all)
21
-
22
- def create_labels(self, data):
23
- path = 'labels/'
24
- return self._post(path, payload=data)
25
-
26
- def set_tags_labels(self, data, params=None):
27
- path = 'labels/set_tags/'
28
- return self._post(path, payload=data, params=params)
@@ -1,29 +0,0 @@
1
- class IntegrationClientMixin:
2
- def get_plugin(self, pk):
3
- path = f'plugins/{pk}/'
4
- return self._get(path)
5
-
6
- def create_plugin(self, data):
7
- path = 'plugins/'
8
- return self._post(path, payload=data)
9
-
10
- def update_plugin(self, pk, data):
11
- path = f'plugins/{pk}/'
12
- return self._put(path, payload=data)
13
-
14
- def get_plugin_release(self, pk, params=None):
15
- path = f'plugin_releases/{pk}/'
16
- return self._get(path, payload=params)
17
-
18
- def create_plugin_release(self, data):
19
- path = 'plugin_releases/'
20
- files = {'file': data.pop('file')}
21
- return self._post(path, payload=data, files=files)
22
-
23
- def create_logs(self, data):
24
- path = 'logs/'
25
- return self._post(path, payload=data)
26
-
27
- def create_task(self, data):
28
- path = 'agent_tasks/'
29
- return self._post(path, payload=data)
@@ -1,23 +0,0 @@
1
- from ..utils import get_default_url_conversion
2
-
3
-
4
- class MLClientMixin:
5
- def get_model(self, pk, payload=None, url_conversion=None):
6
- path = f'models/{pk}/'
7
- url_conversion = get_default_url_conversion(
8
- url_conversion, files_fields=['files', 'parent.files'], is_list=False
9
- )
10
- return self._get(path, payload, url_conversion)
11
-
12
- def create_model(self, data):
13
- path = 'models/'
14
- return self._post(path, payload=data)
15
-
16
- def update_model(self, pk, data, files=None):
17
- path = f'models/{pk}/'
18
- return self._patch(path, payload=data, files=files)
19
-
20
- def list_train_dataset(self, payload=None, url_conversion=None, list_all=False):
21
- path = 'train_dataset/'
22
- url_conversion = get_default_url_conversion(url_conversion, files_fields=['files'])
23
- return self._list(path, payload, url_conversion, list_all)
synapse_sdk/config.py DELETED
@@ -1,8 +0,0 @@
1
- try:
2
- from constance import config as constance_config
3
-
4
- config = constance_config
5
- except ImportError:
6
- config = None
7
-
8
- __all__ = ['config']
File without changes
File without changes