kubetorch 0.2.0__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.

Potentially problematic release.


This version of kubetorch might be problematic. Click here for more details.

Files changed (93) hide show
  1. kubetorch/__init__.py +60 -0
  2. kubetorch/cli.py +1985 -0
  3. kubetorch/cli_utils.py +1025 -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 +285 -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 +157 -0
  28. kubetorch/resources/callables/fn/__init__.py +0 -0
  29. kubetorch/resources/callables/fn/fn.py +133 -0
  30. kubetorch/resources/callables/module.py +1416 -0
  31. kubetorch/resources/callables/utils.py +174 -0
  32. kubetorch/resources/compute/__init__.py +0 -0
  33. kubetorch/resources/compute/app.py +261 -0
  34. kubetorch/resources/compute/compute.py +2596 -0
  35. kubetorch/resources/compute/decorators.py +139 -0
  36. kubetorch/resources/compute/rbac.py +74 -0
  37. kubetorch/resources/compute/utils.py +1114 -0
  38. kubetorch/resources/compute/websocket.py +137 -0
  39. kubetorch/resources/images/__init__.py +1 -0
  40. kubetorch/resources/images/image.py +414 -0
  41. kubetorch/resources/images/images.py +74 -0
  42. kubetorch/resources/secrets/__init__.py +2 -0
  43. kubetorch/resources/secrets/kubernetes_secrets_client.py +412 -0
  44. kubetorch/resources/secrets/provider_secrets/__init__.py +0 -0
  45. kubetorch/resources/secrets/provider_secrets/anthropic_secret.py +12 -0
  46. kubetorch/resources/secrets/provider_secrets/aws_secret.py +16 -0
  47. kubetorch/resources/secrets/provider_secrets/azure_secret.py +14 -0
  48. kubetorch/resources/secrets/provider_secrets/cohere_secret.py +12 -0
  49. kubetorch/resources/secrets/provider_secrets/gcp_secret.py +16 -0
  50. kubetorch/resources/secrets/provider_secrets/github_secret.py +13 -0
  51. kubetorch/resources/secrets/provider_secrets/huggingface_secret.py +20 -0
  52. kubetorch/resources/secrets/provider_secrets/kubeconfig_secret.py +12 -0
  53. kubetorch/resources/secrets/provider_secrets/lambda_secret.py +13 -0
  54. kubetorch/resources/secrets/provider_secrets/langchain_secret.py +12 -0
  55. kubetorch/resources/secrets/provider_secrets/openai_secret.py +11 -0
  56. kubetorch/resources/secrets/provider_secrets/pinecone_secret.py +12 -0
  57. kubetorch/resources/secrets/provider_secrets/providers.py +93 -0
  58. kubetorch/resources/secrets/provider_secrets/ssh_secret.py +12 -0
  59. kubetorch/resources/secrets/provider_secrets/wandb_secret.py +11 -0
  60. kubetorch/resources/secrets/secret.py +238 -0
  61. kubetorch/resources/secrets/secret_factory.py +70 -0
  62. kubetorch/resources/secrets/utils.py +209 -0
  63. kubetorch/resources/volumes/__init__.py +0 -0
  64. kubetorch/resources/volumes/volume.py +365 -0
  65. kubetorch/servers/__init__.py +0 -0
  66. kubetorch/servers/http/__init__.py +0 -0
  67. kubetorch/servers/http/distributed_utils.py +3223 -0
  68. kubetorch/servers/http/http_client.py +730 -0
  69. kubetorch/servers/http/http_server.py +1788 -0
  70. kubetorch/servers/http/server_metrics.py +278 -0
  71. kubetorch/servers/http/utils.py +728 -0
  72. kubetorch/serving/__init__.py +0 -0
  73. kubetorch/serving/autoscaling.py +173 -0
  74. kubetorch/serving/base_service_manager.py +363 -0
  75. kubetorch/serving/constants.py +83 -0
  76. kubetorch/serving/deployment_service_manager.py +478 -0
  77. kubetorch/serving/knative_service_manager.py +519 -0
  78. kubetorch/serving/raycluster_service_manager.py +582 -0
  79. kubetorch/serving/service_manager.py +18 -0
  80. kubetorch/serving/templates/deployment_template.yaml +17 -0
  81. kubetorch/serving/templates/knative_service_template.yaml +19 -0
  82. kubetorch/serving/templates/kt_setup_template.sh.j2 +81 -0
  83. kubetorch/serving/templates/pod_template.yaml +194 -0
  84. kubetorch/serving/templates/raycluster_service_template.yaml +42 -0
  85. kubetorch/serving/templates/raycluster_template.yaml +35 -0
  86. kubetorch/serving/templates/service_template.yaml +21 -0
  87. kubetorch/serving/templates/workerset_template.yaml +36 -0
  88. kubetorch/serving/utils.py +377 -0
  89. kubetorch/utils.py +284 -0
  90. kubetorch-0.2.0.dist-info/METADATA +121 -0
  91. kubetorch-0.2.0.dist-info/RECORD +93 -0
  92. kubetorch-0.2.0.dist-info/WHEEL +4 -0
  93. kubetorch-0.2.0.dist-info/entry_points.txt +5 -0
