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.

Files changed (28) hide show
  1. {malevich-coretools-0.2.1/malevich_coretools.egg-info → malevich-coretools-0.2.3}/PKG-INFO +1 -1
  2. malevich-coretools-0.2.3/VERSION +1 -0
  3. {malevich-coretools-0.2.1 → malevich-coretools-0.2.3}/malevich_coretools/__init__.py +1 -0
  4. {malevich-coretools-0.2.1 → malevich-coretools-0.2.3}/malevich_coretools/abstract/abstract.py +3 -0
  5. {malevich-coretools-0.2.1 → malevich-coretools-0.2.3}/malevich_coretools/funcs/funcs.py +7 -0
  6. {malevich-coretools-0.2.1 → malevich-coretools-0.2.3}/malevich_coretools/secondary/const.py +2 -0
  7. malevich-coretools-0.2.3/malevich_coretools/tools/__init__.py +1 -0
  8. malevich-coretools-0.2.3/malevich_coretools/tools/vast.py +306 -0
  9. {malevich-coretools-0.2.1 → malevich-coretools-0.2.3}/malevich_coretools/utils.py +131 -119
  10. {malevich-coretools-0.2.1 → malevich-coretools-0.2.3/malevich_coretools.egg-info}/PKG-INFO +1 -1
  11. {malevich-coretools-0.2.1 → malevich-coretools-0.2.3}/malevich_coretools.egg-info/SOURCES.txt +3 -1
  12. malevich-coretools-0.2.1/VERSION +0 -1
  13. {malevich-coretools-0.2.1 → malevich-coretools-0.2.3}/LICENSE +0 -0
  14. {malevich-coretools-0.2.1 → malevich-coretools-0.2.3}/MANIFEST.in +0 -0
  15. {malevich-coretools-0.2.1 → malevich-coretools-0.2.3}/README.md +0 -0
  16. {malevich-coretools-0.2.1 → malevich-coretools-0.2.3}/malevich_coretools/abstract/__init__.py +0 -0
  17. {malevich-coretools-0.2.1 → malevich-coretools-0.2.3}/malevich_coretools/funcs/__init__.py +0 -0
  18. {malevich-coretools-0.2.1 → malevich-coretools-0.2.3}/malevich_coretools/funcs/helpers.py +0 -0
  19. {malevich-coretools-0.2.1 → malevich-coretools-0.2.3}/malevich_coretools/secondary/__init__.py +0 -0
  20. {malevich-coretools-0.2.1 → malevich-coretools-0.2.3}/malevich_coretools/secondary/config.py +0 -0
  21. {malevich-coretools-0.2.1 → malevich-coretools-0.2.3}/malevich_coretools/secondary/helpers.py +0 -0
  22. {malevich-coretools-0.2.1 → malevich-coretools-0.2.3}/malevich_coretools.egg-info/dependency_links.txt +0 -0
  23. {malevich-coretools-0.2.1 → malevich-coretools-0.2.3}/malevich_coretools.egg-info/requires.txt +0 -0
  24. {malevich-coretools-0.2.1 → malevich-coretools-0.2.3}/malevich_coretools.egg-info/top_level.txt +0 -0
  25. {malevich-coretools-0.2.1 → malevich-coretools-0.2.3}/pyproject.toml +0 -0
  26. {malevich-coretools-0.2.1 → malevich-coretools-0.2.3}/requirements.txt +0 -0
  27. {malevich-coretools-0.2.1 → malevich-coretools-0.2.3}/setup.cfg +0 -0
  28. {malevich-coretools-0.2.1 → malevich-coretools-0.2.3}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: malevich-coretools
3
- Version: 0.2.1
3
+ Version: 0.2.3
4
4
  Author: Andrew Pogrebnoj
5
5
  Author-email: andrew@onjulius.co
6
6
  License-File: LICENSE
@@ -0,0 +1 @@
1
+ 0.2.3
@@ -1 +1,2 @@
1
1
  from .utils import * # noqa: F403
2
+ from .tools import vast_settings
@@ -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
+