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.
Files changed (97) hide show
  1. tetra_rp/__init__.py +109 -19
  2. tetra_rp/cli/commands/__init__.py +1 -0
  3. tetra_rp/cli/commands/apps.py +143 -0
  4. tetra_rp/cli/commands/build.py +1082 -0
  5. tetra_rp/cli/commands/build_utils/__init__.py +1 -0
  6. tetra_rp/cli/commands/build_utils/handler_generator.py +176 -0
  7. tetra_rp/cli/commands/build_utils/lb_handler_generator.py +309 -0
  8. tetra_rp/cli/commands/build_utils/manifest.py +430 -0
  9. tetra_rp/cli/commands/build_utils/mothership_handler_generator.py +75 -0
  10. tetra_rp/cli/commands/build_utils/scanner.py +596 -0
  11. tetra_rp/cli/commands/deploy.py +580 -0
  12. tetra_rp/cli/commands/init.py +123 -0
  13. tetra_rp/cli/commands/resource.py +108 -0
  14. tetra_rp/cli/commands/run.py +296 -0
  15. tetra_rp/cli/commands/test_mothership.py +458 -0
  16. tetra_rp/cli/commands/undeploy.py +533 -0
  17. tetra_rp/cli/main.py +97 -0
  18. tetra_rp/cli/utils/__init__.py +1 -0
  19. tetra_rp/cli/utils/app.py +15 -0
  20. tetra_rp/cli/utils/conda.py +127 -0
  21. tetra_rp/cli/utils/deployment.py +530 -0
  22. tetra_rp/cli/utils/ignore.py +143 -0
  23. tetra_rp/cli/utils/skeleton.py +184 -0
  24. tetra_rp/cli/utils/skeleton_template/.env.example +4 -0
  25. tetra_rp/cli/utils/skeleton_template/.flashignore +40 -0
  26. tetra_rp/cli/utils/skeleton_template/.gitignore +44 -0
  27. tetra_rp/cli/utils/skeleton_template/README.md +263 -0
  28. tetra_rp/cli/utils/skeleton_template/main.py +44 -0
  29. tetra_rp/cli/utils/skeleton_template/mothership.py +55 -0
  30. tetra_rp/cli/utils/skeleton_template/pyproject.toml +58 -0
  31. tetra_rp/cli/utils/skeleton_template/requirements.txt +1 -0
  32. tetra_rp/cli/utils/skeleton_template/workers/__init__.py +0 -0
  33. tetra_rp/cli/utils/skeleton_template/workers/cpu/__init__.py +19 -0
  34. tetra_rp/cli/utils/skeleton_template/workers/cpu/endpoint.py +36 -0
  35. tetra_rp/cli/utils/skeleton_template/workers/gpu/__init__.py +19 -0
  36. tetra_rp/cli/utils/skeleton_template/workers/gpu/endpoint.py +61 -0
  37. tetra_rp/client.py +136 -33
  38. tetra_rp/config.py +29 -0
  39. tetra_rp/core/api/runpod.py +591 -39
  40. tetra_rp/core/deployment.py +232 -0
  41. tetra_rp/core/discovery.py +425 -0
  42. tetra_rp/core/exceptions.py +50 -0
  43. tetra_rp/core/resources/__init__.py +27 -9
  44. tetra_rp/core/resources/app.py +738 -0
  45. tetra_rp/core/resources/base.py +139 -4
  46. tetra_rp/core/resources/constants.py +21 -0
  47. tetra_rp/core/resources/cpu.py +115 -13
  48. tetra_rp/core/resources/gpu.py +182 -16
  49. tetra_rp/core/resources/live_serverless.py +153 -16
  50. tetra_rp/core/resources/load_balancer_sls_resource.py +440 -0
  51. tetra_rp/core/resources/network_volume.py +126 -31
  52. tetra_rp/core/resources/resource_manager.py +436 -35
  53. tetra_rp/core/resources/serverless.py +537 -120
  54. tetra_rp/core/resources/serverless_cpu.py +201 -0
  55. tetra_rp/core/resources/template.py +1 -59
  56. tetra_rp/core/utils/constants.py +10 -0
  57. tetra_rp/core/utils/file_lock.py +260 -0
  58. tetra_rp/core/utils/http.py +67 -0
  59. tetra_rp/core/utils/lru_cache.py +75 -0
  60. tetra_rp/core/utils/singleton.py +36 -1
  61. tetra_rp/core/validation.py +44 -0
  62. tetra_rp/execute_class.py +301 -0
  63. tetra_rp/protos/remote_execution.py +98 -9
  64. tetra_rp/runtime/__init__.py +1 -0
  65. tetra_rp/runtime/circuit_breaker.py +274 -0
  66. tetra_rp/runtime/config.py +12 -0
  67. tetra_rp/runtime/exceptions.py +49 -0
  68. tetra_rp/runtime/generic_handler.py +206 -0
  69. tetra_rp/runtime/lb_handler.py +189 -0
  70. tetra_rp/runtime/load_balancer.py +160 -0
  71. tetra_rp/runtime/manifest_fetcher.py +192 -0
  72. tetra_rp/runtime/metrics.py +325 -0
  73. tetra_rp/runtime/models.py +73 -0
  74. tetra_rp/runtime/mothership_provisioner.py +512 -0
  75. tetra_rp/runtime/production_wrapper.py +266 -0
  76. tetra_rp/runtime/reliability_config.py +149 -0
  77. tetra_rp/runtime/retry_manager.py +118 -0
  78. tetra_rp/runtime/serialization.py +124 -0
  79. tetra_rp/runtime/service_registry.py +346 -0
  80. tetra_rp/runtime/state_manager_client.py +248 -0
  81. tetra_rp/stubs/live_serverless.py +35 -17
  82. tetra_rp/stubs/load_balancer_sls.py +357 -0
  83. tetra_rp/stubs/registry.py +145 -19
  84. {tetra_rp-0.6.0.dist-info → tetra_rp-0.24.0.dist-info}/METADATA +398 -60
  85. tetra_rp-0.24.0.dist-info/RECORD +99 -0
  86. {tetra_rp-0.6.0.dist-info → tetra_rp-0.24.0.dist-info}/WHEEL +1 -1
  87. tetra_rp-0.24.0.dist-info/entry_points.txt +2 -0
  88. tetra_rp/core/pool/cluster_manager.py +0 -177
  89. tetra_rp/core/pool/dataclass.py +0 -18
  90. tetra_rp/core/pool/ex.py +0 -38
  91. tetra_rp/core/pool/job.py +0 -22
  92. tetra_rp/core/pool/worker.py +0 -19
  93. tetra_rp/core/resources/utils.py +0 -50
  94. tetra_rp/core/utils/json.py +0 -33
  95. tetra_rp-0.6.0.dist-info/RECORD +0 -39
  96. /tetra_rp/{core/pool → cli}/__init__.py +0 -0
  97. {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
- from .client import remote # noqa: E402
13
- from .core.resources import ( # noqa: E402
14
- CpuServerlessEndpoint,
15
- CpuInstanceType,
16
- CudaVersion,
17
- GpuGroup,
18
- LiveServerless,
19
- PodTemplate,
20
- ResourceManager,
21
- ServerlessEndpoint,
22
- runpod,
23
- NetworkVolume,
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
- "runpod",
38
- "NetworkVolume",
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()