malevich-coretools 0.2.1__tar.gz → 0.2.3__tar.gz
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 malevich-coretools might be problematic. Click here for more details.
- {malevich-coretools-0.2.1/malevich_coretools.egg-info → malevich-coretools-0.2.3}/PKG-INFO +1 -1
- malevich-coretools-0.2.3/VERSION +1 -0
- {malevich-coretools-0.2.1 → malevich-coretools-0.2.3}/malevich_coretools/__init__.py +1 -0
- {malevich-coretools-0.2.1 → malevich-coretools-0.2.3}/malevich_coretools/abstract/abstract.py +3 -0
- {malevich-coretools-0.2.1 → malevich-coretools-0.2.3}/malevich_coretools/funcs/funcs.py +7 -0
- {malevich-coretools-0.2.1 → malevich-coretools-0.2.3}/malevich_coretools/secondary/const.py +2 -0
- malevich-coretools-0.2.3/malevich_coretools/tools/__init__.py +1 -0
- malevich-coretools-0.2.3/malevich_coretools/tools/vast.py +306 -0
- {malevich-coretools-0.2.1 → malevich-coretools-0.2.3}/malevich_coretools/utils.py +131 -119
- {malevich-coretools-0.2.1 → malevich-coretools-0.2.3/malevich_coretools.egg-info}/PKG-INFO +1 -1
- {malevich-coretools-0.2.1 → malevich-coretools-0.2.3}/malevich_coretools.egg-info/SOURCES.txt +3 -1
- malevich-coretools-0.2.1/VERSION +0 -1
- {malevich-coretools-0.2.1 → malevich-coretools-0.2.3}/LICENSE +0 -0
- {malevich-coretools-0.2.1 → malevich-coretools-0.2.3}/MANIFEST.in +0 -0
- {malevich-coretools-0.2.1 → malevich-coretools-0.2.3}/README.md +0 -0
- {malevich-coretools-0.2.1 → malevich-coretools-0.2.3}/malevich_coretools/abstract/__init__.py +0 -0
- {malevich-coretools-0.2.1 → malevich-coretools-0.2.3}/malevich_coretools/funcs/__init__.py +0 -0
- {malevich-coretools-0.2.1 → malevich-coretools-0.2.3}/malevich_coretools/funcs/helpers.py +0 -0
- {malevich-coretools-0.2.1 → malevich-coretools-0.2.3}/malevich_coretools/secondary/__init__.py +0 -0
- {malevich-coretools-0.2.1 → malevich-coretools-0.2.3}/malevich_coretools/secondary/config.py +0 -0
- {malevich-coretools-0.2.1 → malevich-coretools-0.2.3}/malevich_coretools/secondary/helpers.py +0 -0
- {malevich-coretools-0.2.1 → malevich-coretools-0.2.3}/malevich_coretools.egg-info/dependency_links.txt +0 -0
- {malevich-coretools-0.2.1 → malevich-coretools-0.2.3}/malevich_coretools.egg-info/requires.txt +0 -0
- {malevich-coretools-0.2.1 → malevich-coretools-0.2.3}/malevich_coretools.egg-info/top_level.txt +0 -0
- {malevich-coretools-0.2.1 → malevich-coretools-0.2.3}/pyproject.toml +0 -0
- {malevich-coretools-0.2.1 → malevich-coretools-0.2.3}/requirements.txt +0 -0
- {malevich-coretools-0.2.1 → malevich-coretools-0.2.3}/setup.cfg +0 -0
- {malevich-coretools-0.2.1 → malevich-coretools-0.2.3}/setup.py +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
0.2.3
|
{malevich-coretools-0.2.1 → malevich-coretools-0.2.3}/malevich_coretools/abstract/abstract.py
RENAMED
|
@@ -103,6 +103,8 @@ class UserApp(BaseModel):
|
|
|
103
103
|
outputId: Optional[Alias.Id]
|
|
104
104
|
cfg: Optional[Alias.Json]
|
|
105
105
|
image: JsonImage
|
|
106
|
+
platform: str
|
|
107
|
+
platformSettings: Optional[str]
|
|
106
108
|
collectionsFrom: Optional[Dict[str, str]]
|
|
107
109
|
extraCollectionsFrom: Optional[Dict[str, str]]
|
|
108
110
|
|
|
@@ -391,3 +393,4 @@ class AppFunctionsInfo(BaseModel):
|
|
|
391
393
|
schemes: Dict[str, str] = dict()
|
|
392
394
|
inits: Dict[str, InitInfo] = dict()
|
|
393
395
|
logs: Optional[str] = None
|
|
396
|
+
instanceInfo: Optional[str] = None # json with info about instance
|
|
@@ -451,6 +451,13 @@ def get_app_info_by_real_id(id: str, parse: bool, *args, **kwargs) -> Union[Alia
|
|
|
451
451
|
return res
|
|
452
452
|
|
|
453
453
|
|
|
454
|
+
def get_image_info(data: JsonImage, parse: bool, *args, **kwargs) -> Union[Alias.Json, AppFunctionsInfo]:
|
|
455
|
+
res = send_to_core_modify(MANAGER_IMAGE_INFO, data, with_auth=False, with_show=False, show_func = show_fail_app_info, *args, **kwargs)
|
|
456
|
+
if parse:
|
|
457
|
+
res = model_from_json(res, AppFunctionsInfo)
|
|
458
|
+
return res
|
|
459
|
+
|
|
460
|
+
|
|
454
461
|
def get_task_schedules(data: Operation, with_show: bool, *args, **kwargs) -> Schedules:
|
|
455
462
|
res = model_from_json(send_to_core_modify(MANAGER_TASK_SCHEDULES, data, with_show=False, *args, **kwargs), Schedules)
|
|
456
463
|
if with_show:
|
|
@@ -12,6 +12,7 @@ LONG_SLEEP_TIME = 1 # second
|
|
|
12
12
|
WAIT_RESULT_TIMEOUT = 60 * 60 # hour
|
|
13
13
|
AIOHTTP_TIMEOUT = aiohttp.ClientTimeout(total=60 * 10) # 10 min
|
|
14
14
|
AIOHTTP_TIMEOUT_MINI = aiohttp.ClientTimeout(total=60 * 5) # 5 min
|
|
15
|
+
POSSIBLE_APPS_PLATFORMS = {"base", "vast"}
|
|
15
16
|
# endpoints
|
|
16
17
|
|
|
17
18
|
def with_wait(url, wait) -> str:
|
|
@@ -124,6 +125,7 @@ MANAGER_DAG_KEY_VALUE = lambda wait: with_wait(f"{MANAGER_MAIN}/dagKeyValue", wa
|
|
|
124
125
|
MANAGER_DAG_KEY_VALUE_OPERATION_ID = lambda operationId: f"{MANAGER_MAIN}/dagKeyValue/{operationId}"
|
|
125
126
|
MANAGER_APP_INFO = lambda appId: f"{MANAGER_MAIN}/appInfo/{appId}"
|
|
126
127
|
MANAGER_APP_INFO_REAL_ID = lambda appId: f"{MANAGER_MAIN}/appInfo/realId/{appId}"
|
|
128
|
+
MANAGER_IMAGE_INFO = f"{MANAGER_MAIN}/imageInfo"
|
|
127
129
|
MANAGER_TASK_SCHEDULES = f"{MANAGER_MAIN}/task/schedules"
|
|
128
130
|
MANAGER_TASK = lambda wait: with_wait(f"{MANAGER_MAIN}/task", wait)
|
|
129
131
|
MANAGER_TASK_RUN = lambda wait: with_wait(f"{MANAGER_MAIN}/task/run", wait)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .vast import vast_settings
|
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
from typing import Dict, Optional, Union, Any
|
|
2
|
+
import re
|
|
3
|
+
import json
|
|
4
|
+
|
|
5
|
+
# from https://raw.githubusercontent.com/vast-ai/vast-python/master/vast.py
|
|
6
|
+
def parse_query(query_str: str, res: Dict = None) -> Dict:
|
|
7
|
+
"""
|
|
8
|
+
Basically takes a query string (like the ones in the examples of commands for the search__offers function) and
|
|
9
|
+
processes it into a dict of URL parameters to be sent to the server.
|
|
10
|
+
|
|
11
|
+
:param str query_str:
|
|
12
|
+
:param Dict res:
|
|
13
|
+
:return Dict:
|
|
14
|
+
"""
|
|
15
|
+
if res is None: res = {}
|
|
16
|
+
if type(query_str) == list:
|
|
17
|
+
query_str = " ".join(query_str)
|
|
18
|
+
query_str = query_str.strip()
|
|
19
|
+
opts = re.findall("([a-zA-Z0-9_]+)( *[=><!]+| +(?:[lg]te?|nin|neq|eq|not ?eq|not ?in|in) )?( *)(\[[^\]]+\]|[^ ]+)?( *)", query_str)
|
|
20
|
+
|
|
21
|
+
#print(opts)
|
|
22
|
+
# res = {}
|
|
23
|
+
op_names = {
|
|
24
|
+
">=": "gte",
|
|
25
|
+
">": "gt",
|
|
26
|
+
"gt": "gt",
|
|
27
|
+
"gte": "gte",
|
|
28
|
+
"<=": "lte",
|
|
29
|
+
"<": "lt",
|
|
30
|
+
"lt": "lt",
|
|
31
|
+
"lte": "lte",
|
|
32
|
+
"!=": "neq",
|
|
33
|
+
"==": "eq",
|
|
34
|
+
"=": "eq",
|
|
35
|
+
"eq": "eq",
|
|
36
|
+
"neq": "neq",
|
|
37
|
+
"noteq": "neq",
|
|
38
|
+
"not eq": "neq",
|
|
39
|
+
"notin": "notin",
|
|
40
|
+
"not in": "notin",
|
|
41
|
+
"nin": "notin",
|
|
42
|
+
"in": "in",
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
field_alias = {
|
|
46
|
+
"cuda_vers": "cuda_max_good",
|
|
47
|
+
"display_active": "gpu_display_active",
|
|
48
|
+
"reliability": "reliability2",
|
|
49
|
+
"dlperf_usd": "dlperf_per_dphtotal",
|
|
50
|
+
"dph": "dph_total",
|
|
51
|
+
"flops_usd": "flops_per_dphtotal",
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
field_multiplier = {
|
|
55
|
+
"cpu_ram": 1000,
|
|
56
|
+
"gpu_ram": 1000,
|
|
57
|
+
"duration": 24.0 * 60.0 * 60.0,
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
fields = {
|
|
61
|
+
"bw_nvlink",
|
|
62
|
+
"compute_cap",
|
|
63
|
+
"cpu_cores",
|
|
64
|
+
"cpu_cores_effective",
|
|
65
|
+
"cpu_ram",
|
|
66
|
+
"cuda_max_good",
|
|
67
|
+
"datacenter",
|
|
68
|
+
"direct_port_count",
|
|
69
|
+
"driver_version",
|
|
70
|
+
"disk_bw",
|
|
71
|
+
"disk_space",
|
|
72
|
+
"dlperf",
|
|
73
|
+
"dlperf_per_dphtotal",
|
|
74
|
+
"dph_total",
|
|
75
|
+
"duration",
|
|
76
|
+
"external",
|
|
77
|
+
"flops_per_dphtotal",
|
|
78
|
+
"gpu_display_active",
|
|
79
|
+
# "gpu_ram_free_min",
|
|
80
|
+
"gpu_mem_bw",
|
|
81
|
+
"gpu_name",
|
|
82
|
+
"gpu_ram",
|
|
83
|
+
"gpu_display_active",
|
|
84
|
+
"has_avx",
|
|
85
|
+
"host_id",
|
|
86
|
+
"id",
|
|
87
|
+
"inet_down",
|
|
88
|
+
"inet_down_cost",
|
|
89
|
+
"inet_up",
|
|
90
|
+
"inet_up_cost",
|
|
91
|
+
"machine_id",
|
|
92
|
+
"min_bid",
|
|
93
|
+
"mobo_name",
|
|
94
|
+
"num_gpus",
|
|
95
|
+
"pci_gen",
|
|
96
|
+
"pcie_bw",
|
|
97
|
+
"reliability2",
|
|
98
|
+
"rentable",
|
|
99
|
+
"rented",
|
|
100
|
+
"storage_cost",
|
|
101
|
+
"static_ip",
|
|
102
|
+
"total_flops",
|
|
103
|
+
"verification",
|
|
104
|
+
"verified",
|
|
105
|
+
"geolocation"
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
joined = "".join("".join(x) for x in opts)
|
|
109
|
+
if joined != query_str:
|
|
110
|
+
raise ValueError(
|
|
111
|
+
"Unconsumed text. Did you forget to quote your query? " + repr(joined) + " != " + repr(query_str))
|
|
112
|
+
|
|
113
|
+
for field, op, _, value, _ in opts:
|
|
114
|
+
value = value.strip(",[]")
|
|
115
|
+
v = res.setdefault(field, {})
|
|
116
|
+
op = op.strip()
|
|
117
|
+
op_name = op_names.get(op)
|
|
118
|
+
|
|
119
|
+
if field in field_alias:
|
|
120
|
+
field = field_alias[field]
|
|
121
|
+
|
|
122
|
+
if (field == "driver_version") and ('.' in value):
|
|
123
|
+
value = numeric_version(value)
|
|
124
|
+
|
|
125
|
+
if not field in fields:
|
|
126
|
+
print("Warning: Unrecognized field: {}, see list of recognized fields.".format(field), file=sys.stderr);
|
|
127
|
+
if not op_name:
|
|
128
|
+
raise ValueError("Unknown operator. Did you forget to quote your query? " + repr(op).strip("u"))
|
|
129
|
+
if op_name in ["in", "notin"]:
|
|
130
|
+
value = [x.strip() for x in value.split(",") if x.strip()]
|
|
131
|
+
if not value:
|
|
132
|
+
raise ValueError("Value cannot be blank. Did you forget to quote your query? " + repr((field, op, value)))
|
|
133
|
+
if not field:
|
|
134
|
+
raise ValueError("Field cannot be blank. Did you forget to quote your query? " + repr((field, op, value)))
|
|
135
|
+
if value in ["?", "*", "any"]:
|
|
136
|
+
if op_name != "eq":
|
|
137
|
+
raise ValueError("Wildcard only makes sense with equals.")
|
|
138
|
+
if field in v:
|
|
139
|
+
del v[field]
|
|
140
|
+
if field in res:
|
|
141
|
+
del res[field]
|
|
142
|
+
continue
|
|
143
|
+
|
|
144
|
+
if field in field_multiplier:
|
|
145
|
+
value = float(value) * field_multiplier[field]
|
|
146
|
+
|
|
147
|
+
if isinstance(value, str):
|
|
148
|
+
v[op_name] = value.replace('_', ' ')
|
|
149
|
+
else:
|
|
150
|
+
v[op_name] = value
|
|
151
|
+
|
|
152
|
+
else:
|
|
153
|
+
#print(value)
|
|
154
|
+
if value == 'true':
|
|
155
|
+
v[op_name] = True
|
|
156
|
+
elif value == 'False':
|
|
157
|
+
v[op_name] = False
|
|
158
|
+
elif isinstance(value, str):
|
|
159
|
+
v[op_name] = value.replace('_', ' ')
|
|
160
|
+
else:
|
|
161
|
+
#v[op_name] = [v.replace('_', ' ') for v in value]
|
|
162
|
+
v[op_name] = value
|
|
163
|
+
|
|
164
|
+
res[field] = v;
|
|
165
|
+
|
|
166
|
+
return res
|
|
167
|
+
|
|
168
|
+
# from https://raw.githubusercontent.com/vast-ai/vast-python/master/vast.py
|
|
169
|
+
def parse(search_query: str, show_order: str = "score-", no_default: bool = False, instance_type = "on-demand") -> Dict[str, Any]:
|
|
170
|
+
"""Query syntax:
|
|
171
|
+
|
|
172
|
+
query = comparison comparison...
|
|
173
|
+
comparison = field op value
|
|
174
|
+
field = <name of a field>
|
|
175
|
+
op = one of: <, <=, ==, !=, >=, >, in, notin
|
|
176
|
+
value = <bool, int, float, etc> | 'any'
|
|
177
|
+
bool: True, False
|
|
178
|
+
|
|
179
|
+
note: to pass '>' and '<' on the command line, make sure to use quotes
|
|
180
|
+
note: to encode a string query value (ie for gpu_name), replace any spaces ' ' with underscore '_'
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
Examples:
|
|
184
|
+
|
|
185
|
+
# search for somewhat reliable single RTX 3090 instances, filter out any duplicates or offers that conflict with our existing stopped instances
|
|
186
|
+
./vast search offers 'reliability > 0.98 num_gpus=1 gpu_name=RTX_3090 rented=False'
|
|
187
|
+
|
|
188
|
+
# search for datacenter gpus with minimal compute_cap and total_flops
|
|
189
|
+
./vast search offers 'compute_cap > 610 total_flops > 5 datacenter=True'
|
|
190
|
+
|
|
191
|
+
# search for reliable machines with at least 4 gpus, unverified, order by num_gpus, allow duplicates
|
|
192
|
+
./vast search offers 'reliability > 0.99 num_gpus>=4 verified=False rented=any' -o 'num_gpus-'
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
Available fields:
|
|
196
|
+
|
|
197
|
+
Name Type Description
|
|
198
|
+
|
|
199
|
+
bw_nvlink float bandwidth NVLink
|
|
200
|
+
compute_cap: int cuda compute capability*100 (ie: 650 for 6.5, 700 for 7.0)
|
|
201
|
+
cpu_cores: int # virtual cpus
|
|
202
|
+
cpu_cores_effective: float # virtual cpus you get
|
|
203
|
+
cpu_ram: float system RAM in gigabytes
|
|
204
|
+
cuda_vers: float machine max supported cuda version (based on driver version)
|
|
205
|
+
datacenter: bool show only datacenter offers
|
|
206
|
+
direct_port_count int open ports on host's router
|
|
207
|
+
disk_bw: float disk read bandwidth, in MB/s
|
|
208
|
+
disk_space: float disk storage space, in GB
|
|
209
|
+
dlperf: float DL-perf score (see FAQ for explanation)
|
|
210
|
+
dlperf_usd: float DL-perf/$
|
|
211
|
+
dph: float $/hour rental cost
|
|
212
|
+
driver_version string machine's nvidia driver version as 3 digit string ex. "535.86.05"
|
|
213
|
+
duration: float max rental duration in days
|
|
214
|
+
external: bool show external offers in addition to datacenter offers
|
|
215
|
+
flops_usd: float TFLOPs/$
|
|
216
|
+
geolocation: string Two letter country code. Works with operators =, !=, in, not in (e.g. geolocation not in [XV,XZ])
|
|
217
|
+
gpu_mem_bw: float GPU memory bandwidth in GB/s
|
|
218
|
+
gpu_name: string GPU model name (no quotes, replace spaces with underscores, ie: RTX_3090 rather than 'RTX 3090')
|
|
219
|
+
gpu_ram: float GPU RAM in GB
|
|
220
|
+
gpu_frac: float Ratio of GPUs in the offer to gpus in the system
|
|
221
|
+
gpu_display_active: bool True if the GPU has a display attached
|
|
222
|
+
has_avx: bool CPU supports AVX instruction set.
|
|
223
|
+
id: int instance unique ID
|
|
224
|
+
inet_down: float internet download speed in Mb/s
|
|
225
|
+
inet_down_cost: float internet download bandwidth cost in $/GB
|
|
226
|
+
inet_up: float internet upload speed in Mb/s
|
|
227
|
+
inet_up_cost: float internet upload bandwidth cost in $/GB
|
|
228
|
+
machine_id int machine id of instance
|
|
229
|
+
min_bid: float current minimum bid price in $/hr for interruptible
|
|
230
|
+
num_gpus: int # of GPUs
|
|
231
|
+
pci_gen: float PCIE generation
|
|
232
|
+
pcie_bw: float PCIE bandwidth (CPU to GPU)
|
|
233
|
+
reliability: float machine reliability score (see FAQ for explanation)
|
|
234
|
+
rentable: bool is the instance currently rentable
|
|
235
|
+
rented: bool allow/disallow duplicates and potential conflicts with existing stopped instances
|
|
236
|
+
storage_cost: float storage cost in $/GB/month
|
|
237
|
+
static_ip: bool is the IP addr static/stable
|
|
238
|
+
total_flops: float total TFLOPs from all GPUs
|
|
239
|
+
verified: bool is the machine verified
|
|
240
|
+
"""
|
|
241
|
+
field_alias = {
|
|
242
|
+
"cuda_vers": "cuda_max_good",
|
|
243
|
+
"reliability": "reliability2",
|
|
244
|
+
"dlperf_usd": "dlperf_per_dphtotal",
|
|
245
|
+
"dph": "dph_total",
|
|
246
|
+
"flops_usd": "flops_per_dphtotal",
|
|
247
|
+
};
|
|
248
|
+
|
|
249
|
+
try:
|
|
250
|
+
|
|
251
|
+
if no_default:
|
|
252
|
+
query = {}
|
|
253
|
+
else:
|
|
254
|
+
query = {"verified": {"eq": True}, "external": {"eq": False}, "rentable": {"eq": True}, "rented": {"eq": False}}
|
|
255
|
+
#query = {"verified": {"eq": True}, "external": {"eq": False}, "rentable": {"eq": True} }
|
|
256
|
+
|
|
257
|
+
if search_query is not None:
|
|
258
|
+
query = parse_query(search_query, query)
|
|
259
|
+
|
|
260
|
+
order = []
|
|
261
|
+
for name in show_order.split(","):
|
|
262
|
+
name = name.strip()
|
|
263
|
+
if not name: continue
|
|
264
|
+
direction = "asc"
|
|
265
|
+
if name.strip("-") != name:
|
|
266
|
+
direction = "desc"
|
|
267
|
+
field = name.strip("-");
|
|
268
|
+
if field in field_alias:
|
|
269
|
+
field = field_alias[field];
|
|
270
|
+
order.append([field, direction])
|
|
271
|
+
|
|
272
|
+
query["order"] = order
|
|
273
|
+
query["type"] = instance_type
|
|
274
|
+
# For backwards compatibility, support --type=interruptible option
|
|
275
|
+
if query["type"] == 'interruptible':
|
|
276
|
+
query["type"] = 'bid'
|
|
277
|
+
except ValueError as e:
|
|
278
|
+
print("Error: ", e)
|
|
279
|
+
return None
|
|
280
|
+
return query
|
|
281
|
+
|
|
282
|
+
def vast_settings(id: Optional[str] = None, query: Optional[Union[str, Dict[str, Any]]] = None) -> str:
|
|
283
|
+
"""
|
|
284
|
+
Platform settings for create app:\n
|
|
285
|
+
by nothing - use default query inside\n
|
|
286
|
+
by offer id - try use this id for app\n
|
|
287
|
+
by query - use this query inside\n
|
|
288
|
+
|
|
289
|
+
Examples:\n
|
|
290
|
+
vast_settings()\n
|
|
291
|
+
vast_settings(id="<id, that found with vast (vastai search offers)>")\n
|
|
292
|
+
vast_settings(query={"dph": {"lt": "0.1"}})\n
|
|
293
|
+
vast_settings(query="dph<0.1")\n
|
|
294
|
+
"""
|
|
295
|
+
res = {}
|
|
296
|
+
if id is not None:
|
|
297
|
+
assert query is None, "id and query set"
|
|
298
|
+
res["id"] = id
|
|
299
|
+
elif query is not None:
|
|
300
|
+
if isinstance(query, str):
|
|
301
|
+
res["query"] = parse(query, no_default=True) # run only with this options
|
|
302
|
+
else:
|
|
303
|
+
assert isinstance(query, dict), "wrong query type: it should be str or dict"
|
|
304
|
+
res["query"] = query
|
|
305
|
+
return json.dumps(res)
|
|
306
|
+
|