Skip to content

Commit 9d03779

Browse files
authored
Merge pull request #86 from NimbleBoxAI/v1
New LMAO v4 backend [0.13.0rc4]
2 parents 8f551da + 7d4957f commit 9d03779

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

59 files changed

+6133
-1540
lines changed

.gitignore

Lines changed: 1 addition & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -140,27 +140,10 @@ cython_debug/
140140

141141
# our ignores
142142
.DS_Store
143-
*.cfg
144143
notebooks/
145-
__ignore/
146144
.vscode/
147-
serve_fp.py
148-
*.pt
149-
*.onnx
150-
*.pb
151-
152-
# you have recipes, and then you have dark magic!
153-
__dark_magic/
154-
__secret/
155145

156146
# not need to add / in symlink
157147
.nbx
158-
nbox-kernel
159-
yolov4-trainer-nbox
160-
example_jobs/gperc_model/
161148
ex_jobs
162-
hyperloop
163-
scripts
164-
konark/
165-
_modules/
166-
nbox/_jobs
149+
scripts

LICENSE

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,7 @@ Copyright 2018- The NimbleBox.ai team. All rights reserved.
188188
same "printed page" as the copyright notice for easier
189189
identification within third-party archives.
190190

191-
Copyright 2021-22 NimbleBox.ai
191+
Copyright 2021-23 NimbleBox.ai
192192

193193
Licensed under the Apache License, Version 2.0 (the "License");
194194
you may not use this file except in compliance with the License.

