kubetorch 0.2.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.
Files changed (92) hide show
  1. kubetorch/__init__.py +59 -0
  2. kubetorch/cli.py +1939 -0
  3. kubetorch/cli_utils.py +967 -0
  4. kubetorch/config.py +453 -0
  5. kubetorch/constants.py +18 -0
  6. kubetorch/docs/Makefile +18 -0
  7. kubetorch/docs/__init__.py +0 -0
  8. kubetorch/docs/_ext/json_globaltoc.py +42 -0
  9. kubetorch/docs/api/cli.rst +10 -0
  10. kubetorch/docs/api/python/app.rst +21 -0
  11. kubetorch/docs/api/python/cls.rst +19 -0
  12. kubetorch/docs/api/python/compute.rst +25 -0
  13. kubetorch/docs/api/python/config.rst +11 -0
  14. kubetorch/docs/api/python/fn.rst +19 -0
  15. kubetorch/docs/api/python/image.rst +14 -0
  16. kubetorch/docs/api/python/secret.rst +18 -0
  17. kubetorch/docs/api/python/volumes.rst +13 -0
  18. kubetorch/docs/api/python.rst +101 -0
  19. kubetorch/docs/conf.py +69 -0
  20. kubetorch/docs/index.rst +20 -0
  21. kubetorch/docs/requirements.txt +5 -0
  22. kubetorch/globals.py +269 -0
  23. kubetorch/logger.py +59 -0
  24. kubetorch/resources/__init__.py +0 -0
  25. kubetorch/resources/callables/__init__.py +0 -0
  26. kubetorch/resources/callables/cls/__init__.py +0 -0
  27. kubetorch/resources/callables/cls/cls.py +159 -0
  28. kubetorch/resources/callables/fn/__init__.py +0 -0
  29. kubetorch/resources/callables/fn/fn.py +140 -0
  30. kubetorch/resources/callables/module.py +1315 -0
  31. kubetorch/resources/callables/utils.py +203 -0
  32. kubetorch/resources/compute/__init__.py +0 -0
  33. kubetorch/resources/compute/app.py +253 -0
  34. kubetorch/resources/compute/compute.py +2414 -0
  35. kubetorch/resources/compute/decorators.py +137 -0
  36. kubetorch/resources/compute/utils.py +1026 -0
  37. kubetorch/resources/compute/websocket.py +135 -0
  38. kubetorch/resources/images/__init__.py +1 -0
  39. kubetorch/resources/images/image.py +412 -0
  40. kubetorch/resources/images/images.py +64 -0
  41. kubetorch/resources/secrets/__init__.py +2 -0
  42. kubetorch/resources/secrets/kubernetes_secrets_client.py +377 -0
  43. kubetorch/resources/secrets/provider_secrets/__init__.py +0 -0
  44. kubetorch/resources/secrets/provider_secrets/anthropic_secret.py +12 -0
  45. kubetorch/resources/secrets/provider_secrets/aws_secret.py +16 -0
  46. kubetorch/resources/secrets/provider_secrets/azure_secret.py +14 -0
  47. kubetorch/resources/secrets/provider_secrets/cohere_secret.py +12 -0
  48. kubetorch/resources/secrets/provider_secrets/gcp_secret.py +16 -0
  49. kubetorch/resources/secrets/provider_secrets/github_secret.py +13 -0
  50. kubetorch/resources/secrets/provider_secrets/huggingface_secret.py +20 -0
  51. kubetorch/resources/secrets/provider_secrets/kubeconfig_secret.py +12 -0
  52. kubetorch/resources/secrets/provider_secrets/lambda_secret.py +13 -0
  53. kubetorch/resources/secrets/provider_secrets/langchain_secret.py +12 -0
  54. kubetorch/resources/secrets/provider_secrets/openai_secret.py +11 -0
  55. kubetorch/resources/secrets/provider_secrets/pinecone_secret.py +12 -0
  56. kubetorch/resources/secrets/provider_secrets/providers.py +92 -0
  57. kubetorch/resources/secrets/provider_secrets/ssh_secret.py +12 -0
  58. kubetorch/resources/secrets/provider_secrets/wandb_secret.py +11 -0
  59. kubetorch/resources/secrets/secret.py +224 -0
  60. kubetorch/resources/secrets/secret_factory.py +64 -0
  61. kubetorch/resources/secrets/utils.py +222 -0
  62. kubetorch/resources/volumes/__init__.py +0 -0
  63. kubetorch/resources/volumes/volume.py +340 -0
  64. kubetorch/servers/__init__.py +0 -0
  65. kubetorch/servers/http/__init__.py +0 -0
  66. kubetorch/servers/http/distributed_utils.py +2968 -0
  67. kubetorch/servers/http/http_client.py +802 -0
  68. kubetorch/servers/http/http_server.py +1622 -0
  69. kubetorch/servers/http/server_metrics.py +255 -0
  70. kubetorch/servers/http/utils.py +722 -0
  71. kubetorch/serving/__init__.py +0 -0
  72. kubetorch/serving/autoscaling.py +153 -0
  73. kubetorch/serving/base_service_manager.py +344 -0
  74. kubetorch/serving/constants.py +77 -0
  75. kubetorch/serving/deployment_service_manager.py +431 -0
  76. kubetorch/serving/knative_service_manager.py +487 -0
  77. kubetorch/serving/raycluster_service_manager.py +526 -0
  78. kubetorch/serving/service_manager.py +18 -0
  79. kubetorch/serving/templates/deployment_template.yaml +17 -0
  80. kubetorch/serving/templates/knative_service_template.yaml +19 -0
  81. kubetorch/serving/templates/kt_setup_template.sh.j2 +91 -0
  82. kubetorch/serving/templates/pod_template.yaml +198 -0
  83. kubetorch/serving/templates/raycluster_service_template.yaml +42 -0
  84. kubetorch/serving/templates/raycluster_template.yaml +35 -0
  85. kubetorch/serving/templates/service_template.yaml +21 -0
  86. kubetorch/serving/templates/workerset_template.yaml +36 -0
  87. kubetorch/serving/utils.py +344 -0
  88. kubetorch/utils.py +263 -0
  89. kubetorch-0.2.5.dist-info/METADATA +75 -0
  90. kubetorch-0.2.5.dist-info/RECORD +92 -0
  91. kubetorch-0.2.5.dist-info/WHEEL +4 -0
  92. kubetorch-0.2.5.dist-info/entry_points.txt +5 -0
