openbayes-cli 0.4.5__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.
- bayes/__init__.py +0 -0
- bayes/client/__init__.py +0 -0
- bayes/client/base.py +20 -0
- bayes/client/dataset_version_client.py +91 -0
- bayes/client/gear_client.py +468 -0
- bayes/client/job_run_client.py +504 -0
- bayes/client/job_upload_client.py +114 -0
- bayes/client/org_client.py +67 -0
- bayes/client/resource_client.py +108 -0
- bayes/client/runtime_client.py +32 -0
- bayes/client/ssh_client.py +66 -0
- bayes/client/status_client.py +14 -0
- bayes/client/user_client.py +58 -0
- bayes/commands/__init__.py +0 -0
- bayes/commands/gear.py +691 -0
- bayes/commands/org.py +100 -0
- bayes/commands/ssh.py +70 -0
- bayes/error.py +9 -0
- bayes/model/__init__.py +0 -0
- bayes/model/dataset_version.py +14 -0
- bayes/model/file/__init__.py +0 -0
- bayes/model/file/data_bindings.py +42 -0
- bayes/model/file/openbayes_data.py +133 -0
- bayes/model/file/openbayes_gear.py +72 -0
- bayes/model/file/openbayes_ignore.py +75 -0
- bayes/model/file/openbayes_yaml.py +180 -0
- bayes/model/file/settings.py +157 -0
- bayes/model/party.py +35 -0
- bayes/model/resource.py +147 -0
- bayes/model/runtime.py +34 -0
- bayes/root.py +170 -0
- bayes/templates/openbayes_zh-Hans.yaml +134 -0
- bayes/templates/openbayesignore.yaml +3 -0
- bayes/usercases/__init__.py +0 -0
- bayes/usercases/archive_usecase.py +79 -0
- bayes/usercases/auth_usecase.py +35 -0
- bayes/usercases/dataset_version_usecase.py +61 -0
- bayes/usercases/disk_usecase.py +98 -0
- bayes/usercases/gear_download_usecese.py +113 -0
- bayes/usercases/gear_logs_usecase.py +44 -0
- bayes/usercases/gear_open_usecase.py +16 -0
- bayes/usercases/gear_run_usecase.py +123 -0
- bayes/usercases/gear_upload_usecase.py +248 -0
- bayes/usercases/gear_usecase.py +301 -0
- bayes/usercases/openbayes_data_usecase.py +73 -0
- bayes/usercases/org_usecase.py +85 -0
- bayes/usercases/resource_usecase.py +74 -0
- bayes/usercases/runtime_usecase.py +58 -0
- bayes/usercases/ssh_usecase.py +110 -0
- bayes/usercases/switch_usercase.py +19 -0
- bayes/utils.py +163 -0
- openbayes_cli-0.4.5.dist-info/METADATA +62 -0
- openbayes_cli-0.4.5.dist-info/RECORD +55 -0
- openbayes_cli-0.4.5.dist-info/WHEEL +4 -0
- openbayes_cli-0.4.5.dist-info/entry_points.txt +3 -0
|
@@ -0,0 +1,504 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
from enum import Enum
|
|
3
|
+
from typing import Tuple, Optional, List
|
|
4
|
+
|
|
5
|
+
import pydantic
|
|
6
|
+
import typer
|
|
7
|
+
from gql.transport.exceptions import TransportQueryError
|
|
8
|
+
from pydantic import BaseModel, Field
|
|
9
|
+
|
|
10
|
+
from bayes.client.base import BayesGQLClient
|
|
11
|
+
from bayes.client.gear_client import Job, JobOutputBinding, DatasetBinding
|
|
12
|
+
from bayes.model.file.openbayes_yaml import ParameterSpec, HyperTuning, DEFAULT_PARALLEL_COUNT
|
|
13
|
+
from bayes.model.file.settings import DefaultJobModeTask, DefaultBatchJobModeTask, DefaultJobModeWorkSpace, \
|
|
14
|
+
DefaultBatchJobModeWorkSpace
|
|
15
|
+
from bayes.model.party import ModeEnum
|
|
16
|
+
from bayes.utils import Utils
|
|
17
|
+
from bayes.client.gear_client import DataBinding as job_dataBindings
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class BindingAuthType(str, Enum):
|
|
21
|
+
READ_ONLY = "READ_ONLY"
|
|
22
|
+
READ_WRITE = "READ_WRITE"
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class DataBinding(BaseModel):
|
|
26
|
+
path: str
|
|
27
|
+
name: str
|
|
28
|
+
bindingAuth: BindingAuthType
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class BatchTask(BaseModel):
|
|
32
|
+
size: int
|
|
33
|
+
command: str
|
|
34
|
+
code: str
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class NewTaskInput(BaseModel):
|
|
38
|
+
command: str
|
|
39
|
+
code: str
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class NewWorkspaceInput(BaseModel):
|
|
43
|
+
code: str
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class BatchWorkSpace(BaseModel):
|
|
47
|
+
code: str
|
|
48
|
+
size: int
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class TagInput(BaseModel):
|
|
52
|
+
name: Optional[str] = None
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class VariableInput(BaseModel):
|
|
56
|
+
mode: str
|
|
57
|
+
projectId: str
|
|
58
|
+
runtime: str
|
|
59
|
+
resource: str
|
|
60
|
+
newBatchTask: Optional[BatchTask] = None
|
|
61
|
+
newTask: Optional[NewTaskInput] = None
|
|
62
|
+
newWorkspace: Optional[NewWorkspaceInput] = None
|
|
63
|
+
newBatchWorkspace: Optional[BatchWorkSpace] = None
|
|
64
|
+
dataBindings: Optional[List[DataBinding]] = None
|
|
65
|
+
tagNames: Optional[List[TagInput]] = None
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
class Algorithm(Enum):
|
|
69
|
+
GRID = "Grid"
|
|
70
|
+
RANDOM = "Random"
|
|
71
|
+
BAYESIAN = "Bayesian"
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
class IntegerParameterSpecInput(BaseModel):
|
|
75
|
+
scaleType: str
|
|
76
|
+
minValue: int
|
|
77
|
+
maxValue: int
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
class DoubleParameterSpecInput(BaseModel):
|
|
81
|
+
scaleType: str
|
|
82
|
+
minValue: float
|
|
83
|
+
maxValue: float
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
class DiscreteParameterSpecInput(BaseModel):
|
|
87
|
+
discreteValues: List[float]
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
class CategoryParameterSpecInput(BaseModel):
|
|
91
|
+
categoricalValues: List[str]
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
class ParameterSpecInput(BaseModel):
|
|
95
|
+
name: str
|
|
96
|
+
integerParameterSpec: Optional[IntegerParameterSpecInput] = None
|
|
97
|
+
doubleParameterSpec: Optional[DoubleParameterSpecInput] = None
|
|
98
|
+
discreteParameterSpec: Optional[DiscreteParameterSpecInput] = None
|
|
99
|
+
categoryParameterSpec: Optional[CategoryParameterSpecInput] = None
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
class CreateHypertuningInput(BaseModel):
|
|
103
|
+
tagNames: Optional[List[TagInput]] = None
|
|
104
|
+
runtime: str
|
|
105
|
+
resource: str
|
|
106
|
+
projectId: Optional[str] = None
|
|
107
|
+
code: str
|
|
108
|
+
command: str
|
|
109
|
+
dataBindings: Optional[List[DataBinding]] = None
|
|
110
|
+
maxJobCount: int
|
|
111
|
+
parallelCount: int
|
|
112
|
+
hyperparameterMetric: str
|
|
113
|
+
sideMetrics: Optional[List[str]] = None
|
|
114
|
+
goal: str
|
|
115
|
+
algorithm: str
|
|
116
|
+
parameterSpecs: List[ParameterSpecInput]
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def build_command(command: str, parameters: dict) -> str:
|
|
120
|
+
if not parameters:
|
|
121
|
+
return command
|
|
122
|
+
else:
|
|
123
|
+
param_str = " ".join([f"--{k}={v}" for k, v in parameters.items()])
|
|
124
|
+
return f"{command} {param_str}"
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def get_graphql_binding_datasets(datasets: List[str]) -> List[DataBinding]:
|
|
128
|
+
result = []
|
|
129
|
+
|
|
130
|
+
for dataset in datasets:
|
|
131
|
+
list_parts = dataset.split(":")
|
|
132
|
+
if len(list_parts) == 2:
|
|
133
|
+
result.append(DataBinding(
|
|
134
|
+
name=list_parts[0],
|
|
135
|
+
path=list_parts[1],
|
|
136
|
+
bindingAuth=BindingAuthType.READ_ONLY
|
|
137
|
+
))
|
|
138
|
+
elif len(list_parts) == 3:
|
|
139
|
+
auth = list_parts[2].upper()
|
|
140
|
+
if auth == "RO" or auth == "READ_ONLY":
|
|
141
|
+
result.append(DataBinding(
|
|
142
|
+
name=list_parts[0],
|
|
143
|
+
path=list_parts[1],
|
|
144
|
+
bindingAuth=BindingAuthType.READ_ONLY
|
|
145
|
+
))
|
|
146
|
+
elif auth == "RW" or auth == "READ_WRITE":
|
|
147
|
+
result.append(DataBinding(
|
|
148
|
+
name=list_parts[0],
|
|
149
|
+
path=list_parts[1],
|
|
150
|
+
bindingAuth=BindingAuthType.READ_WRITE
|
|
151
|
+
))
|
|
152
|
+
|
|
153
|
+
return result
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def create(party_name, client: BayesGQLClient, mode, project_id, datasets: List[str], runtime, resource, code_id,
|
|
157
|
+
command, parameters,
|
|
158
|
+
node_count):
|
|
159
|
+
input_data = VariableInput(
|
|
160
|
+
mode=mode,
|
|
161
|
+
projectId=project_id,
|
|
162
|
+
runtime=runtime,
|
|
163
|
+
resource=resource
|
|
164
|
+
)
|
|
165
|
+
input_data.tagNames = [TagInput(name="BUSINESS_CHANNEL_ML")]
|
|
166
|
+
|
|
167
|
+
upper_mode = mode.upper()
|
|
168
|
+
if upper_mode == DefaultJobModeTask or upper_mode == DefaultBatchJobModeTask:
|
|
169
|
+
if node_count > 1:
|
|
170
|
+
input_data.newBatchTask = BatchTask(
|
|
171
|
+
size=node_count,
|
|
172
|
+
code=code_id,
|
|
173
|
+
command=build_command(command, parameters)
|
|
174
|
+
)
|
|
175
|
+
input_data.mode = "BATCH_TASK"
|
|
176
|
+
else:
|
|
177
|
+
input_data.mode = "TASK"
|
|
178
|
+
input_data.newTask = NewTaskInput(
|
|
179
|
+
code=code_id,
|
|
180
|
+
command=build_command(command, parameters)
|
|
181
|
+
)
|
|
182
|
+
elif upper_mode == DefaultJobModeWorkSpace or upper_mode == DefaultBatchJobModeWorkSpace:
|
|
183
|
+
if node_count > 1:
|
|
184
|
+
input_data.mode = "BATCH_WORKSPACE"
|
|
185
|
+
input_data.newBatchWorkspace = BatchWorkSpace(
|
|
186
|
+
code=code_id,
|
|
187
|
+
size=node_count
|
|
188
|
+
)
|
|
189
|
+
else:
|
|
190
|
+
input_data.mode = "WORKSPACE"
|
|
191
|
+
input_data.newWorkspace = NewWorkspaceInput(
|
|
192
|
+
code=code_id
|
|
193
|
+
)
|
|
194
|
+
|
|
195
|
+
if datasets and len(datasets) > 0:
|
|
196
|
+
input_data.dataBindings = get_graphql_binding_datasets(datasets)
|
|
197
|
+
|
|
198
|
+
query = """
|
|
199
|
+
mutation CreateJob($userId: String!, $input: CreateJobInput) {
|
|
200
|
+
createJob(userId: $userId, input: $input) {
|
|
201
|
+
id
|
|
202
|
+
links {
|
|
203
|
+
name
|
|
204
|
+
value
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
"""
|
|
209
|
+
|
|
210
|
+
variables = {"userId": party_name, "input": input_data.model_dump()}
|
|
211
|
+
try:
|
|
212
|
+
response = client.exec(query, variables)
|
|
213
|
+
except Exception as e:
|
|
214
|
+
if hasattr(e, 'errors') and e.errors:
|
|
215
|
+
error_message = e.errors[0]['message']
|
|
216
|
+
else:
|
|
217
|
+
error_message = str(e)
|
|
218
|
+
print(error_message, file=sys.stderr) # 打印错误消息到标准错误流
|
|
219
|
+
raise ValueError(f"create job error: {error_message}")
|
|
220
|
+
|
|
221
|
+
result = response.get("createJob")
|
|
222
|
+
if result is None:
|
|
223
|
+
raise ValueError("create job result is none")
|
|
224
|
+
|
|
225
|
+
return Job(**result)
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
class UpdateJobInput(BaseModel):
|
|
229
|
+
description: Optional[str] = None
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
def convert_to_parameter_spec_input(spec):
|
|
233
|
+
if spec.type == 'DOUBLE':
|
|
234
|
+
return ParameterSpecInput(
|
|
235
|
+
name=spec.name,
|
|
236
|
+
doubleParameterSpec=DoubleParameterSpecInput(
|
|
237
|
+
scaleType=spec.scale_type,
|
|
238
|
+
minValue=spec.min_value,
|
|
239
|
+
maxValue=spec.max_value
|
|
240
|
+
)
|
|
241
|
+
)
|
|
242
|
+
elif spec.type == 'INTEGER':
|
|
243
|
+
return ParameterSpecInput(
|
|
244
|
+
name=spec.name,
|
|
245
|
+
integerParameterSpec=IntegerParameterSpecInput(
|
|
246
|
+
scaleType=spec.scale_type,
|
|
247
|
+
minValue=spec.min_value,
|
|
248
|
+
maxValue=spec.max_value
|
|
249
|
+
)
|
|
250
|
+
)
|
|
251
|
+
elif spec.type == 'DISCRETE':
|
|
252
|
+
return ParameterSpecInput(
|
|
253
|
+
name=spec.name,
|
|
254
|
+
discreteParameterSpec=DiscreteParameterSpecInput(
|
|
255
|
+
discreteValues=spec.discrete_values
|
|
256
|
+
)
|
|
257
|
+
)
|
|
258
|
+
elif spec.type == 'CATEGORICAL':
|
|
259
|
+
return ParameterSpecInput(
|
|
260
|
+
name=spec.name,
|
|
261
|
+
categoryParameterSpec=CategoryParameterSpecInput(
|
|
262
|
+
categoricalValues=spec.categorical_values
|
|
263
|
+
)
|
|
264
|
+
)
|
|
265
|
+
else:
|
|
266
|
+
raise ValueError(f"Unsupported parameter spec type: {spec.type}")
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
class BindingAuth(str, Enum):
|
|
270
|
+
READ_ONLY = "READ_ONLY"
|
|
271
|
+
READ_WRITE = "READ_WRITE"
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
class DataBindingInput(BaseModel):
|
|
275
|
+
name: str
|
|
276
|
+
path: str
|
|
277
|
+
bindingType: Optional[str] = None
|
|
278
|
+
bindingAuth: Optional[BindingAuth] = None
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
class JobParameterInput(BaseModel):
|
|
282
|
+
name: str
|
|
283
|
+
value: pydantic.Json
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
class RestartWorkspaceInput(BaseModel):
|
|
287
|
+
dataBindings: List[DataBindingInput]
|
|
288
|
+
runtime: str
|
|
289
|
+
resource: str
|
|
290
|
+
parameters: Optional[List[JobParameterInput]] = Field(default_factory=list)
|
|
291
|
+
useRDMADevices: bool = False
|
|
292
|
+
|
|
293
|
+
|
|
294
|
+
class RestartBatchWorkspaceInput(BaseModel):
|
|
295
|
+
dataBindings: List[DataBindingInput]
|
|
296
|
+
runtime: str
|
|
297
|
+
resource: str
|
|
298
|
+
parameters: Optional[List[JobParameterInput]] = Field(default_factory=list)
|
|
299
|
+
useRDMADevices: bool = False
|
|
300
|
+
size: Optional[int] = None
|
|
301
|
+
|
|
302
|
+
|
|
303
|
+
def create_hypertuning(party_name, client: BayesGQLClient, project_id, datasets, runtime, resource, code_id,
|
|
304
|
+
command, hyper_tuning: HyperTuning):
|
|
305
|
+
# 判断hyper_tuning的参数,不为空字符串和None
|
|
306
|
+
if Utils.is_empty_or_none(hyper_tuning.hyperparameter_metric) or Utils.is_empty_or_none(
|
|
307
|
+
hyper_tuning.algorithm) or Utils.is_empty_or_none(hyper_tuning.goal):
|
|
308
|
+
print("请检查输入的 hupertuning 参数")
|
|
309
|
+
raise typer.Exit(code=1)
|
|
310
|
+
|
|
311
|
+
if Utils.is_empty_or_none(command):
|
|
312
|
+
print("command 不能为空")
|
|
313
|
+
raise typer.Exit(code=1)
|
|
314
|
+
|
|
315
|
+
if Utils.is_empty_or_none(code_id):
|
|
316
|
+
print("code_id 不能为空")
|
|
317
|
+
raise typer.Exit(code=1)
|
|
318
|
+
|
|
319
|
+
hyper_tuning_algorithm = Algorithm(hyper_tuning.algorithm.capitalize())
|
|
320
|
+
|
|
321
|
+
parameter_specs = hyper_tuning.get_parameter_specs()
|
|
322
|
+
parameter_specs_input = [convert_to_parameter_spec_input(spec) for spec in parameter_specs]
|
|
323
|
+
|
|
324
|
+
input_data = CreateHypertuningInput(
|
|
325
|
+
projectId=project_id,
|
|
326
|
+
runtime=runtime,
|
|
327
|
+
resource=resource,
|
|
328
|
+
command=command,
|
|
329
|
+
code=code_id,
|
|
330
|
+
maxJobCount=hyper_tuning.max_job_count,
|
|
331
|
+
hyperparameterMetric=hyper_tuning.hyperparameter_metric,
|
|
332
|
+
goal=hyper_tuning.goal,
|
|
333
|
+
algorithm=hyper_tuning_algorithm,
|
|
334
|
+
parameterSpecs=parameter_specs_input,
|
|
335
|
+
sideMetrics=hyper_tuning.side_metrics,
|
|
336
|
+
parallelCount=max(DEFAULT_PARALLEL_COUNT, hyper_tuning.parallel_count)
|
|
337
|
+
)
|
|
338
|
+
|
|
339
|
+
if datasets and len(datasets) > 0:
|
|
340
|
+
input_data.dataBindings = get_graphql_binding_datasets(datasets)
|
|
341
|
+
|
|
342
|
+
input_data.tagNames = [TagInput(name="BUSINESS_CHANNEL_ML")]
|
|
343
|
+
|
|
344
|
+
query = """
|
|
345
|
+
mutation CreateHypertuning($userId: String!, $input: CreateHypertuningInput!) {
|
|
346
|
+
createHypertuning(userId: $userId, input: $input) {
|
|
347
|
+
id
|
|
348
|
+
links {
|
|
349
|
+
name
|
|
350
|
+
value
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
"""
|
|
355
|
+
variables = {"userId": party_name, "input": input_data.model_dump()}
|
|
356
|
+
|
|
357
|
+
try:
|
|
358
|
+
response = client.exec(query, variables)
|
|
359
|
+
except TransportQueryError as e:
|
|
360
|
+
error_message = e.errors[0]['message'] if e.errors else str(e)
|
|
361
|
+
print(error_message, file=sys.stderr)
|
|
362
|
+
sys.exit(1)
|
|
363
|
+
|
|
364
|
+
result = response.get("createHypertuning")
|
|
365
|
+
if result is None:
|
|
366
|
+
raise ValueError("create hypertuning result is none")
|
|
367
|
+
|
|
368
|
+
return Job(**result)
|
|
369
|
+
|
|
370
|
+
|
|
371
|
+
def update_job_description(client: BayesGQLClient, party_name, job_id, message):
|
|
372
|
+
query = """
|
|
373
|
+
mutation UpdateJob($username: String!, $jobId: String!, $input: UpdateJobInput!) {
|
|
374
|
+
updateJob(userId: $username, jobId: $jobId, input: $input) {
|
|
375
|
+
id
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
"""
|
|
379
|
+
input_data = UpdateJobInput(description=message)
|
|
380
|
+
variables = {"username": party_name, "jobId": job_id, "input": input_data.model_dump()}
|
|
381
|
+
try:
|
|
382
|
+
response = client.exec(query, variables)
|
|
383
|
+
except TransportQueryError as e:
|
|
384
|
+
error_message = e.errors[0]['message'] if e.errors else str(e)
|
|
385
|
+
print(error_message, file=sys.stderr)
|
|
386
|
+
sys.exit(1)
|
|
387
|
+
|
|
388
|
+
result = response.get("updateJob")
|
|
389
|
+
if result is None:
|
|
390
|
+
raise ValueError("updateJob result is none")
|
|
391
|
+
|
|
392
|
+
return Job(**result)
|
|
393
|
+
|
|
394
|
+
|
|
395
|
+
def get_restart_job_graphql_dataBindings(dataBindings: List[str]) -> DataBindingInput:
|
|
396
|
+
result = []
|
|
397
|
+
for dataBinding in dataBindings:
|
|
398
|
+
list_parts = dataBinding.split(":")
|
|
399
|
+
if len(list_parts) == 2:
|
|
400
|
+
name = list_parts[0]
|
|
401
|
+
path = list_parts[1]
|
|
402
|
+
|
|
403
|
+
binding_type = "OUTPUT" if name.endswith("/output") else None
|
|
404
|
+
result.append(DataBindingInput(
|
|
405
|
+
name=name,
|
|
406
|
+
path=path,
|
|
407
|
+
bindingAuth=BindingAuthType.READ_ONLY,
|
|
408
|
+
bindingType=binding_type
|
|
409
|
+
))
|
|
410
|
+
elif len(list_parts) == 3:
|
|
411
|
+
name = list_parts[0]
|
|
412
|
+
path = list_parts[1]
|
|
413
|
+
|
|
414
|
+
binding_type = "OUTPUT" if name.endswith("/output") else None
|
|
415
|
+
|
|
416
|
+
auth = list_parts[2].upper()
|
|
417
|
+
if auth == "RO" or auth == "READ_ONLY":
|
|
418
|
+
result.append(DataBindingInput(
|
|
419
|
+
name=name,
|
|
420
|
+
path=path,
|
|
421
|
+
bindingAuth=BindingAuthType.READ_ONLY
|
|
422
|
+
))
|
|
423
|
+
elif auth == "RW" or auth == "READ_WRITE":
|
|
424
|
+
result.append(DataBindingInput(
|
|
425
|
+
name=name,
|
|
426
|
+
path=path,
|
|
427
|
+
bindingAuth=BindingAuthType.READ_WRITE
|
|
428
|
+
))
|
|
429
|
+
|
|
430
|
+
return result
|
|
431
|
+
|
|
432
|
+
|
|
433
|
+
def restart_workspace(client: BayesGQLClient, party_name, jid, runtime, resource, dataBindings, parameters):
|
|
434
|
+
query = """
|
|
435
|
+
mutation RestartWorkspace($userId: String!, $jobId: String!, $input: RestartWorkspaceInput!) {
|
|
436
|
+
restartWorkspace(userId: $userId, jobId: $jobId, input: $input) {
|
|
437
|
+
id
|
|
438
|
+
status
|
|
439
|
+
links {
|
|
440
|
+
name
|
|
441
|
+
value
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
"""
|
|
446
|
+
|
|
447
|
+
input_data = RestartWorkspaceInput(
|
|
448
|
+
runtime=runtime,
|
|
449
|
+
resource=resource,
|
|
450
|
+
parameters=parameters,
|
|
451
|
+
dataBindings=get_restart_job_graphql_dataBindings(dataBindings) if dataBindings else []
|
|
452
|
+
)
|
|
453
|
+
|
|
454
|
+
variables = {"userId": party_name, "jobId": jid, "input": input_data.model_dump()}
|
|
455
|
+
|
|
456
|
+
try:
|
|
457
|
+
response = client.exec(query, variables)
|
|
458
|
+
except TransportQueryError as e:
|
|
459
|
+
error_message = e.errors[0]['message'] if e.errors else str(e)
|
|
460
|
+
print(error_message, file=sys.stderr)
|
|
461
|
+
sys.exit(1)
|
|
462
|
+
|
|
463
|
+
result = response.get("restartWorkspace")
|
|
464
|
+
if result is None:
|
|
465
|
+
raise ValueError("restartWorkspace result is none")
|
|
466
|
+
|
|
467
|
+
return Job(**result)
|
|
468
|
+
|
|
469
|
+
|
|
470
|
+
def restart_batch_workspace(client: BayesGQLClient, party_name, jid, runtime, resource, dataBindings, parameters):
|
|
471
|
+
query = """
|
|
472
|
+
mutation RestartBatchWorkspace($userId: String!, $jobId: String!, $input: RestartBatchWorkspaceInput!) {
|
|
473
|
+
restartBatchWorkspace(userId: $userId, jobId: $jobId, input: $input) {
|
|
474
|
+
id
|
|
475
|
+
status
|
|
476
|
+
links {
|
|
477
|
+
name
|
|
478
|
+
value
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
"""
|
|
483
|
+
|
|
484
|
+
input_data = RestartBatchWorkspaceInput(
|
|
485
|
+
runtime=runtime,
|
|
486
|
+
resource=resource,
|
|
487
|
+
parameters=parameters,
|
|
488
|
+
dataBindings=get_restart_job_graphql_dataBindings(dataBindings) if dataBindings else []
|
|
489
|
+
)
|
|
490
|
+
|
|
491
|
+
variables = {"userId": party_name, "jobId": jid, "input": input_data.model_dump()}
|
|
492
|
+
|
|
493
|
+
try:
|
|
494
|
+
response = client.exec(query, variables)
|
|
495
|
+
except TransportQueryError as e:
|
|
496
|
+
error_message = e.errors[0]['message'] if e.errors else str(e)
|
|
497
|
+
print(error_message, file=sys.stderr)
|
|
498
|
+
sys.exit(1)
|
|
499
|
+
|
|
500
|
+
result = response.get("restartBatchWorkspace")
|
|
501
|
+
if result is None:
|
|
502
|
+
raise ValueError("restartBatchWorkspace result is none")
|
|
503
|
+
|
|
504
|
+
return Job(**result)
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import os
|
|
3
|
+
import base64
|
|
4
|
+
import json
|
|
5
|
+
from typing import Optional, Tuple
|
|
6
|
+
|
|
7
|
+
import requests
|
|
8
|
+
from tusclient import client
|
|
9
|
+
from tusclient.storage import filestorage
|
|
10
|
+
from tqdm import tqdm
|
|
11
|
+
from tusclient.exceptions import TusCommunicationError
|
|
12
|
+
|
|
13
|
+
from pydantic import BaseModel
|
|
14
|
+
|
|
15
|
+
from bayes.error import Error
|
|
16
|
+
from bayes.model.file.settings import BayesEnvConfig, BayesSettings
|
|
17
|
+
from bayes.utils import Utils
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class RequestUploadUrl(BaseModel):
|
|
21
|
+
upload_url: str
|
|
22
|
+
token: str
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def upload_request() -> Tuple[Optional[RequestUploadUrl], Optional[Exception]]:
|
|
26
|
+
# https://beta.openbayes.com/api/users/Qion1/jobs/upload-request?protocol=tusd
|
|
27
|
+
default_env = BayesSettings().default_env
|
|
28
|
+
url = f"{default_env.endpoint}/api/users/{default_env.username}/jobs/upload-request?protocol=tusd"
|
|
29
|
+
print(f"upload_request url:{url}")
|
|
30
|
+
auth_token = default_env.token
|
|
31
|
+
|
|
32
|
+
try:
|
|
33
|
+
response = requests.post(url, headers={"Authorization": f"Bearer {auth_token}"})
|
|
34
|
+
except requests.RequestException as e:
|
|
35
|
+
return None, e
|
|
36
|
+
|
|
37
|
+
logging.info(response)
|
|
38
|
+
|
|
39
|
+
if response.status_code != 200:
|
|
40
|
+
err = request_failed(response.status_code)
|
|
41
|
+
return None, err
|
|
42
|
+
|
|
43
|
+
try:
|
|
44
|
+
result = response.json()
|
|
45
|
+
upload_request = RequestUploadUrl(**result)
|
|
46
|
+
return upload_request, None
|
|
47
|
+
except ValueError as e:
|
|
48
|
+
# JSON 解码错误
|
|
49
|
+
return None, e
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def upload_file(
|
|
53
|
+
file_path: str, upload_url: str, token: str
|
|
54
|
+
) -> Tuple[bool, Optional[str], Optional[Exception]]:
|
|
55
|
+
try:
|
|
56
|
+
my_client = client.TusClient(
|
|
57
|
+
upload_url, headers={"Authorization": f"Bearer {token}"}
|
|
58
|
+
)
|
|
59
|
+
file_size = os.path.getsize(file_path)
|
|
60
|
+
|
|
61
|
+
# Create a FileStorage instance for resumability
|
|
62
|
+
storage = filestorage.FileStorage(
|
|
63
|
+
os.path.join(os.path.dirname(file_path), ".tus_storage")
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
# Prepare metadata
|
|
67
|
+
filename = os.path.basename(file_path)
|
|
68
|
+
metadata = {"filename": filename}
|
|
69
|
+
|
|
70
|
+
with tqdm(total=file_size, unit="B", unit_scale=True, desc="Uploading") as pbar:
|
|
71
|
+
uploader = my_client.uploader(
|
|
72
|
+
file_path,
|
|
73
|
+
chunk_size=2 * 1024 * 1024,
|
|
74
|
+
store_url=True,
|
|
75
|
+
url_storage=storage,
|
|
76
|
+
upload_checksum=False,
|
|
77
|
+
metadata=metadata,
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
while uploader.offset < file_size:
|
|
81
|
+
uploader.upload_chunk()
|
|
82
|
+
pbar.update(uploader.offset - pbar.n)
|
|
83
|
+
|
|
84
|
+
print(f"File uploaded successfully: {file_path}")
|
|
85
|
+
|
|
86
|
+
# Remove filestorage after upload successfully
|
|
87
|
+
storage_path = os.path.join(os.path.dirname(file_path), ".tus_storage")
|
|
88
|
+
if os.path.exists(storage_path):
|
|
89
|
+
os.remove(storage_path)
|
|
90
|
+
print(f"Removed filestorage: {storage_path}")
|
|
91
|
+
|
|
92
|
+
# Decode the JWT token to get the payload
|
|
93
|
+
payload_part = token.split(".")[1]
|
|
94
|
+
padded_payload = payload_part + "=" * (4 - len(payload_part) % 4)
|
|
95
|
+
decoded_payload = base64.urlsafe_b64decode(padded_payload).decode("utf-8")
|
|
96
|
+
payload_data = json.loads(decoded_payload)
|
|
97
|
+
sub_payload = json.loads(payload_data["sub"])["payload"]
|
|
98
|
+
|
|
99
|
+
return True, sub_payload, None
|
|
100
|
+
|
|
101
|
+
except TusCommunicationError as e:
|
|
102
|
+
print(f"TUS Communication Error: {e}")
|
|
103
|
+
print(f"Response status: {e.status_code}")
|
|
104
|
+
print(f"Response body: {e.response_content}")
|
|
105
|
+
return False, None, e
|
|
106
|
+
except Exception as e:
|
|
107
|
+
print(f"Unexpected error: {e}")
|
|
108
|
+
return False, None, e
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def request_failed(status_code: int):
|
|
112
|
+
if status_code >= 400:
|
|
113
|
+
return Exception(f"Request failed with status code {status_code}")
|
|
114
|
+
return None
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
from typing import List
|
|
2
|
+
|
|
3
|
+
from gql.transport.exceptions import TransportQueryError
|
|
4
|
+
|
|
5
|
+
from .base import BayesGQLClient
|
|
6
|
+
|
|
7
|
+
from pydantic import BaseModel
|
|
8
|
+
from bayes.error import Error
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class OrgModel(BaseModel):
|
|
12
|
+
id: str
|
|
13
|
+
displayName: str
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class UserOrgsModel(BaseModel):
|
|
17
|
+
role: str
|
|
18
|
+
org: OrgModel
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def get_org(client: BayesGQLClient, org_id: str) -> OrgModel:
|
|
22
|
+
query = """
|
|
23
|
+
query Org($orgId: ID!) {
|
|
24
|
+
org(id: $orgId) {
|
|
25
|
+
id
|
|
26
|
+
displayName
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
"""
|
|
30
|
+
variables = {"orgId": org_id}
|
|
31
|
+
try:
|
|
32
|
+
response = client.exec(query, variables)
|
|
33
|
+
except TransportQueryError as e:
|
|
34
|
+
raise Error(e.errors[0]['message'] if e.errors else str(e))
|
|
35
|
+
|
|
36
|
+
org_data = response.get("org")
|
|
37
|
+
if org_data is None:
|
|
38
|
+
raise Error("get org is none")
|
|
39
|
+
|
|
40
|
+
return OrgModel(**org_data)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def get_user_orgs(client: BayesGQLClient, username: str) -> List[UserOrgsModel]:
|
|
44
|
+
query = """
|
|
45
|
+
query Orgs($username: String!) {
|
|
46
|
+
user(username: $username) {
|
|
47
|
+
orgs {
|
|
48
|
+
role
|
|
49
|
+
org {
|
|
50
|
+
id
|
|
51
|
+
displayName
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
"""
|
|
57
|
+
variables = {"username": username}
|
|
58
|
+
try:
|
|
59
|
+
response = client.exec(query, variables)
|
|
60
|
+
except TransportQueryError as e:
|
|
61
|
+
raise Error(e.errors[0]['message'] if e.errors else str(e))
|
|
62
|
+
|
|
63
|
+
user_orgs_data = response.get("user", {}).get("orgs")
|
|
64
|
+
if not user_orgs_data:
|
|
65
|
+
return None
|
|
66
|
+
|
|
67
|
+
return [UserOrgsModel(**org) for org in user_orgs_data]
|