@@ -0,0 +1,412 @@
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(
66
+ self, secrets: List[Union[str, Secret]]
67
+ ) -> List[Secret]:
68
+ """
69
+ Converts a list of strings and Secrets into Secret objects without uploading.
70
+ """
71
+ from kubetorch.resources.secrets.secret_factory import secret as secret_factory
72
+
73
+ secret_objects = []
74
+ for secret_or_string in secrets:
75
+ # Create a provider secret if only the name is provided
76
+ secret = (
77
+ secret_factory(provider=secret_or_string)
78
+ if isinstance(secret_or_string, str)
79
+ else secret_or_string
80
+ )
81
+ secret_objects.append(secret)
82
+
83
+ return secret_objects
84
+
85
+ def upload_secrets_list(self, secrets: List[Union[str, Secret]]) -> List[Secret]:
86
+ """Uploads secrets to Kubernetes Secrets to be used in knative yaml."""
87
+ if is_running_in_kubernetes():
88
+ return []
89
+
90
+ # Convert to Secret objects first
91
+ secret_objects = self.convert_to_secret_objects(secrets)
92
+
93
+ synced_secrets = []
94
+ for secret in secret_objects:
95
+ success = self.create_or_update_secret(secret=secret)
96
+ if success:
97
+ synced_secrets.append(secret)
98
+
99
+ return synced_secrets
100
+
101
+ def extract_envs_and_volumes_from_secrets(
102
+ self,
103
+ secrets: List[Secret] = None,
104
+ ) -> Tuple[list, list]:
105
+ if not secrets:
106
+ return [], []
107
+
108
+ env_vars = []
109
+ volumes = []
110
+ for secret in secrets:
111
+ secret_name = self._format_secret_name(secret.name)
112
+ if secret.env_vars:
113
+ env_vars.append(
114
+ {
115
+ "env_vars": secret.env_vars,
116
+ "secret_name": secret_name,
117
+ }
118
+ )
119
+ if secret.path:
120
+ path = secret.path.replace("~", "/root")
121
+ if not secret.filenames:
122
+ # Reformat path to only include directory
123
+ path = os.path.dirname(path)
124
+ volumes.append(
125
+ {
126
+ "name": f"secrets-{secret.name}",
127
+ "secret_name": secret_name,
128
+ "path": path,
129
+ }
130
+ )
131
+ return env_vars, volumes
132
+
133
+ def _format_secret_name(self, name: str) -> str:
134
+ """Appends user ID to name to ensure uniqueness."""
135
+ user = self.kt_config.username or "global"
136
+ if user in name:
137
+ return name
138
+ return f"kt.secret.{user}.{name}"
139
+
140
+ def _read_secret(self, name: str) -> Optional[dict]:
141
+ secret_name = name
142
+ try:
143
+ secret = self.api_client.read_namespaced_secret(name, self.namespace)
144
+ except ApiException as e:
145
+ if (
146
+ e.status == 404
147
+ ): # secret does not exist, try to load with formatted k8 name
148
+ secret_name = self._format_secret_name(name)
149
+ try:
150
+ secret = self.api_client.read_namespaced_secret(
151
+ secret_name, self.namespace
152
+ )
153
+ except ApiException as e:
154
+ if e.status == 404:
155
+ logger.info(
156
+ f"Secret {secret_name} not found in namespace {self.namespace}",
157
+ )
158
+ return None
159
+ else:
160
+ logger.error(
161
+ f"Failed to read secret {name} from Kubernetes: {str(e)}",
162
+ )
163
+ return None
164
+ else:
165
+ logger.error(
166
+ f"Failed to read secret {name} from Kubernetes: {str(e)}",
167
+ )
168
+ return None
169
+ except Exception as e:
170
+ logger.error(
171
+ f"Unexpected error occurred while reading secret {secret_name} from Kubernetes: {str(e)}",
172
+ )
173
+ return None
174
+
175
+ override = (
176
+ secret.metadata.annotations.get("kubetorch.com/override", "False")
177
+ if secret.metadata.annotations
178
+ else None
179
+ )
180
+ path = (
181
+ secret.metadata.annotations.get("kubetorch.com/secret-path", None)
182
+ if secret.metadata.annotations
183
+ else None
184
+ )
185
+ filenames = (
186
+ secret.metadata.annotations.get("kubetorch.com/secret-filenames", None)
187
+ if secret.metadata.annotations
188
+ else None
189
+ )
190
+ filenames = json.loads(filenames) if filenames else filenames
191
+
192
+ secret_config = {
193
+ "name": secret_name,
194
+ "namespace": self.namespace,
195
+ "override": override,
196
+ }
197
+
198
+ if path:
199
+ secret_config["path"] = path
200
+ secret_config["filenames"] = filenames
201
+ return secret_config
202
+
203
+ decoded_values = {
204
+ k: base64.b64decode(v).decode("utf-8") for k, v in secret.data.items()
205
+ }
206
+ secret_config["values"] = decoded_values
207
+
208
+ mount_type = secret.metadata.labels.get("kubetorch.com/mount-type", None)
209
+ if mount_type == "env":
210
+ secret_config["env_vars"] = list(decoded_values.keys())
211
+
212
+ return secret_config
213
+
214
+ def _build_secret_body(self, secret: Secret):
215
+ secret_name = self._format_secret_name(secret.name)
216
+ provider = secret.provider
217
+ mount_type = "volume" if secret.path else "env"
218
+ encoded_data = {
219
+ k: base64.b64encode(v.encode()).decode() for k, v in secret.values.items()
220
+ }
221
+ labels = {
222
+ "kubetorch.com/mount-type": mount_type,
223
+ "kubetorch.com/secret-name": secret.name,
224
+ }
225
+ if self.user_id:
226
+ labels[KT_USER_IDENTIFIER_LABEL] = self.user_id
227
+ if self.kt_config.username:
228
+ labels[KT_USERNAME_LABEL] = self.kt_config.username
229
+ if provider:
230
+ labels["kubetorch.com/provider"] = provider
231
+ annotations = {"kubetorch.com/override": str(secret.override)}
232
+ if secret.path:
233
+ annotations["kubetorch.com/secret-path"] = secret.path
234
+ annotations["kubetorch.com/secret-filenames"] = json.dumps(secret.filenames)
235
+
236
+ metadata = client.V1ObjectMeta(
237
+ name=secret_name,
238
+ namespace=self.namespace,
239
+ labels=labels,
240
+ annotations=annotations,
241
+ )
242
+ secret_body = client.V1Secret(
243
+ metadata=metadata, data=encoded_data, type="Opaque" # default secret type
244
+ )
245
+
246
+ return secret_body
247
+
248
+ def create_secret(self, secret: Secret, console: "Console" = None):
249
+ secret_name = self._format_secret_name(secret.name)
250
+ secret_body = self._build_secret_body(secret=secret)
251
+
252
+ try:
253
+ self.api_client.create_namespaced_secret(self.namespace, secret_body)
254
+ if console:
255
+ console.print("[bold green]✔ Secret created successfully[/bold green]")
256
+ console.print(f" Name: [cyan]{secret.name}[/cyan]")
257
+ console.print(f" Namespace: [cyan]{self.namespace}[/cyan]")
258
+ else:
259
+ logger.info(f"Created new Kubernetes secret {secret.name}")
260
+ return True
261
+
262
+ except ApiException as e:
263
+ if console:
264
+ if e.status == 409:
265
+ msg = f"[yellow]Secret '{secret.name}' already exists in namespace {self.namespace}, skipping creation[/yellow]"
266
+ else:
267
+ msg = f"[red]Failed to create Kubernetes secret {secret_name}: {str(e)}[/red]"
268
+ console.print(msg)
269
+ return False
270
+ else:
271
+ raise e
272
+
273
+ except Exception as e:
274
+ if console:
275
+ msg = f"Unexpected error occurred while creating new Kubernetes secret {secret.name}: {str(e)}"
276
+ console.print(f"[red]{msg}[/red]")
277
+ return False
278
+ else:
279
+ raise e
280
+
281
+ def _get_existing_secret(self, secret: Secret):
282
+ try:
283
+ return self.api_client.read_namespaced_secret(secret.name, self.namespace)
284
+ except ApiException as e:
285
+ if e.status == 404: # try to load secret with kubernetes formatted name
286
+ formatted_name = self._format_secret_name(secret.name)
287
+ try:
288
+ return self.api_client.read_namespaced_secret(
289
+ formatted_name, self.namespace
290
+ )
291
+ except ApiException:
292
+ return None
293
+
294
+ def update_secret(self, secret: Secret, console: "Console" = None):
295
+ existing_secret = self._get_existing_secret(secret)
296
+ if existing_secret is None:
297
+ if console:
298
+ console.print(
299
+ f"[red]Failed to update secret {secret.name}: secret does not exist[/red]"
300
+ )
301
+ return False
302
+ else:
303
+ raise kubetorch.SecretNotFound(
304
+ secret_name=secret.name, namespace=self.namespace
305
+ )
306
+
307
+ if not secret.override:
308
+ decoded_values = {
309
+ k: base64.b64decode(v).decode("utf-8")
310
+ for k, v in existing_secret.data.items()
311
+ }
312
+ if not decoded_values == secret.values:
313
+ msg = f"Secret {secret.name} exists with different values and `secret.override` not set to True."
314
+ if console:
315
+ console.print(f"[yellow]{msg}[/yellow]")
316
+ return False
317
+ else:
318
+ raise ValueError(msg)
319
+ else:
320
+ msg = f"Secret {secret.name} already exists with the same values."
321
+ console.print(
322
+ f"[bold green]{msg}[/bold green]"
323
+ ) if console else logger.info(msg)
324
+ return True
325
+
326
+ secret_name = self._format_secret_name(secret.name)
327
+ secret_body = self._build_secret_body(secret=secret)
328
+
329
+ try:
330
+ self.api_client.replace_namespaced_secret(
331
+ secret_name, self.namespace, secret_body
332
+ )
333
+ if console:
334
+ console.print("[bold green]✔ Secret updated successfully[/bold green]")
335
+ console.print(f" Name: [cyan]{secret.name}[/cyan]")
336
+ console.print(f" Namespace: [cyan]{self.namespace}[/cyan]")
337
+ else:
338
+ logger.info(f"Updated existing Kubernetes secret {secret_name}")
339
+ return True
340
+
341
+ except Exception as e:
342
+ if console:
343
+ console.print(
344
+ f"[red]Failed to update secret {secret.name}: {str(e)}[/red]"
345
+ )
346
+ return False
347
+ raise e
348
+
349
+ def create_or_update_secret(self, secret: Secret, console: "Console" = None):
350
+ try:
351
+ return self.update_secret(secret, console)
352
+ except kubetorch.SecretNotFound:
353
+ # if secret not found, try to create the secret.
354
+ return self.create_secret(secret, console)
355
+
356
+ def _delete_secret(self, name: str, console: "Console" = None):
357
+ name = self._format_secret_name(name)
358
+ try:
359
+ self.api_client.delete_namespaced_secret(name, self.namespace)
360
+ if console:
361
+ console.print(f"✓ Deleted secret [blue]{name}[/blue]")
362
+ else:
363
+ logger.info(f"Deleted Kubernetes secret: {name}")
364
+ return True
365
+
366
+ except ApiException as e:
367
+ msg = f"Failed to delete Kubernetes secret: {name}: {str(e)}"
368
+ console.print(msg) if console else logger.error(msg)
369
+ return False
370
+
371
+ except Exception as e:
372
+ msg = f"Unexpected error occurred while deleting Kubernetes secret {name}: {str(e)}"
373
+ console.print(msg) if console else logger.error(msg)
374
+ return False
375
+
376
+ def _delete_all_secrets_for_user(self, username: Optional[str] = None):
377
+ username = username or self.kt_config.username
378
+ label_selector = f"{KT_USERNAME_LABEL}={username}"
379
+ try:
380
+ secrets: V1SecretList = self.api_client.list_namespaced_secret(
381
+ namespace=self.namespace, label_selector=label_selector
382
+ )
383
+ deleted_all = True
384
+
385
+ for secret in secrets.items:
386
+ secret_name = secret.metadata.name
387
+ try:
388
+ self.api_client.delete_namespaced_secret(
389
+ secret_name, self.namespace
390
+ )
391
+ logger.info(
392
+ f"Deleted Kubernetes secret {secret_name} for user {username}",
393
+ )
394
+
395
+ except ApiException as e:
396
+ logger.warning(
397
+ f"Failed to delete specific Kubernetes secret {secret_name} for user {username}: {str(e)}",
398
+ )
399
+ deleted_all = False
400
+
401
+ return deleted_all
402
+
403
+ except ApiException as e:
404
+ logger.error(
405
+ f"Failed to list Kubernetes secrets: {str(e)}",
406
+ )
407
+ return False
408
+
409
+ except Exception as e:
410
+ logger.error(
411
+ f"Unexpected error occurred while listing Kubernetes secrets: {str(e)}",
412
+ )
@@ -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,93 @@
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
74
+ for path, secret in _path_to_provider_class.items()
75
+ }
76
+ provided_full_path = os.path.abspath(provider_str)
77
+ return full_default_paths_to_provider.get(provided_full_path, None)
78
+
79
+ # user passed secrets keys to env vars mapping
80
+ elif provider_env_vars:
81
+ for provider_name, default_provider_env_vars in _secret_to_env_vars.items():
82
+ if provider_env_vars == default_provider_env_vars:
83
+ return _str_to_provider_class[provider_name]
84
+ return None
85
+
86
+
87
+ def _get_provider_class(provider_info: Union[str, dict]):
88
+ provider_secret = (
89
+ _check_if_provider_secret(provider_str=provider_info)
90
+ if isinstance(provider_info, str)
91
+ else _check_if_provider_secret(provider_env_vars=provider_info)
92
+ )
93
+ return provider_secret