kalavai-client 0.5.30__py3-none-any.whl → 0.6.1__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.
- kalavai_client/__init__.py +1 -1
- kalavai_client/assets/apps.yaml +9 -7
- kalavai_client/assets/apps_values.yaml +0 -2
- kalavai_client/assets/docker-compose-gui.yaml +3 -0
- kalavai_client/assets/docker-compose-template.yaml +1 -3
- kalavai_client/auth.py +92 -50
- kalavai_client/bridge_api.py +39 -26
- kalavai_client/cli.py +111 -105
- kalavai_client/core.py +108 -113
- kalavai_client/env.py +9 -4
- kalavai_client/utils.py +66 -133
- {kalavai_client-0.5.30.dist-info → kalavai_client-0.6.1.dist-info}/METADATA +6 -27
- kalavai_client-0.6.1.dist-info/RECORD +25 -0
- {kalavai_client-0.5.30.dist-info → kalavai_client-0.6.1.dist-info}/WHEEL +1 -1
- kalavai_client-0.5.30.dist-info/RECORD +0 -25
- {kalavai_client-0.5.30.dist-info → kalavai_client-0.6.1.dist-info}/LICENSE +0 -0
- {kalavai_client-0.5.30.dist-info → kalavai_client-0.6.1.dist-info}/entry_points.txt +0 -0
kalavai_client/core.py
CHANGED
@@ -13,16 +13,17 @@ import re
|
|
13
13
|
|
14
14
|
from kalavai_client.cluster import CLUSTER
|
15
15
|
from kalavai_client.utils import (
|
16
|
+
DEPLOY_LLM_SIDECARS_KEY,
|
17
|
+
NODE_ROLE_LABEL,
|
16
18
|
check_gpu_drivers,
|
17
19
|
generate_join_token,
|
20
|
+
load_user_id,
|
18
21
|
register_cluster,
|
19
22
|
request_to_server,
|
20
23
|
load_server_info,
|
21
24
|
decode_dict,
|
22
|
-
get_vpn_details,
|
23
25
|
send_pool_invite,
|
24
26
|
unregister_cluster,
|
25
|
-
validate_join_public_seed,
|
26
27
|
generate_compose_config,
|
27
28
|
store_server_info,
|
28
29
|
is_watcher_alive,
|
@@ -45,10 +46,8 @@ from kalavai_client.utils import (
|
|
45
46
|
WATCHER_PORT_KEY,
|
46
47
|
WATCHER_SERVICE_KEY,
|
47
48
|
USER_NODE_LABEL_KEY,
|
48
|
-
ALLOW_UNREGISTERED_USER_KEY
|
49
|
-
|
50
|
-
from kalavai_client.auth import (
|
51
|
-
KalavaiAuthClient
|
49
|
+
ALLOW_UNREGISTERED_USER_KEY,
|
50
|
+
KALAVAI_AUTH
|
52
51
|
)
|
53
52
|
from kalavai_client.env import (
|
54
53
|
USER_COOKIE,
|
@@ -126,7 +125,7 @@ def set_schedulable(schedulable, node_names):
|
|
126
125
|
except Exception as e:
|
127
126
|
return {"error": f"Error when connecting to kalavai service: {str(e)}"}
|
128
127
|
|
129
|
-
def init_user_workspace(force_namespace=None):
|
128
|
+
def init_user_workspace(user_id=None, node_name=None, force_namespace=None):
|
130
129
|
|
131
130
|
# load template config and populate with values
|
132
131
|
sidecar_template_yaml = load_template(
|
@@ -138,6 +137,10 @@ def init_user_workspace(force_namespace=None):
|
|
138
137
|
data = {"config": sidecar_template_yaml}
|
139
138
|
if force_namespace is not None:
|
140
139
|
data["force_namespace"] = force_namespace
|
140
|
+
if user_id is not None:
|
141
|
+
data["user_id"] = user_id
|
142
|
+
if node_name is not None:
|
143
|
+
data["node_name"] = node_name
|
141
144
|
result = request_to_server(
|
142
145
|
method="post",
|
143
146
|
endpoint="/v1/create_user_space",
|
@@ -495,31 +498,18 @@ def fetch_gpus(available=False):
|
|
495
498
|
|
496
499
|
except Exception as e:
|
497
500
|
return {"error": str(e)}
|
498
|
-
|
499
|
-
def load_user_session():
|
500
|
-
auth = KalavaiAuthClient(
|
501
|
-
user_cookie_file=USER_COOKIE
|
502
|
-
)
|
503
|
-
return auth.load_user_session()
|
504
501
|
|
505
|
-
def authenticate_user(
|
506
|
-
|
507
|
-
|
508
|
-
)
|
509
|
-
user = auth.load_user_session()
|
510
|
-
if user is None:
|
511
|
-
user = auth.login(username=username, password=password)
|
502
|
+
def authenticate_user(user_key=None):
|
503
|
+
if user_key is None:
|
504
|
+
KALAVAI_AUTH.save_auth(user_key)
|
512
505
|
|
513
|
-
|
514
|
-
|
515
|
-
|
506
|
+
return KALAVAI_AUTH.load_user_session()
|
507
|
+
|
508
|
+
def load_user_session():
|
509
|
+
return KALAVAI_AUTH.load_user_session()
|
516
510
|
|
517
511
|
def user_logout():
|
518
|
-
|
519
|
-
user_cookie_file=USER_COOKIE
|
520
|
-
)
|
521
|
-
auth.logout()
|
522
|
-
return True
|
512
|
+
return KALAVAI_AUTH.clear_auth()
|
523
513
|
|
524
514
|
def check_token(token, public=False):
|
525
515
|
try:
|
@@ -574,38 +564,16 @@ def attach_to_pool(token, node_name=None):
|
|
574
564
|
auth_key = data[AUTH_KEY]
|
575
565
|
watcher_service = data[WATCHER_SERVICE_KEY]
|
576
566
|
public_location = data[PUBLIC_LOCATION_KEY]
|
577
|
-
vpn = defaultdict(lambda: None)
|
578
567
|
except Exception as e:
|
579
568
|
return {"error": f"Invalid token. {str(e)}"}
|
580
569
|
|
581
|
-
user = defaultdict(lambda: None)
|
582
|
-
if public_location is not None:
|
583
|
-
user = load_user_session()
|
584
|
-
if user is None:
|
585
|
-
return {"error ": "Must be logged in to join public pools"}
|
586
|
-
try:
|
587
|
-
vpn = get_vpn_details(
|
588
|
-
location=public_location,
|
589
|
-
user_cookie=USER_COOKIE)
|
590
|
-
except Exception as e:
|
591
|
-
return {"error": f"Are you authenticated? {str(e)}"}
|
592
|
-
try:
|
593
|
-
validate_join_public_seed(
|
594
|
-
cluster_name=cluster_name,
|
595
|
-
join_key=token,
|
596
|
-
user_cookie=USER_COOKIE
|
597
|
-
)
|
598
|
-
except Exception as e:
|
599
|
-
return {"error": f"Error when joining network: {str(e)}"}
|
600
|
-
|
601
570
|
# local agent join
|
602
571
|
# 1. Generate local cache files
|
603
572
|
# Generate docker compose recipe
|
604
573
|
generate_compose_config(
|
605
574
|
role="",
|
606
|
-
vpn_token=
|
607
|
-
node_name=node_name
|
608
|
-
is_public=public_location is not None)
|
575
|
+
vpn_token=public_location,
|
576
|
+
node_name=node_name)
|
609
577
|
|
610
578
|
store_server_info(
|
611
579
|
server_ip=kalavai_seed_ip,
|
@@ -615,7 +583,7 @@ def attach_to_pool(token, node_name=None):
|
|
615
583
|
node_name=node_name,
|
616
584
|
cluster_name=cluster_name,
|
617
585
|
public_location=public_location,
|
618
|
-
user_api_key=
|
586
|
+
user_api_key=None)
|
619
587
|
|
620
588
|
run_cmd(f"docker compose -f {USER_COMPOSE_FILE} up -d")
|
621
589
|
# ensure we are connected
|
@@ -636,6 +604,44 @@ def get_max_gpus():
|
|
636
604
|
except:
|
637
605
|
return 0
|
638
606
|
|
607
|
+
def generate_worker_package(num_gpus=0, node_name=None, ip_address="0.0.0.0", storage_compatible=True):
|
608
|
+
# get pool data from token
|
609
|
+
token = get_pool_token(mode=TokenType.WORKER)
|
610
|
+
if "error" in token:
|
611
|
+
return {"error": f"[red]Error when getting pool token: {token['error']}"}
|
612
|
+
|
613
|
+
if node_name is None:
|
614
|
+
node_name = f"worker-{uuid.uuid4().hex[:6]}"
|
615
|
+
|
616
|
+
# parse pool data
|
617
|
+
try:
|
618
|
+
data = decode_dict(token["token"])
|
619
|
+
kalavai_seed_ip = data[CLUSTER_IP_KEY]
|
620
|
+
kalavai_token = data[CLUSTER_TOKEN_KEY]
|
621
|
+
public_location = data[PUBLIC_LOCATION_KEY]
|
622
|
+
except Exception as e:
|
623
|
+
return {"error": f"Invalid token. {str(e)}"}
|
624
|
+
|
625
|
+
# join private network if provided
|
626
|
+
node_labels = {
|
627
|
+
STORAGE_CLASS_LABEL: storage_compatible,
|
628
|
+
NODE_ROLE_LABEL: "worker"
|
629
|
+
}
|
630
|
+
# Generate docker compose recipe
|
631
|
+
compose = generate_compose_config(
|
632
|
+
write_to_file=False,
|
633
|
+
role="agent",
|
634
|
+
node_ip_address=ip_address,
|
635
|
+
pool_ip=f"https://{kalavai_seed_ip}:6443",
|
636
|
+
pool_token=kalavai_token,
|
637
|
+
num_gpus=num_gpus,
|
638
|
+
vpn_token=public_location,
|
639
|
+
node_name=node_name,
|
640
|
+
node_labels=node_labels)
|
641
|
+
|
642
|
+
return compose
|
643
|
+
|
644
|
+
|
639
645
|
def join_pool(token, num_gpus=None, node_name=None, ip_address=None):
|
640
646
|
compatibility = check_worker_compatibility()
|
641
647
|
if len(compatibility["issues"]) > 0:
|
@@ -660,35 +666,14 @@ def join_pool(token, num_gpus=None, node_name=None, ip_address=None):
|
|
660
666
|
auth_key = data[AUTH_KEY]
|
661
667
|
watcher_service = data[WATCHER_SERVICE_KEY]
|
662
668
|
public_location = data[PUBLIC_LOCATION_KEY]
|
663
|
-
vpn = defaultdict(lambda: None)
|
664
669
|
except Exception as e:
|
665
670
|
return {"error": f"Invalid token. {str(e)}"}
|
666
671
|
|
667
672
|
# join private network if provided
|
668
673
|
node_labels = {
|
669
|
-
STORAGE_CLASS_LABEL: is_storage_compatible()
|
670
|
-
|
671
|
-
|
672
|
-
if public_location is not None:
|
673
|
-
user = authenticate_user()
|
674
|
-
if user is None:
|
675
|
-
return {"error": "Must be logged in to join public pools"}
|
676
|
-
try:
|
677
|
-
vpn = get_vpn_details(
|
678
|
-
location=public_location,
|
679
|
-
user_cookie=USER_COOKIE)
|
680
|
-
node_labels[USER_NODE_LABEL] = user["username"]
|
681
|
-
except Exception as e:
|
682
|
-
return {"error": f"Are you authenticated? Error: {str(e)}"}
|
683
|
-
try:
|
684
|
-
validate_join_public_seed(
|
685
|
-
cluster_name=cluster_name,
|
686
|
-
join_key=token,
|
687
|
-
user_cookie=USER_COOKIE
|
688
|
-
)
|
689
|
-
except Exception as e:
|
690
|
-
return {"error": f"Error when joining network: {str(e)}"}
|
691
|
-
|
674
|
+
STORAGE_CLASS_LABEL: is_storage_compatible(),
|
675
|
+
NODE_ROLE_LABEL: "worker"
|
676
|
+
}
|
692
677
|
# local agent join
|
693
678
|
# Generate docker compose recipe
|
694
679
|
generate_compose_config(
|
@@ -697,10 +682,9 @@ def join_pool(token, num_gpus=None, node_name=None, ip_address=None):
|
|
697
682
|
pool_ip=f"https://{kalavai_seed_ip}:6443",
|
698
683
|
pool_token=kalavai_token,
|
699
684
|
num_gpus=num_gpus,
|
700
|
-
vpn_token=
|
685
|
+
vpn_token=public_location,
|
701
686
|
node_name=node_name,
|
702
|
-
node_labels=node_labels
|
703
|
-
is_public=public_location is not None)
|
687
|
+
node_labels=node_labels)
|
704
688
|
|
705
689
|
store_server_info(
|
706
690
|
server_ip=kalavai_seed_ip,
|
@@ -710,7 +694,7 @@ def join_pool(token, num_gpus=None, node_name=None, ip_address=None):
|
|
710
694
|
node_name=node_name,
|
711
695
|
cluster_name=cluster_name,
|
712
696
|
public_location=public_location,
|
713
|
-
user_api_key=
|
697
|
+
user_api_key=None)
|
714
698
|
|
715
699
|
try:
|
716
700
|
CLUSTER.start_worker_node()
|
@@ -730,13 +714,25 @@ def join_pool(token, num_gpus=None, node_name=None, ip_address=None):
|
|
730
714
|
except KeyboardInterrupt:
|
731
715
|
return {"error": "Installation aborted. Leaving pool."}
|
732
716
|
|
733
|
-
result = init_user_workspace(
|
717
|
+
result = init_user_workspace(
|
718
|
+
user_id=load_user_id(),
|
719
|
+
node_name=node_name)
|
734
720
|
if "error" in result:
|
735
721
|
return {"error": f"Error when creating user workspace: {result}"}
|
736
722
|
|
737
723
|
return cluster_name
|
738
724
|
|
739
|
-
def create_pool(
|
725
|
+
def create_pool(
|
726
|
+
cluster_name: str,
|
727
|
+
ip_address: str,
|
728
|
+
description: str="",
|
729
|
+
token_mode: TokenType=TokenType.USER,
|
730
|
+
app_values: str=None,
|
731
|
+
pool_config_values: str=None,
|
732
|
+
num_gpus: int=0,
|
733
|
+
node_name: str=None,
|
734
|
+
location: str=None
|
735
|
+
):
|
740
736
|
|
741
737
|
if not check_seed_compatibility():
|
742
738
|
return {"error": "Requirements failed"}
|
@@ -747,28 +743,22 @@ def create_pool(cluster_name: str, ip_address: str, description: str="", token_m
|
|
747
743
|
if pool_config_values is None:
|
748
744
|
pool_config_values = POOL_CONFIG_DEFAULT_VALUES
|
749
745
|
|
750
|
-
node_name = f"{socket.gethostname()}-{uuid.uuid4().hex[:6]}"
|
746
|
+
node_name = f"{socket.gethostname()}-{uuid.uuid4().hex[:6]}"
|
747
|
+
user_id = load_user_id()
|
751
748
|
|
752
|
-
# if only registered users are allowed, check user has logged in
|
753
|
-
user = defaultdict(lambda: None)
|
754
|
-
if only_registered_users or location is not None:
|
755
|
-
user = authenticate_user()
|
756
|
-
if user is None:
|
757
|
-
return {"error": "[white]--only-registered-users [red]or [white]--location[red] can only be used if the host is authenticated. Run [yellow]kalavai login[red] to authenticate"}
|
758
|
-
|
759
|
-
# join private network if provided
|
760
|
-
vpn = defaultdict(lambda: None)
|
761
749
|
node_labels = {
|
762
|
-
STORAGE_CLASS_LABEL: is_storage_compatible()
|
750
|
+
STORAGE_CLASS_LABEL: is_storage_compatible(),
|
751
|
+
NODE_ROLE_LABEL: "server"
|
763
752
|
}
|
764
|
-
|
765
|
-
|
766
|
-
|
767
|
-
|
768
|
-
|
769
|
-
|
770
|
-
|
771
|
-
|
753
|
+
|
754
|
+
# if location is not None:
|
755
|
+
# try:
|
756
|
+
# vpn = get_vpn_details(
|
757
|
+
# location=location,
|
758
|
+
# user_cookie=USER_COOKIE)
|
759
|
+
# node_labels[USER_NODE_LABEL] = user["username"]
|
760
|
+
# except Exception as e:
|
761
|
+
# return {"error": f"[red]Error when joining network: {str(e)}"}
|
772
762
|
|
773
763
|
if num_gpus is None:
|
774
764
|
num_gpus = get_max_gpus()
|
@@ -776,12 +766,11 @@ def create_pool(cluster_name: str, ip_address: str, description: str="", token_m
|
|
776
766
|
# Generate docker compose recipe
|
777
767
|
generate_compose_config(
|
778
768
|
role="server",
|
779
|
-
vpn_token=
|
769
|
+
vpn_token=location,
|
780
770
|
node_ip_address=ip_address,
|
781
771
|
num_gpus=num_gpus,
|
782
772
|
node_name=node_name,
|
783
|
-
node_labels=node_labels
|
784
|
-
is_public=location is not None
|
773
|
+
node_labels=node_labels
|
785
774
|
)
|
786
775
|
|
787
776
|
# start server
|
@@ -796,7 +785,8 @@ def create_pool(cluster_name: str, ip_address: str, description: str="", token_m
|
|
796
785
|
ip_address = CLUSTER.get_vpn_ip()
|
797
786
|
|
798
787
|
# populate local cred files
|
799
|
-
|
788
|
+
|
789
|
+
auth_key = user_id if user_id is not None else str(uuid.uuid4())
|
800
790
|
write_auth_key = str(uuid.uuid4())
|
801
791
|
readonly_auth_key = str(uuid.uuid4())
|
802
792
|
|
@@ -810,7 +800,8 @@ def create_pool(cluster_name: str, ip_address: str, description: str="", token_m
|
|
810
800
|
WATCHER_PORT_KEY: DEFAULT_WATCHER_PORT,
|
811
801
|
WATCHER_SERVICE_KEY: watcher_service,
|
812
802
|
USER_NODE_LABEL_KEY: USER_NODE_LABEL,
|
813
|
-
ALLOW_UNREGISTERED_USER_KEY:
|
803
|
+
ALLOW_UNREGISTERED_USER_KEY: True, # Change this if only registered users are allowed,
|
804
|
+
DEPLOY_LLM_SIDECARS_KEY: location is not None
|
814
805
|
}
|
815
806
|
|
816
807
|
store_server_info(
|
@@ -823,7 +814,7 @@ def create_pool(cluster_name: str, ip_address: str, description: str="", token_m
|
|
823
814
|
node_name=node_name,
|
824
815
|
cluster_name=cluster_name,
|
825
816
|
public_location=location,
|
826
|
-
user_api_key=
|
817
|
+
user_api_key=None)
|
827
818
|
|
828
819
|
# Generate helmfile recipe
|
829
820
|
helm_yaml = load_template(
|
@@ -857,11 +848,14 @@ def create_pool(cluster_name: str, ip_address: str, description: str="", token_m
|
|
857
848
|
if "error" in result or ("failed" in result and len(result['failed']) > 0):
|
858
849
|
return {"error": f"Error when initialising pool: {result}"}
|
859
850
|
# init default namespace
|
860
|
-
init_user_workspace(
|
861
|
-
|
862
|
-
|
863
|
-
|
864
|
-
|
851
|
+
init_user_workspace(
|
852
|
+
user_id=user_id,
|
853
|
+
node_name=node_name,
|
854
|
+
force_namespace="default")
|
855
|
+
# if only_registered_users:
|
856
|
+
# # init user namespace
|
857
|
+
# init_user_workspace(
|
858
|
+
# user_id=user_id)
|
865
859
|
# register cluster (if user is logged in)
|
866
860
|
result = register_pool(
|
867
861
|
cluster_name=cluster_name,
|
@@ -876,6 +870,7 @@ def create_pool(cluster_name: str, ip_address: str, description: str="", token_m
|
|
876
870
|
|
877
871
|
return {"success"}
|
878
872
|
|
873
|
+
|
879
874
|
def get_pool_token(mode: TokenType):
|
880
875
|
|
881
876
|
try:
|
kalavai_client/env.py
CHANGED
@@ -3,10 +3,15 @@ from pathlib import Path
|
|
3
3
|
import importlib.resources
|
4
4
|
|
5
5
|
|
6
|
+
DEFAULT_KALAVAI_PATH = os.getenv("KALAVAI_PATH", None)
|
7
|
+
|
6
8
|
def user_path(relative_path, create_path=False):
|
7
9
|
"""Transform a relative path into the user's cache folder path"""
|
8
|
-
|
9
|
-
|
10
|
+
if DEFAULT_KALAVAI_PATH is not None:
|
11
|
+
kalavai_user_path = DEFAULT_KALAVAI_PATH
|
12
|
+
else:
|
13
|
+
base = os.path.expanduser("~")
|
14
|
+
kalavai_user_path = os.path.join(base, ".cache/kalavai")
|
10
15
|
full_path = os.path.join(kalavai_user_path, relative_path)
|
11
16
|
if create_path:
|
12
17
|
Path(full_path).mkdir(parents=True, exist_ok=True)
|
@@ -34,7 +39,7 @@ KALAVAI_PLATFORM_ENDPOINT = "https://platform.kalavai.net/_/api"
|
|
34
39
|
DEFAULT_CONTAINER_NAME = "kalavai"
|
35
40
|
DEFAULT_VPN_CONTAINER_NAME = "kalavai-vpn"
|
36
41
|
CONTAINER_HOST_PATH = user_path("pool/", create_path=True)
|
37
|
-
DEFAULT_FLANNEL_IFACE = os.getenv("KALAVAI_FLANNEL_IFACE", "netmaker
|
42
|
+
DEFAULT_FLANNEL_IFACE = os.getenv("KALAVAI_FLANNEL_IFACE", "netmaker")
|
38
43
|
DEFAULT_WATCHER_PORT = 30001
|
39
44
|
KUBE_VERSION = os.getenv("KALAVAI_KUBE_VERSION", "v1.31.1+k3s1")
|
40
45
|
FORBIDEDEN_IPS = ["127.0.0.1"]
|
@@ -50,7 +55,7 @@ POOL_CONFIG_DEFAULT_VALUES = resource_path("kalavai_client/assets/pool_config_va
|
|
50
55
|
# user specific config files
|
51
56
|
USER_TEMPLATES_FOLDER = user_path("templates", create_path=True)
|
52
57
|
USER_LOCAL_SERVER_FILE = user_path(".server")
|
53
|
-
USER_COOKIE = user_path(".user_cookie.
|
58
|
+
USER_COOKIE = user_path(".user_cookie.json")
|
54
59
|
USER_COMPOSE_FILE = user_path("docker-compose-worker.yaml")
|
55
60
|
USER_GUI_COMPOSE_FILE = user_path("docker-compose-gui.yaml")
|
56
61
|
USER_HELM_APPS_FILE = user_path("apps.yaml")
|