@@ -0,0 +1,377 @@
1
+ import base64
2
+ import json
3
+ import os
4
+ from pathlib import Path
5
+ from typing import List, Optional, Tuple, Union
6
+
7
+ from kubernetes import client, config
8
+ from kubernetes.client import V1SecretList
9
+ from kubernetes.client.rest import ApiException
10
+
11
+ import kubetorch
12
+ from kubetorch import globals
13
+ from kubetorch.constants import DEFAULT_KUBECONFIG_PATH
14
+ from kubetorch.logger import get_logger
15
+ from kubetorch.resources.secrets import Secret
16
+ from kubetorch.resources.secrets.utils import get_k8s_identity_name
17
+ from kubetorch.servers.http.utils import is_running_in_kubernetes
18
+ from kubetorch.serving.constants import KT_USER_IDENTIFIER_LABEL, KT_USERNAME_LABEL
19
+
20
+ logger = get_logger(__name__)
21
+
22
+
23
+ class KubernetesSecretsClient:
24
+ def __init__(self, namespace: str = None, kubeconfig_path: str = None):
25
+ self._kubeconfig_path = kubeconfig_path
26
+ self.namespace = namespace or globals.config.namespace
27
+
28
+ # Load config
29
+ self.kt_config = globals.config
30
+
31
+ # Derive User ID from config context
32
+ self.user_id = get_k8s_identity_name()
33
+
34
+ try:
35
+ config.load_incluster_config()
36
+ except config.ConfigException:
37
+ config.load_kube_config(config_file=self.kubeconfig_path)
38
+ self.api_client = client.CoreV1Api()
39
+
40
+ @property
41
+ def kubeconfig_path(self):
42
+ if not self._kubeconfig_path:
43
+ self._kubeconfig_path = os.getenv("KUBECONFIG") or DEFAULT_KUBECONFIG_PATH
44
+ return str(Path(self._kubeconfig_path).expanduser())
45
+
46
+ # -------------------------------------
47
+ # SECRETS APIS
48
+ # -------------------------------------
49
+ def load_secret(self, name: str) -> Optional[Secret]:
50
+ secret_dict = self._read_secret(name)
51
+ if not secret_dict:
52
+ return None
53
+ secret = Secret.from_config(secret_dict)
54
+ return secret
55
+
56
+ def delete_secret(self, name: str, console: "Console" = None) -> bool:
57
+ """Delete secret with provided name for current user."""
58
+ name = name if self._read_secret(name=name) else self._format_secret_name(name)
59
+ return self._delete_secret(name=name, console=console)
60
+
61
+ def delete_all_secrets(self, username: Optional[str] = None) -> bool:
62
+ """Delete all secrets for current user."""
63
+ return self._delete_all_secrets_for_user(username=username)
64
+
65
+ def convert_to_secret_objects(self, secrets: List[Union[str, Secret]]) -> List[Secret]:
66
+ """
67
+ Converts a list of strings and Secrets into Secret objects without uploading.
68
+ """
69
+ from kubetorch.resources.secrets.secret_factory import secret as secret_factory
70
+
71
+ secret_objects = []
72
+ for secret_or_string in secrets:
73
+ # Create a provider secret if only the name is provided
74
+ secret = (
75
+ secret_factory(provider=secret_or_string) if isinstance(secret_or_string, str) else secret_or_string
76
+ )
77
+ secret_objects.append(secret)
78
+
79
+ return secret_objects
80
+
81
+ def upload_secrets_list(self, secrets: List[Union[str, Secret]]) -> List[Secret]:
82
+ """Uploads secrets to Kubernetes Secrets to be used in knative yaml."""
83
+ if is_running_in_kubernetes():
84
+ return []
85
+
86
+ # Convert to Secret objects first
87
+ secret_objects = self.convert_to_secret_objects(secrets)
88
+
89
+ synced_secrets = []
90
+ for secret in secret_objects:
91
+ success = self.create_or_update_secret(secret=secret)
92
+ if success:
93
+ synced_secrets.append(secret)
94
+
95
+ return synced_secrets
96
+
97
+ def extract_envs_and_volumes_from_secrets(
98
+ self,
99
+ secrets: List[Secret] = None,
100
+ ) -> Tuple[list, list]:
101
+ if not secrets:
102
+ return [], []
103
+
104
+ env_vars = []
105
+ volumes = []
106
+ for secret in secrets:
107
+ secret_name = self._format_secret_name(secret.name)
108
+ if secret.env_vars:
109
+ env_vars.append(
110
+ {
111
+ "env_vars": secret.env_vars,
112
+ "secret_name": secret_name,
113
+ }
114
+ )
115
+ if secret.path:
116
+ path = secret.path.replace("~", "/root")
117
+ if not secret.filenames:
118
+ # Reformat path to only include directory
119
+ path = os.path.dirname(path)
120
+ volumes.append(
121
+ {
122
+ "name": f"secrets-{secret.name}",
123
+ "secret_name": secret_name,
124
+ "path": path,
125
+ }
126
+ )
127
+ return env_vars, volumes
128
+
129
+ def _format_secret_name(self, name: str) -> str:
130
+ """Appends user ID to name to ensure uniqueness."""
131
+ user = self.kt_config.username or "global"
132
+ if user in name:
133
+ return name
134
+ return f"kt.secret.{user}.{name}"
135
+
136
+ def _read_secret(self, name: str) -> Optional[dict]:
137
+ secret_name = name
138
+ try:
139
+ secret = self.api_client.read_namespaced_secret(name, self.namespace)
140
+ except ApiException as e:
141
+ if e.status == 404: # secret does not exist, try to load with formatted k8 name
142
+ secret_name = self._format_secret_name(name)
143
+ try:
144
+ secret = self.api_client.read_namespaced_secret(secret_name, self.namespace)
145
+ except ApiException as e:
146
+ if e.status == 404:
147
+ logger.info(
148
+ f"Secret {secret_name} not found in namespace {self.namespace}",
149
+ )
150
+ return None
151
+ else:
152
+ logger.error(
153
+ f"Failed to read secret {name} from Kubernetes: {str(e)}",
154
+ )
155
+ return None
156
+ else:
157
+ logger.error(
158
+ f"Failed to read secret {name} from Kubernetes: {str(e)}",
159
+ )
160
+ return None
161
+ except Exception as e:
162
+ logger.error(
163
+ f"Unexpected error occurred while reading secret {secret_name} from Kubernetes: {str(e)}",
164
+ )
165
+ return None
166
+
167
+ override = (
168
+ secret.metadata.annotations.get("kubetorch.com/override", "False") if secret.metadata.annotations else None
169
+ )
170
+ path = (
171
+ secret.metadata.annotations.get("kubetorch.com/secret-path", None) if secret.metadata.annotations else None
172
+ )
173
+ filenames = (
174
+ secret.metadata.annotations.get("kubetorch.com/secret-filenames", None)
175
+ if secret.metadata.annotations
176
+ else None
177
+ )
178
+ filenames = json.loads(filenames) if filenames else filenames
179
+
180
+ secret_config = {
181
+ "name": secret_name,
182
+ "namespace": self.namespace,
183
+ "override": override,
184
+ }
185
+
186
+ if path:
187
+ secret_config["path"] = path
188
+ secret_config["filenames"] = filenames
189
+ return secret_config
190
+
191
+ decoded_values = {k: base64.b64decode(v).decode("utf-8") for k, v in secret.data.items()}
192
+ secret_config["values"] = decoded_values
193
+
194
+ mount_type = secret.metadata.labels.get("kubetorch.com/mount-type", None)
195
+ if mount_type == "env":
196
+ secret_config["env_vars"] = list(decoded_values.keys())
197
+
198
+ return secret_config
199
+
200
+ def _build_secret_body(self, secret: Secret):
201
+ secret_name = self._format_secret_name(secret.name)
202
+ provider = secret.provider
203
+ mount_type = "volume" if secret.path else "env"
204
+ encoded_data = {k: base64.b64encode(v.encode()).decode() for k, v in secret.values.items()}
205
+ labels = {
206
+ "kubetorch.com/mount-type": mount_type,
207
+ "kubetorch.com/secret-name": secret.name,
208
+ }
209
+ if self.user_id:
210
+ labels[KT_USER_IDENTIFIER_LABEL] = self.user_id
211
+ if self.kt_config.username:
212
+ labels[KT_USERNAME_LABEL] = self.kt_config.username
213
+ if provider:
214
+ labels["kubetorch.com/provider"] = provider
215
+ annotations = {"kubetorch.com/override": str(secret.override)}
216
+ if secret.path:
217
+ annotations["kubetorch.com/secret-path"] = secret.path
218
+ annotations["kubetorch.com/secret-filenames"] = json.dumps(secret.filenames)
219
+
220
+ metadata = client.V1ObjectMeta(
221
+ name=secret_name,
222
+ namespace=self.namespace,
223
+ labels=labels,
224
+ annotations=annotations,
225
+ )
226
+ secret_body = client.V1Secret(metadata=metadata, data=encoded_data, type="Opaque") # default secret type
227
+
228
+ return secret_body
229
+
230
+ def create_secret(self, secret: Secret, console: "Console" = None):
231
+ secret_name = self._format_secret_name(secret.name)
232
+ secret_body = self._build_secret_body(secret=secret)
233
+
234
+ try:
235
+ self.api_client.create_namespaced_secret(self.namespace, secret_body)
236
+ if console:
237
+ console.print("[bold green]✔ Secret created successfully[/bold green]")
238
+ console.print(f" Name: [cyan]{secret.name}[/cyan]")
239
+ console.print(f" Namespace: [cyan]{self.namespace}[/cyan]")
240
+ else:
241
+ logger.info(f"Created new Kubernetes secret {secret.name}")
242
+ return True
243
+
244
+ except ApiException as e:
245
+ if console:
246
+ if e.status == 409:
247
+ msg = f"[yellow]Secret '{secret.name}' already exists in namespace {self.namespace}, skipping creation[/yellow]"
248
+ else:
249
+ msg = f"[red]Failed to create Kubernetes secret {secret_name}: {str(e)}[/red]"
250
+ console.print(msg)
251
+ return False
252
+ else:
253
+ raise e
254
+
255
+ except Exception as e:
256
+ if console:
257
+ msg = f"Unexpected error occurred while creating new Kubernetes secret {secret.name}: {str(e)}"
258
+ console.print(f"[red]{msg}[/red]")
259
+ return False
260
+ else:
261
+ raise e
262
+
263
+ def _get_existing_secret(self, secret: Secret):
264
+ try:
265
+ return self.api_client.read_namespaced_secret(secret.name, self.namespace)
266
+ except ApiException as e:
267
+ if e.status == 404: # try to load secret with kubernetes formatted name
268
+ formatted_name = self._format_secret_name(secret.name)
269
+ try:
270
+ return self.api_client.read_namespaced_secret(formatted_name, self.namespace)
271
+ except ApiException:
272
+ return None
273
+
274
+ def update_secret(self, secret: Secret, console: "Console" = None):
275
+ existing_secret = self._get_existing_secret(secret)
276
+ if existing_secret is None:
277
+ if console:
278
+ console.print(f"[red]Failed to update secret {secret.name}: secret does not exist[/red]")
279
+ return False
280
+ else:
281
+ raise kubetorch.SecretNotFound(secret_name=secret.name, namespace=self.namespace)
282
+
283
+ if not secret.override:
284
+ decoded_values = {k: base64.b64decode(v).decode("utf-8") for k, v in existing_secret.data.items()}
285
+ if not decoded_values == secret.values:
286
+ msg = f"Secret {secret.name} exists with different values and `secret.override` not set to True."
287
+ if console:
288
+ console.print(f"[yellow]{msg}[/yellow]")
289
+ return False
290
+ else:
291
+ raise ValueError(msg)
292
+ else:
293
+ msg = f"Secret {secret.name} already exists with the same values."
294
+ console.print(f"[bold green]{msg}[/bold green]") if console else logger.info(msg)
295
+ return True
296
+
297
+ secret_name = self._format_secret_name(secret.name)
298
+ secret_body = self._build_secret_body(secret=secret)
299
+
300
+ try:
301
+ self.api_client.replace_namespaced_secret(secret_name, self.namespace, secret_body)
302
+ if console:
303
+ console.print("[bold green]✔ Secret updated successfully[/bold green]")
304
+ console.print(f" Name: [cyan]{secret.name}[/cyan]")
305
+ console.print(f" Namespace: [cyan]{self.namespace}[/cyan]")
306
+ else:
307
+ logger.info(f"Updated existing Kubernetes secret {secret_name}")
308
+ return True
309
+
310
+ except Exception as e:
311
+ if console:
312
+ console.print(f"[red]Failed to update secret {secret.name}: {str(e)}[/red]")
313
+ return False
314
+ raise e
315
+
316
+ def create_or_update_secret(self, secret: Secret, console: "Console" = None):
317
+ try:
318
+ return self.update_secret(secret, console)
319
+ except kubetorch.SecretNotFound:
320
+ # if secret not found, try to create the secret.
321
+ return self.create_secret(secret, console)
322
+
323
+ def _delete_secret(self, name: str, console: "Console" = None):
324
+ name = self._format_secret_name(name)
325
+ try:
326
+ self.api_client.delete_namespaced_secret(name, self.namespace)
327
+ if console:
328
+ console.print(f"✓ Deleted secret [blue]{name}[/blue]")
329
+ else:
330
+ logger.info(f"Deleted Kubernetes secret: {name}")
331
+ return True
332
+
333
+ except ApiException as e:
334
+ msg = f"Failed to delete Kubernetes secret: {name}: {str(e)}"
335
+ console.print(msg) if console else logger.error(msg)
336
+ return False
337
+
338
+ except Exception as e:
339
+ msg = f"Unexpected error occurred while deleting Kubernetes secret {name}: {str(e)}"
340
+ console.print(msg) if console else logger.error(msg)
341
+ return False
342
+
343
+ def _delete_all_secrets_for_user(self, username: Optional[str] = None):
344
+ username = username or self.kt_config.username
345
+ label_selector = f"{KT_USERNAME_LABEL}={username}"
346
+ try:
347
+ secrets: V1SecretList = self.api_client.list_namespaced_secret(
348
+ namespace=self.namespace, label_selector=label_selector
349
+ )
350
+ deleted_all = True
351
+
352
+ for secret in secrets.items:
353
+ secret_name = secret.metadata.name
354
+ try:
355
+ self.api_client.delete_namespaced_secret(secret_name, self.namespace)
356
+ logger.info(
357
+ f"Deleted Kubernetes secret {secret_name} for user {username}",
358
+ )
359
+
360
+ except ApiException as e:
361
+ logger.warning(
362
+ f"Failed to delete specific Kubernetes secret {secret_name} for user {username}: {str(e)}",
363
+ )
364
+ deleted_all = False
365
+
366
+ return deleted_all
367
+
368
+ except ApiException as e:
369
+ logger.error(
370
+ f"Failed to list Kubernetes secrets: {str(e)}",
371
+ )
372
+ return False
373
+
374
+ except Exception as e:
375
+ logger.error(
376
+ f"Unexpected error occurred while listing Kubernetes secrets: {str(e)}",
377
+ )
@@ -0,0 +1,12 @@
1
+ from .. import Secret
2
+
3
+
4
+ class AnthropicSecret(Secret):
5
+ """
6
+ .. note::
7
+ To create an AnthropicSecret, please use the factory method :func:`secret`
8
+ with ``provider="anthropic"``.
9
+ """
10
+
11
+ _PROVIDER = "anthropic"
12
+ _DEFAULT_ENV_VARS = {"api_key": "ANTHROPIC_API_KEY"}
@@ -0,0 +1,16 @@
1
+ from .. import Secret
2
+
3
+
4
+ class AWSSecret(Secret):
5
+ """
6
+ .. note::
7
+ To create an AWSSecret, please use the factory method :func:`secret` with ``provider="aws"``.
8
+ """
9
+
10
+ _PROVIDER = "aws"
11
+ _DEFAULT_PATH = "~/.aws"
12
+ _DEFAULT_FILENAMES = ["config", "credentials"]
13
+ _DEFAULT_ENV_VARS = {
14
+ "access_key": "AWS_ACCESS_KEY_ID",
15
+ "secret_key": "AWS_SECRET_ACCESS_KEY",
16
+ }
@@ -0,0 +1,14 @@
1
+ from .. import Secret
2
+
3
+
4
+ class AzureSecret(Secret):
5
+ """
6
+ .. note::
7
+ To create an AzureSecret, please use the factory method :func:`secret` with ``provider="azure"``.
8
+ """
9
+
10
+ # values format: {"subscription_id": subscription_id}
11
+ _PROVIDER = "azure"
12
+ _DEFAULT_PATH = "~/.azure"
13
+ _DEFAULT_FILENAMES = ["clouds.config"]
14
+ _DEFAULT_ENV_VARS = {"subscription_id": "AZURE_SUBSCRIPTION_ID"}
@@ -0,0 +1,12 @@
1
+ from .. import Secret
2
+
3
+
4
+ class CohereSecret(Secret):
5
+ """
6
+ .. note::
7
+ To create an CohereSecret, please use the factory method :func:`secret`
8
+ with ``provider="cohere"``.
9
+ """
10
+
11
+ _PROVIDER = "cohere"
12
+ _DEFAULT_ENV_VARS = {"api_key": "COHERE_API_KEY"}
@@ -0,0 +1,16 @@
1
+ from .. import Secret
2
+
3
+
4
+ class GCPSecret(Secret):
5
+ """
6
+ .. note::
7
+ To create a GCPSecret, please use the factory method :func:`secret` with ``provider="gcp"``.
8
+ """
9
+
10
+ _PROVIDER = "gcp"
11
+ _DEFAULT_PATH = "~/.config/gcloud"
12
+ _DEFAULT_FILENAMES = ["application_default_credentials.json"]
13
+ _DEFAULT_ENV_VARS = {
14
+ "client_id": "CLIENT_ID",
15
+ "client_secret": "CLIENT_SECRET",
16
+ }
@@ -0,0 +1,13 @@
1
+ from .. import Secret
2
+
3
+
4
+ class GitHubSecret(Secret):
5
+ """
6
+ .. note::
7
+ To create a GitHubSecret, please use the factory method :func:`secret` with ``provider="github"``.
8
+ """
9
+
10
+ # values format: {"oauth_token": oath_token}
11
+ _PROVIDER = "github"
12
+ _DEFAULT_PATH = "~/.config/gh"
13
+ _DEFAULT_FILENAMES = ["hosts.yml"]
@@ -0,0 +1,20 @@
1
+ from .. import Secret
2
+
3
+
4
+ class HuggingFaceSecret(Secret):
5
+ """
6
+ .. note::
7
+ To create a HuggingFaceSecret, please use the factory method :func:`secret` with
8
+ ``provider="huggingface"``.
9
+ """
10
+
11
+ # values format: {"token": hf_token}
12
+ _PROVIDER = "huggingface"
13
+ _DEFAULT_PATH = "~/.cache/huggingface"
14
+ _DEFAULT_FILENAMES = ["token"]
15
+ _DEFAULT_ENV_VARS = ["HF_TOKEN"]
16
+
17
+ # Ensure secrets can be used as environment variables
18
+ _MAP_FILENAMES_TO_ENV_VARS = {
19
+ "token": "HF_TOKEN",
20
+ }
@@ -0,0 +1,12 @@
1
+ from .. import Secret
2
+
3
+
4
+ class KubeConfigSecret(Secret):
5
+ """
6
+ .. note::
7
+ To create a KubeConfigSecret, please use the factory method :func:`secret` with ``provider=="kubernetes"``.
8
+ """
9
+
10
+ _PROVIDER = "kubernetes"
11
+ _DEFAULT_PATH = "~/.kube"
12
+ _DEFAULT_FILENAMES = ["config"]
@@ -0,0 +1,13 @@
1
+ from .. import Secret
2
+
3
+
4
+ class LambdaSecret(Secret):
5
+ """
6
+ .. note::
7
+ To create a LambdaSecret, please use the factory method :func:`secret` with ``provider="lambda"``.
8
+ """
9
+
10
+ # values format: {"api_key": api_key}
11
+ _DEFAULT_PATH = "~/.lambda_cloud"
12
+ _DEFAULT_FILENAMES = ["lambda_keys"]
13
+ _PROVIDER = "lambda"
@@ -0,0 +1,12 @@
1
+ from .. import Secret
2
+
3
+
4
+ class LangChainSecret(Secret):
5
+ """
6
+ .. note::
7
+ To create an LangChainSecret, please use the factory method :func:`secret`
8
+ with ``provider="langchain"``.
9
+ """
10
+
11
+ _PROVIDER = "langchain"
12
+ _DEFAULT_ENV_VARS = {"api_key": "LANGCHAIN_API_KEY"}
@@ -0,0 +1,11 @@
1
+ from .. import Secret
2
+
3
+
4
+ class OpenAISecret(Secret):
5
+ """
6
+ .. note::
7
+ To create an OpenAISecret, please use the factory method :func:`secret` with ``provider="openai"``.
8
+ """
9
+
10
+ _PROVIDER = "openai"
11
+ _DEFAULT_ENV_VARS = {"api_key": "OPENAI_API_KEY"}
@@ -0,0 +1,12 @@
1
+ from .. import Secret
2
+
3
+
4
+ class PineconeSecret(Secret):
5
+ """
6
+ .. note::
7
+ To create an PineconeSecret, please use the factory method :func:`secret`
8
+ with ``provider="pinecone"``.
9
+ """
10
+
11
+ _PROVIDER = "pinecone"
12
+ _DEFAULT_ENV_VARS = {"api_key": "PINECONE_API_KEY"}
@@ -0,0 +1,92 @@
1
+ from typing import Union
2
+
3
+ from .anthropic_secret import AnthropicSecret
4
+ from .aws_secret import AWSSecret
5
+ from .azure_secret import AzureSecret
6
+ from .cohere_secret import CohereSecret
7
+ from .gcp_secret import GCPSecret
8
+ from .github_secret import GitHubSecret
9
+ from .huggingface_secret import HuggingFaceSecret
10
+ from .kubeconfig_secret import KubeConfigSecret
11
+ from .lambda_secret import LambdaSecret
12
+ from .langchain_secret import LangChainSecret
13
+ from .openai_secret import OpenAISecret
14
+ from .pinecone_secret import PineconeSecret
15
+ from .ssh_secret import SSHSecret
16
+ from .wandb_secret import WandBSecret
17
+
18
+ _str_to_provider_class = {
19
+ # File and/or Env secrets
20
+ "aws": AWSSecret,
21
+ "azure": AzureSecret,
22
+ "gcp": GCPSecret,
23
+ "github": GitHubSecret,
24
+ "huggingface": HuggingFaceSecret,
25
+ "kubernetes": KubeConfigSecret,
26
+ "lambda": LambdaSecret,
27
+ # SSH secrets
28
+ "ssh": SSHSecret,
29
+ # API key secrets
30
+ "anthropic": AnthropicSecret,
31
+ "cohere": CohereSecret,
32
+ "langchain": LangChainSecret,
33
+ "openai": OpenAISecret,
34
+ "pinecone": PineconeSecret,
35
+ "wandb": WandBSecret,
36
+ }
37
+
38
+ _path_to_provider_class = {
39
+ "~/.aws": AWSSecret,
40
+ "~/.azure": AzureSecret,
41
+ "~/.config/gcloud": GCPSecret,
42
+ "~/.config/gh": GitHubSecret,
43
+ "~/.cache/huggingface": HuggingFaceSecret,
44
+ "~/.kube": KubeConfigSecret,
45
+ "~/.lambda_cloud": LambdaSecret,
46
+ "~/.ssh": SSHSecret,
47
+ }
48
+
49
+ _secret_to_env_vars = {
50
+ "aws": {"access_key": "AWS_ACCESS_KEY_ID", "secret_key": "AWS_SECRET_ACCESS_KEY"},
51
+ "azure": {"subscription_id": "AZURE_SUBSCRIPTION_ID"},
52
+ "gcp": {"client_id": "CLIENT_ID", "client_secret": "CLIENT_SECRET"},
53
+ "github": {},
54
+ "huggingface": ["HF_TOKEN"],
55
+ "anthropic": {"api_key": "ANTHROPIC_API_KEY"},
56
+ "cohere": {"api_key": "COHERE_API_KEY"},
57
+ "langchain": {"api_key": "LANGCHAIN_API_KEY"},
58
+ "openai": {"api_key": "OPENAI_API_KEY"},
59
+ "pinecone": {"api_key": "PINECONE_API_KEY"},
60
+ "wandb": {"api_key": "WANDB_API_KEY"},
61
+ }
62
+
63
+
64
+ def _check_if_provider_secret(provider_str: str = None, provider_env_vars: dict = None):
65
+ import os
66
+
67
+ # user passed provider name or path to secrets values
68
+ if provider_str:
69
+ if provider_str in _str_to_provider_class.keys():
70
+ return _str_to_provider_class[provider_str]
71
+
72
+ full_default_paths_to_provider = {
73
+ os.path.abspath(path): secret for path, secret in _path_to_provider_class.items()
74
+ }
75
+ provided_full_path = os.path.abspath(provider_str)
76
+ return full_default_paths_to_provider.get(provided_full_path, None)
77
+
78
+ # user passed secrets keys to env vars mapping
79
+ elif provider_env_vars:
80
+ for provider_name, default_provider_env_vars in _secret_to_env_vars.items():
81
+ if provider_env_vars == default_provider_env_vars:
82
+ return _str_to_provider_class[provider_name]
83
+ return None
84
+
85
+
86
+ def _get_provider_class(provider_info: Union[str, dict]):
87
+ provider_secret = (
88
+ _check_if_provider_secret(provider_str=provider_info)
89
+ if isinstance(provider_info, str)
90
+ else _check_if_provider_secret(provider_env_vars=provider_info)
91
+ )
92
+ return provider_secret
@@ -0,0 +1,12 @@
1
+ from .. import Secret
2
+
3
+
4
+ class SSHSecret(Secret):
5
+ """
6
+ .. note::
7
+ To create a SSHSecret, please use the factory method :func:`secret` with ``provider="ssh"``.
8
+ """
9
+
10
+ _DEFAULT_PATH = "~/.ssh"
11
+ _DEFAULT_FILENAMES = ["id_rsa"]
12
+ _PROVIDER = "ssh"
@@ -0,0 +1,11 @@
1
+ from .. import Secret
2
+
3
+
4
+ class WandBSecret(Secret):
5
+ """
6
+ .. note::
7
+ To create an WandBSecret, please use the factory method :func:`secret` with ``provider="wandb"``.
8
+ """
9
+
10
+ _PROVIDER = "wandb"
11
+ _DEFAULT_ENV_VARS = {"api_key": "WANDB_API_KEY"}