tetra-rp 0.6.0__py3-none-any.whl → 0.24.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.
- tetra_rp/__init__.py +109 -19
- tetra_rp/cli/commands/__init__.py +1 -0
- tetra_rp/cli/commands/apps.py +143 -0
- tetra_rp/cli/commands/build.py +1082 -0
- tetra_rp/cli/commands/build_utils/__init__.py +1 -0
- tetra_rp/cli/commands/build_utils/handler_generator.py +176 -0
- tetra_rp/cli/commands/build_utils/lb_handler_generator.py +309 -0
- tetra_rp/cli/commands/build_utils/manifest.py +430 -0
- tetra_rp/cli/commands/build_utils/mothership_handler_generator.py +75 -0
- tetra_rp/cli/commands/build_utils/scanner.py +596 -0
- tetra_rp/cli/commands/deploy.py +580 -0
- tetra_rp/cli/commands/init.py +123 -0
- tetra_rp/cli/commands/resource.py +108 -0
- tetra_rp/cli/commands/run.py +296 -0
- tetra_rp/cli/commands/test_mothership.py +458 -0
- tetra_rp/cli/commands/undeploy.py +533 -0
- tetra_rp/cli/main.py +97 -0
- tetra_rp/cli/utils/__init__.py +1 -0
- tetra_rp/cli/utils/app.py +15 -0
- tetra_rp/cli/utils/conda.py +127 -0
- tetra_rp/cli/utils/deployment.py +530 -0
- tetra_rp/cli/utils/ignore.py +143 -0
- tetra_rp/cli/utils/skeleton.py +184 -0
- tetra_rp/cli/utils/skeleton_template/.env.example +4 -0
- tetra_rp/cli/utils/skeleton_template/.flashignore +40 -0
- tetra_rp/cli/utils/skeleton_template/.gitignore +44 -0
- tetra_rp/cli/utils/skeleton_template/README.md +263 -0
- tetra_rp/cli/utils/skeleton_template/main.py +44 -0
- tetra_rp/cli/utils/skeleton_template/mothership.py +55 -0
- tetra_rp/cli/utils/skeleton_template/pyproject.toml +58 -0
- tetra_rp/cli/utils/skeleton_template/requirements.txt +1 -0
- tetra_rp/cli/utils/skeleton_template/workers/__init__.py +0 -0
- tetra_rp/cli/utils/skeleton_template/workers/cpu/__init__.py +19 -0
- tetra_rp/cli/utils/skeleton_template/workers/cpu/endpoint.py +36 -0
- tetra_rp/cli/utils/skeleton_template/workers/gpu/__init__.py +19 -0
- tetra_rp/cli/utils/skeleton_template/workers/gpu/endpoint.py +61 -0
- tetra_rp/client.py +136 -33
- tetra_rp/config.py +29 -0
- tetra_rp/core/api/runpod.py +591 -39
- tetra_rp/core/deployment.py +232 -0
- tetra_rp/core/discovery.py +425 -0
- tetra_rp/core/exceptions.py +50 -0
- tetra_rp/core/resources/__init__.py +27 -9
- tetra_rp/core/resources/app.py +738 -0
- tetra_rp/core/resources/base.py +139 -4
- tetra_rp/core/resources/constants.py +21 -0
- tetra_rp/core/resources/cpu.py +115 -13
- tetra_rp/core/resources/gpu.py +182 -16
- tetra_rp/core/resources/live_serverless.py +153 -16
- tetra_rp/core/resources/load_balancer_sls_resource.py +440 -0
- tetra_rp/core/resources/network_volume.py +126 -31
- tetra_rp/core/resources/resource_manager.py +436 -35
- tetra_rp/core/resources/serverless.py +537 -120
- tetra_rp/core/resources/serverless_cpu.py +201 -0
- tetra_rp/core/resources/template.py +1 -59
- tetra_rp/core/utils/constants.py +10 -0
- tetra_rp/core/utils/file_lock.py +260 -0
- tetra_rp/core/utils/http.py +67 -0
- tetra_rp/core/utils/lru_cache.py +75 -0
- tetra_rp/core/utils/singleton.py +36 -1
- tetra_rp/core/validation.py +44 -0
- tetra_rp/execute_class.py +301 -0
- tetra_rp/protos/remote_execution.py +98 -9
- tetra_rp/runtime/__init__.py +1 -0
- tetra_rp/runtime/circuit_breaker.py +274 -0
- tetra_rp/runtime/config.py +12 -0
- tetra_rp/runtime/exceptions.py +49 -0
- tetra_rp/runtime/generic_handler.py +206 -0
- tetra_rp/runtime/lb_handler.py +189 -0
- tetra_rp/runtime/load_balancer.py +160 -0
- tetra_rp/runtime/manifest_fetcher.py +192 -0
- tetra_rp/runtime/metrics.py +325 -0
- tetra_rp/runtime/models.py +73 -0
- tetra_rp/runtime/mothership_provisioner.py +512 -0
- tetra_rp/runtime/production_wrapper.py +266 -0
- tetra_rp/runtime/reliability_config.py +149 -0
- tetra_rp/runtime/retry_manager.py +118 -0
- tetra_rp/runtime/serialization.py +124 -0
- tetra_rp/runtime/service_registry.py +346 -0
- tetra_rp/runtime/state_manager_client.py +248 -0
- tetra_rp/stubs/live_serverless.py +35 -17
- tetra_rp/stubs/load_balancer_sls.py +357 -0
- tetra_rp/stubs/registry.py +145 -19
- {tetra_rp-0.6.0.dist-info → tetra_rp-0.24.0.dist-info}/METADATA +398 -60
- tetra_rp-0.24.0.dist-info/RECORD +99 -0
- {tetra_rp-0.6.0.dist-info → tetra_rp-0.24.0.dist-info}/WHEEL +1 -1
- tetra_rp-0.24.0.dist-info/entry_points.txt +2 -0
- tetra_rp/core/pool/cluster_manager.py +0 -177
- tetra_rp/core/pool/dataclass.py +0 -18
- tetra_rp/core/pool/ex.py +0 -38
- tetra_rp/core/pool/job.py +0 -22
- tetra_rp/core/pool/worker.py +0 -19
- tetra_rp/core/resources/utils.py +0 -50
- tetra_rp/core/utils/json.py +0 -33
- tetra_rp-0.6.0.dist-info/RECORD +0 -39
- /tetra_rp/{core/pool → cli}/__init__.py +0 -0
- {tetra_rp-0.6.0.dist-info → tetra_rp-0.24.0.dist-info}/top_level.txt +0 -0
tetra_rp/__init__.py
CHANGED
|
@@ -1,39 +1,129 @@
|
|
|
1
|
-
# Load .env vars from file
|
|
2
|
-
# before everything else
|
|
1
|
+
# Load .env vars from file before everything else
|
|
3
2
|
from dotenv import load_dotenv
|
|
4
3
|
|
|
5
4
|
load_dotenv()
|
|
6
5
|
|
|
7
|
-
|
|
8
6
|
from .logger import setup_logging # noqa: E402
|
|
9
7
|
|
|
10
8
|
setup_logging()
|
|
11
9
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
10
|
+
# TYPE_CHECKING imports provide full IDE support (autocomplete, type hints)
|
|
11
|
+
# while __getattr__ enables lazy loading at runtime for fast CLI startup
|
|
12
|
+
from typing import TYPE_CHECKING # noqa: E402
|
|
13
|
+
|
|
14
|
+
if TYPE_CHECKING:
|
|
15
|
+
from .client import remote
|
|
16
|
+
from .core.resources import (
|
|
17
|
+
CpuInstanceType,
|
|
18
|
+
CpuLiveLoadBalancer,
|
|
19
|
+
CpuLiveServerless,
|
|
20
|
+
CpuLoadBalancerSlsResource,
|
|
21
|
+
CpuServerlessEndpoint,
|
|
22
|
+
CudaVersion,
|
|
23
|
+
DataCenter,
|
|
24
|
+
GpuGroup,
|
|
25
|
+
GpuType,
|
|
26
|
+
LiveLoadBalancer,
|
|
27
|
+
LiveServerless,
|
|
28
|
+
LoadBalancerSlsResource,
|
|
29
|
+
NetworkVolume,
|
|
30
|
+
PodTemplate,
|
|
31
|
+
ResourceManager,
|
|
32
|
+
ServerlessEndpoint,
|
|
33
|
+
ServerlessType,
|
|
34
|
+
FlashApp,
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def __getattr__(name):
|
|
39
|
+
"""Lazily import core modules only when accessed."""
|
|
40
|
+
if name == "remote":
|
|
41
|
+
from .client import remote
|
|
42
|
+
|
|
43
|
+
return remote
|
|
44
|
+
elif name in (
|
|
45
|
+
"CpuInstanceType",
|
|
46
|
+
"CpuLiveLoadBalancer",
|
|
47
|
+
"CpuLiveServerless",
|
|
48
|
+
"CpuLoadBalancerSlsResource",
|
|
49
|
+
"CpuServerlessEndpoint",
|
|
50
|
+
"CudaVersion",
|
|
51
|
+
"DataCenter",
|
|
52
|
+
"GpuGroup",
|
|
53
|
+
"GpuType",
|
|
54
|
+
"LiveLoadBalancer",
|
|
55
|
+
"LiveServerless",
|
|
56
|
+
"LoadBalancerSlsResource",
|
|
57
|
+
"NetworkVolume",
|
|
58
|
+
"PodTemplate",
|
|
59
|
+
"ResourceManager",
|
|
60
|
+
"ServerlessEndpoint",
|
|
61
|
+
"ServerlessType",
|
|
62
|
+
"FlashApp",
|
|
63
|
+
):
|
|
64
|
+
from .core.resources import (
|
|
65
|
+
CpuInstanceType,
|
|
66
|
+
CpuLiveLoadBalancer,
|
|
67
|
+
CpuLiveServerless,
|
|
68
|
+
CpuLoadBalancerSlsResource,
|
|
69
|
+
CpuServerlessEndpoint,
|
|
70
|
+
CudaVersion,
|
|
71
|
+
DataCenter,
|
|
72
|
+
GpuGroup,
|
|
73
|
+
GpuType,
|
|
74
|
+
LiveLoadBalancer,
|
|
75
|
+
LiveServerless,
|
|
76
|
+
LoadBalancerSlsResource,
|
|
77
|
+
NetworkVolume,
|
|
78
|
+
PodTemplate,
|
|
79
|
+
ResourceManager,
|
|
80
|
+
ServerlessEndpoint,
|
|
81
|
+
ServerlessType,
|
|
82
|
+
FlashApp,
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
attrs = {
|
|
86
|
+
"CpuInstanceType": CpuInstanceType,
|
|
87
|
+
"CpuLiveLoadBalancer": CpuLiveLoadBalancer,
|
|
88
|
+
"CpuLiveServerless": CpuLiveServerless,
|
|
89
|
+
"CpuLoadBalancerSlsResource": CpuLoadBalancerSlsResource,
|
|
90
|
+
"CpuServerlessEndpoint": CpuServerlessEndpoint,
|
|
91
|
+
"CudaVersion": CudaVersion,
|
|
92
|
+
"DataCenter": DataCenter,
|
|
93
|
+
"GpuGroup": GpuGroup,
|
|
94
|
+
"GpuType": GpuType,
|
|
95
|
+
"LiveLoadBalancer": LiveLoadBalancer,
|
|
96
|
+
"LiveServerless": LiveServerless,
|
|
97
|
+
"LoadBalancerSlsResource": LoadBalancerSlsResource,
|
|
98
|
+
"NetworkVolume": NetworkVolume,
|
|
99
|
+
"PodTemplate": PodTemplate,
|
|
100
|
+
"ResourceManager": ResourceManager,
|
|
101
|
+
"ServerlessEndpoint": ServerlessEndpoint,
|
|
102
|
+
"ServerlessType": ServerlessType,
|
|
103
|
+
"FlashApp": FlashApp,
|
|
104
|
+
}
|
|
105
|
+
return attrs[name]
|
|
106
|
+
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
|
25
107
|
|
|
26
108
|
|
|
27
109
|
__all__ = [
|
|
28
110
|
"remote",
|
|
29
|
-
"CpuServerlessEndpoint",
|
|
30
111
|
"CpuInstanceType",
|
|
112
|
+
"CpuLiveLoadBalancer",
|
|
113
|
+
"CpuLiveServerless",
|
|
114
|
+
"CpuLoadBalancerSlsResource",
|
|
115
|
+
"CpuServerlessEndpoint",
|
|
31
116
|
"CudaVersion",
|
|
117
|
+
"DataCenter",
|
|
32
118
|
"GpuGroup",
|
|
119
|
+
"GpuType",
|
|
120
|
+
"LiveLoadBalancer",
|
|
33
121
|
"LiveServerless",
|
|
122
|
+
"LoadBalancerSlsResource",
|
|
123
|
+
"NetworkVolume",
|
|
34
124
|
"PodTemplate",
|
|
35
125
|
"ResourceManager",
|
|
36
126
|
"ServerlessEndpoint",
|
|
37
|
-
"
|
|
38
|
-
"
|
|
127
|
+
"ServerlessType",
|
|
128
|
+
"FlashApp",
|
|
39
129
|
]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""CLI command modules."""
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
"""Deployment environment management commands."""
|
|
2
|
+
|
|
3
|
+
import typer
|
|
4
|
+
from rich.console import Console
|
|
5
|
+
from rich.table import Table
|
|
6
|
+
from rich.panel import Panel
|
|
7
|
+
import asyncio
|
|
8
|
+
|
|
9
|
+
from tetra_rp.cli.utils.app import discover_flash_project
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
from tetra_rp.core.resources.app import FlashApp
|
|
13
|
+
|
|
14
|
+
console = Console()
|
|
15
|
+
|
|
16
|
+
apps_app = typer.Typer(short_help="Manage existing apps", name="app")
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@apps_app.command("create", short_help="Create a new flash app")
|
|
20
|
+
def create(app_name: str = typer.Argument(..., help="Name for the new flash app")):
|
|
21
|
+
return asyncio.run(create_flash_app(app_name))
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@apps_app.command("get", short_help="Get detailed information about a flash app")
|
|
25
|
+
def get(app_name: str = typer.Argument(..., help="Name of the flash app")):
|
|
26
|
+
return asyncio.run(get_flash_app(app_name))
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@apps_app.command("ls", short_help="List existing apps under your account.")
|
|
30
|
+
@apps_app.command("list", short_help="List existing apps under your account.")
|
|
31
|
+
def ls():
|
|
32
|
+
return asyncio.run(list_flash_apps())
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@apps_app.command(
|
|
36
|
+
"delete", short_help="Delete an existing flash app and all its associated resources"
|
|
37
|
+
)
|
|
38
|
+
def delete(
|
|
39
|
+
app_name: str = typer.Option(
|
|
40
|
+
..., "--app-name", "-a", help="Flash app name to delete"
|
|
41
|
+
),
|
|
42
|
+
):
|
|
43
|
+
if not app_name:
|
|
44
|
+
_, app_name = discover_flash_project()
|
|
45
|
+
return asyncio.run(delete_flash_app(app_name))
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
async def list_flash_apps():
|
|
49
|
+
apps = await FlashApp.list()
|
|
50
|
+
if not apps:
|
|
51
|
+
console.print("No Flash apps found.")
|
|
52
|
+
return
|
|
53
|
+
|
|
54
|
+
table = Table(show_header=True, header_style="bold")
|
|
55
|
+
table.add_column("Name", style="bold")
|
|
56
|
+
table.add_column("ID", overflow="fold")
|
|
57
|
+
table.add_column("Environments", overflow="fold")
|
|
58
|
+
table.add_column("Builds", overflow="fold")
|
|
59
|
+
|
|
60
|
+
for app in apps:
|
|
61
|
+
environments = app.get("flashEnvironments") or []
|
|
62
|
+
env_summary = ", ".join(env.get("name", "?") for env in environments) or "—"
|
|
63
|
+
builds = app.get("flashBuilds") or []
|
|
64
|
+
build_summary = ", ".join(build.get("id", "?") for build in builds) or "—"
|
|
65
|
+
table.add_row(
|
|
66
|
+
app.get("name", "(unnamed)"), app.get("id", "—"), env_summary, build_summary
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
console.print(table)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
async def create_flash_app(app_name: str):
|
|
73
|
+
with console.status(f"Creating flash app: {app_name}"):
|
|
74
|
+
app = await FlashApp.create(app_name)
|
|
75
|
+
|
|
76
|
+
panel_content = (
|
|
77
|
+
f"Flash app '[bold]{app_name}[/bold]' created successfully\n\nApp ID: {app.id}"
|
|
78
|
+
)
|
|
79
|
+
console.print(Panel(panel_content, title="✅ App Created", expand=False))
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
async def get_flash_app(app_name: str):
|
|
83
|
+
with console.status(f"Fetching flash app: {app_name}"):
|
|
84
|
+
app = await FlashApp.from_name(app_name)
|
|
85
|
+
# Fetch environments and builds in parallel for better performance
|
|
86
|
+
envs, builds = await asyncio.gather(app.list_environments(), app.list_builds())
|
|
87
|
+
|
|
88
|
+
main_info = f"Name: {app.name}\n"
|
|
89
|
+
main_info += f"ID: {app.id}\n"
|
|
90
|
+
main_info += f"Environments: {len(envs)}\n"
|
|
91
|
+
main_info += f"Builds: {len(builds)}"
|
|
92
|
+
|
|
93
|
+
console.print(Panel(main_info, title=f"📱 Flash App: {app_name}", expand=False))
|
|
94
|
+
|
|
95
|
+
if envs:
|
|
96
|
+
env_table = Table(title="Environments")
|
|
97
|
+
env_table.add_column("Name", style="cyan")
|
|
98
|
+
env_table.add_column("ID", overflow="fold")
|
|
99
|
+
env_table.add_column("State", style="yellow")
|
|
100
|
+
env_table.add_column("Active Build", overflow="fold")
|
|
101
|
+
env_table.add_column("Created", style="dim")
|
|
102
|
+
|
|
103
|
+
for env in envs:
|
|
104
|
+
env_table.add_row(
|
|
105
|
+
env.get("name"),
|
|
106
|
+
env.get("id", "-"),
|
|
107
|
+
env.get("state", "UNKNOWN"),
|
|
108
|
+
env.get("activeBuildId", "-"),
|
|
109
|
+
env.get("createdAt", "-"),
|
|
110
|
+
)
|
|
111
|
+
console.print(env_table)
|
|
112
|
+
|
|
113
|
+
if builds:
|
|
114
|
+
build_table = Table(title="Builds")
|
|
115
|
+
build_table.add_column("ID", overflow="fold")
|
|
116
|
+
build_table.add_column("Object Key", overflow="fold")
|
|
117
|
+
build_table.add_column("Created", style="dim")
|
|
118
|
+
|
|
119
|
+
for build in builds:
|
|
120
|
+
build_table.add_row(
|
|
121
|
+
build.get("id"),
|
|
122
|
+
build.get("objectKey", "-"),
|
|
123
|
+
build.get("createdAt", "-"),
|
|
124
|
+
)
|
|
125
|
+
console.print(build_table)
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
async def delete_flash_app(app_name: str):
|
|
129
|
+
with console.status(f"Deleting flash app: {app_name}"):
|
|
130
|
+
success = await FlashApp.delete(app_name=app_name)
|
|
131
|
+
|
|
132
|
+
if success:
|
|
133
|
+
console.print(f"✅ Flash app '{app_name}' deleted successfully")
|
|
134
|
+
else:
|
|
135
|
+
console.print(f"❌ Failed to delete flash app '{app_name}'")
|
|
136
|
+
raise typer.Exit(1)
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
@apps_app.callback(invoke_without_command=True)
|
|
140
|
+
def apps(ctx: typer.Context) -> None:
|
|
141
|
+
if ctx.invoked_subcommand is None:
|
|
142
|
+
typer.echo(ctx.command.get_help(ctx))
|
|
143
|
+
raise typer.Exit()
|