README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,12 @@ pip install nbox
1919
pip install nbox
2020
```
2121

22+
# Stability and Compatibility
23+
24+
Status: The library is currently undergoing heavy development.
25+
26+
☝️ Important Note: Current major version is zero (v0.x.x) to accommodate rapid development and fast iteration while getting early feedback from users (feedback on APIs are appreciated!). **The public API need not change** without a major version update before v1.0.0 release.
27+
2228
# 🤷Why NimbleBox
2329

2430
- Write and execute code in Python

nbox/assets/exe.jinja

Lines changed: 30 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,34 @@ os.environ["PYTHONUNBUFFERED"] = "true" # so print comes when it should come
1010
import fire
1111
import inspect
1212
from functools import lru_cache, partial
13-
from {{ file_name }} import {{ fn_name }}
14-
15-
from nbox import Operator, logger
16-
import nbox.utils as U
17-
from nbox.lib.dist import NBXLet
1813

1914
@lru_cache(1)
20-
def get_op(cloud = False) -> Operator:
15+
def get_op(cloud = False):
2116
# The beauty of this function is that it ensures that the operator class is loaded only once
17+
18+
# first try except is to catch any issues during the import itself
2219
try:
20+
# import user code, add this to the try/except because if the code does not exit and there
21+
# is an infinite loop, there can be a whole bunch of side effects, ex: 100s of LMAO live trackers
22+
from {{ file_name }} import {{ fn_name }}
23+
24+
# we still want to ensure that user initialises `nbox` first so their environment variables are
25+
# always going to hold true
26+
import nbox.utils as U
27+
from nbox import Operator, logger
28+
29+
except Exception as e:
30+
# again import the nbox things so that we can log the traceback and exit the program
31+
import nbox.utils as U
32+
from nbox import Operator, logger
33+
34+
U.log_traceback()
35+
logger.error(f"Failed to load operator: {e}")
36+
U.hard_exit_program(1)
37+
38+
# second is to initialise it as an Operator
39+
try:
40+
# load the operator
2341
obj = {{ init_code }}
2442
if not type(obj) == Operator and {{ load_operator }}:
2543
# there is an initial level of precaution that we use during deployment, but we are adding simple
@@ -31,7 +49,6 @@ def get_op(cloud = False) -> Operator:
3149
obj = Operator.from_fn(obj)
3250
else:
3351
raise ValueError("{{ fn_name }} is not an Operator or class or function")
34-
3552
# at this point there is a guarantee that obj is an Operator
3653
op: Operator = obj
3754
except Exception as e:
@@ -48,10 +65,14 @@ def get_op(cloud = False) -> Operator:
4865
U.log_traceback()
4966
logger.error(f"Failed to remote initialise operator: {e}")
5067
U.hard_exit_program(1)
51-
return op
68+
69+
# now import the class as well and return a builder function
70+
from nbox.lib.dist import NBXLet
71+
return lambda : NBXLet(op = op)
5272

5373
if __name__ == "__main__":
54-
nbxlet = NBXLet(op = get_op(cloud = True))
74+
builder = get_op(cloud = True)
75+
nbxlet = builder()
5576
fire.Fire({
5677
"run": nbxlet.run, # NBX-Jobs
5778
"serve": partial(

nbox/auth.py

Lines changed: 73 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,13 @@
1111
"""
1212
import os
1313
import json
14+
import socket
1415
import requests
1516
import webbrowser
1617
from typing import Dict
1718
from getpass import getpass
1819
from functools import lru_cache
20+
from dataclasses import dataclass
1921

2022
import nbox.utils as U
2123
from nbox.utils import join, logger, lo
@@ -46,7 +48,22 @@ class JobDetails(object):
4648
job_id: str
4749
run_id: str
4850

49-
# class DeployDetails(object):
51+
class DeployDetails(object):
52+
deployment_id: str
53+
model_id: str
54+
55+
56+
@dataclass
57+
class AgentDetails():
58+
group_id: str
59+
instance_id: str
60+
nbx_type: str
61+
62+
63+
NBX_JOB_TYPE = "job"
64+
NBX_DEPLOY_TYPE = "deploy"
65+
NBX_LOCAL_TYPE = "local"
66+
5067

5168
class NBXClient:
5269
def __init__(self, nbx_url = "https://app.nimblebox.ai"):
@@ -175,30 +192,62 @@ def __call__(self, item, default=None, reload: bool = False):
175192

176193
@property
177194
def workspace_id(self) -> str:
178-
return self.get(AuthConfig.workspace_id) or self.get(AuthConfig._workspace_id)
195+
return self.get(AuthConfig.workspace_id, "") or self.get(AuthConfig._workspace_id, "")
196+
197+
@property
198+
def workspace_name(self) -> str:
199+
return self(AuthConfig.workspace_name, "")
179200

180201
@property
181202
def nbx_url(self) -> str:
182-
return self.get(AuthConfig.url)
203+
return self.get(AuthConfig.url, "")
183204

184205
@property
185206
def access_token(self) -> str:
186-
return self.get(AuthConfig.access_token)
207+
return self.get(AuthConfig.access_token, "")
187208

188209
@property
189210
def username(self) -> str:
190-
return self.get(AuthConfig.username)
211+
return self.get(AuthConfig.username, "")
212+
213+
@property
214+
def run_details(self):
215+
return secret(AuthConfig.nbx_pod_run, {})
191216

192-
def get_agent_details(self) -> Dict[str, str]:
193-
if ConfigString.nbx_pod_run in self.secrets:
217+
@property
218+
def inside_pod(self) -> bool:
219+
return self.inside_job_pod or self.inside_deploy_pod
220+
221+
@property
222+
def inside_job_pod(self) -> bool:
223+
return ConfigString.nbx_pod_run in self.secrets
224+
225+
@property
226+
def inside_deploy_pod(self) -> bool:
227+
return ConfigString.nbx_pod_deploy in self.secrets
228+
229+
def get_agent_details(self) -> AgentDetails:
230+
if self.inside_job_pod:
194231
run_data = self.secrets[ConfigString.nbx_pod_run]
195-
jd = JobDetails()
196-
jd.job_id = run_data.get("job_id", None)
197-
jd.run_id = run_data.get("token", None)
198-
return jd
199-
# elif ConfigString.nbx_pod_deploy in self.secrets:
200-
# return self.secrets[ConfigString.nbx_pod_deploy]
201-
return {}
232+
out = AgentDetails(
233+
group_id = run_data.get("job_id", None),
234+
instance_id = run_data.get("token", None),
235+
nbx_type = NBX_JOB_TYPE
236+
)
237+
elif self.inside_deploy_pod:
238+
deploy_data = self.secrets[ConfigString.nbx_pod_deploy]
239+
out = AgentDetails(
240+
group_id = deploy_data.get("deployment_id", None),
241+
instance_id = deploy_data.get("model_id", None),
242+
nbx_type = NBX_DEPLOY_TYPE
243+
)
244+
else:
245+
out = AgentDetails(
246+
group_id = f"local-{socket.gethostname()}",
247+
instance_id = f"{socket.gethostbyname(socket.gethostname())}-{os.getpid()}",
248+
nbx_type = NBX_LOCAL_TYPE
249+
)
250+
return out
202251

203252

204253
def init_secret():
@@ -211,10 +260,13 @@ def init_secret():
211260
logger.info(lo(
212261
f"workspace details",
213262
workspace_id = secret.workspace_id,
214-
workspace_name = AuthConfig.workspace_name,
263+
workspace_name = secret.workspace_name,
215264
token_present = len(secret.access_token) > 0,
216265
nbx_url = secret.nbx_url,
217266
))
267+
268+
if not secret.workspace_id:
269+
raise Exception("Workspace ID not found. Please run `nbox login` to login to NimbleBox.")
218270
return secret
219271
else:
220272
logger.info(f"Skipping authentication as NBOX_NO_AUTH is set to True")
@@ -231,10 +283,12 @@ def auth_info_pb():
231283
from nbox.hyperloop.common.common_pb2 import NBXAuthInfo
232284

233285
return NBXAuthInfo(
234-
username = secret(AuthConfig.username),
235-
workspace_id = secret(AuthConfig.workspace_id) or secret(AuthConfig._workspace_id),
236-
access_token = secret(AuthConfig.access_token),
286+
username = secret.username,
287+
workspace_id = secret.workspace_id,
288+
access_token = secret.access_token,
237289
)
238290

239291
def inside_pod():
240-
return secret(AuthConfig.nbx_pod_run, False) or secret(AuthConfig.nbx_pod_deploy, False)
292+
if secret is None:
293+
raise Exception("Secrets not initialized. Cannot determine where am I.")
294+
return secret.inside_pod

nbox/cli.py

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -21,19 +21,21 @@
2121
from typing import Dict, Any
2222

2323
import nbox.utils as U
24-
from nbox.jobs import Job, Serve
2524
from nbox.init import nbox_ws_v1
2625
from nbox.auth import init_secret, AuthConfig, secret
2726
from nbox.instance import Instance
2827
from nbox.sub_utils.ssh import tunnel
2928
from nbox.relics import Relics
3029
from nbox.lmao import LmaoCLI
30+
from nbox.lmao_v4 import LmaoCLI as Lmaov4CLI
3131
from nbox.version import __version__ as V
32-
from nbox.nbxlib.fire import NBXFire
3332
from nbox.projects import Project
3433
from nbox.utils import logger, lo
3534
from nbox.plugins.base import PluginCLI
3635

36+
# from nbox.jobs import Job, Serve
37+
from nbox.jd_core import JobsCli, ServeCli
38+
3739
class Config(object):
3840
def update(self, workspace_id: str):
3941
"""Set global config for `nbox`"""
@@ -42,7 +44,7 @@ def update(self, workspace_id: str):
4244
redo = not data or (workspace_id not in data)
4345
if redo:
4446
workspaces = requests.get(
45-
secret(AuthConfig.url) + f"/api/v1/workspace",
47+
secret.nbx_url + f"/api/v1/workspace",
4648
headers = nbox_ws_v1._session.headers
4749
).json()["data"]
4850
# workspaces = nbox_ws_v1.workspace()
@@ -71,11 +73,11 @@ def show(self):
7173
"""Pretty print global config for `nbox`"""
7274
logger.info(lo(
7375
"nbox config:",
74-
workspace_name = secret(AuthConfig.workspace_name),
75-
workspace_id = secret(AuthConfig.workspace_id),
76-
username = secret(AuthConfig.username),
76+
workspace_name = secret.workspace_id,
77+
workspace_id = secret.workspace_id,
78+
username = secret.username,
7779
nbox = V,
78-
URL = secret(AuthConfig.url),
80+
URL = secret.nbx_url,
7981
))
8082

8183
def clear(self):
@@ -86,7 +88,7 @@ def clear(self):
8688

8789
def open_home():
8890
"""Open current NBX platform"""
89-
webbrowser.open(secret(AuthConfig.url))
91+
webbrowser.open(secret.nbx_url)
9092

9193

9294
def get(api_end: str, no_pp: bool = False, **kwargs):
@@ -179,22 +181,24 @@ def main():
179181
"build" : Instance,
180182
"config" : Config,
181183
"get" : get,
182-
"jobs" : Job,
183-
"lmao" : LmaoCLI,
184+
# "jobs" : Job,
185+
"jobs" : JobsCli,
186+
# "lmao" : LmaoCLI,
187+
"lmao" : Lmaov4CLI,
184188
"login" : login,
185189
"open" : open_home,
186190
"plugins" : PluginCLI,
187191
"projects" : Project,
188192
"relics" : Relics,
189-
"serve" : Serve,
193+
# "serve" : Serve,
194+
"serve" : ServeCli,
190195
"tunnel" : tunnel,
191196
"version" : version,
192197
"why" : why,
193198
"ws" : NBXWS_CLI,
194199
}
195200

196201
fire.Fire(component)
197-
# NBXFire(component)
198202

199203
if __name__ == "__main__":
200204
main()

0 commit comments

Comments
 (0)