tetra-rp 0.20.1__tar.gz → 0.23.0__tar.gz
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-0.20.1 → tetra_rp-0.23.0}/PKG-INFO +1 -1
- {tetra_rp-0.20.1 → tetra_rp-0.23.0}/pyproject.toml +1 -6
- {tetra_rp-0.20.1 → tetra_rp-0.23.0}/src/tetra_rp/__init__.py +5 -0
- {tetra_rp-0.20.1 → tetra_rp-0.23.0}/src/tetra_rp/cli/commands/build.py +50 -11
- {tetra_rp-0.20.1 → tetra_rp-0.23.0}/src/tetra_rp/cli/commands/build_utils/lb_handler_generator.py +14 -66
- tetra_rp-0.23.0/src/tetra_rp/cli/commands/build_utils/manifest.py +430 -0
- tetra_rp-0.23.0/src/tetra_rp/cli/commands/build_utils/mothership_handler_generator.py +75 -0
- {tetra_rp-0.20.1 → tetra_rp-0.23.0}/src/tetra_rp/cli/commands/build_utils/scanner.py +204 -1
- {tetra_rp-0.20.1 → tetra_rp-0.23.0}/src/tetra_rp/cli/commands/deploy.py +61 -20
- {tetra_rp-0.20.1 → tetra_rp-0.23.0}/src/tetra_rp/cli/commands/init.py +4 -0
- {tetra_rp-0.20.1 → tetra_rp-0.23.0}/src/tetra_rp/cli/commands/run.py +25 -9
- {tetra_rp-0.20.1 → tetra_rp-0.23.0}/src/tetra_rp/cli/commands/test_mothership.py +6 -5
- tetra_rp-0.23.0/src/tetra_rp/cli/utils/deployment.py +517 -0
- tetra_rp-0.23.0/src/tetra_rp/cli/utils/skeleton_template/mothership.py +55 -0
- tetra_rp-0.23.0/src/tetra_rp/cli/utils/skeleton_template/pyproject.toml +58 -0
- {tetra_rp-0.20.1 → tetra_rp-0.23.0}/src/tetra_rp/cli/utils/skeleton_template/workers/cpu/__init__.py +0 -1
- {tetra_rp-0.20.1 → tetra_rp-0.23.0}/src/tetra_rp/cli/utils/skeleton_template/workers/cpu/endpoint.py +1 -3
- {tetra_rp-0.20.1 → tetra_rp-0.23.0}/src/tetra_rp/cli/utils/skeleton_template/workers/gpu/__init__.py +0 -1
- {tetra_rp-0.20.1 → tetra_rp-0.23.0}/src/tetra_rp/cli/utils/skeleton_template/workers/gpu/endpoint.py +1 -2
- {tetra_rp-0.20.1 → tetra_rp-0.23.0}/src/tetra_rp/core/api/runpod.py +4 -1
- {tetra_rp-0.20.1 → tetra_rp-0.23.0}/src/tetra_rp/core/resources/app.py +54 -3
- {tetra_rp-0.20.1 → tetra_rp-0.23.0}/src/tetra_rp/core/resources/constants.py +15 -0
- {tetra_rp-0.20.1 → tetra_rp-0.23.0}/src/tetra_rp/core/resources/cpu.py +0 -10
- tetra_rp-0.23.0/src/tetra_rp/core/resources/gpu.py +219 -0
- {tetra_rp-0.20.1 → tetra_rp-0.23.0}/src/tetra_rp/core/resources/load_balancer_sls_resource.py +9 -4
- {tetra_rp-0.20.1 → tetra_rp-0.23.0}/src/tetra_rp/core/resources/serverless.py +159 -10
- {tetra_rp-0.20.1 → tetra_rp-0.23.0}/src/tetra_rp/core/resources/serverless_cpu.py +2 -12
- tetra_rp-0.23.0/src/tetra_rp/runtime/circuit_breaker.py +274 -0
- {tetra_rp-0.20.1 → tetra_rp-0.23.0}/src/tetra_rp/runtime/lb_handler.py +0 -50
- tetra_rp-0.23.0/src/tetra_rp/runtime/load_balancer.py +160 -0
- tetra_rp-0.23.0/src/tetra_rp/runtime/metrics.py +325 -0
- {tetra_rp-0.20.1 → tetra_rp-0.23.0}/src/tetra_rp/runtime/mothership_provisioner.py +60 -15
- {tetra_rp-0.20.1 → tetra_rp-0.23.0}/src/tetra_rp/runtime/production_wrapper.py +4 -2
- tetra_rp-0.23.0/src/tetra_rp/runtime/reliability_config.py +149 -0
- tetra_rp-0.23.0/src/tetra_rp/runtime/retry_manager.py +118 -0
- {tetra_rp-0.20.1 → tetra_rp-0.23.0}/src/tetra_rp/runtime/service_registry.py +68 -38
- {tetra_rp-0.20.1 → tetra_rp-0.23.0}/src/tetra_rp.egg-info/PKG-INFO +1 -1
- {tetra_rp-0.20.1 → tetra_rp-0.23.0}/src/tetra_rp.egg-info/SOURCES.txt +8 -2
- tetra_rp-0.20.1/src/tetra_rp/cli/commands/build_utils/manifest.py +0 -170
- tetra_rp-0.20.1/src/tetra_rp/cli/utils/deployment.py +0 -157
- tetra_rp-0.20.1/src/tetra_rp/core/resources/gpu.py +0 -68
- tetra_rp-0.20.1/src/tetra_rp/core/utils/json.py +0 -46
- tetra_rp-0.20.1/src/tetra_rp/runtime/manifest_client.py +0 -141
- {tetra_rp-0.20.1 → tetra_rp-0.23.0}/README.md +0 -0
- {tetra_rp-0.20.1 → tetra_rp-0.23.0}/setup.cfg +0 -0
- {tetra_rp-0.20.1 → tetra_rp-0.23.0}/src/tetra_rp/cli/__init__.py +0 -0
- {tetra_rp-0.20.1 → tetra_rp-0.23.0}/src/tetra_rp/cli/commands/__init__.py +0 -0
- {tetra_rp-0.20.1 → tetra_rp-0.23.0}/src/tetra_rp/cli/commands/apps.py +0 -0
- {tetra_rp-0.20.1 → tetra_rp-0.23.0}/src/tetra_rp/cli/commands/build_utils/__init__.py +0 -0
- {tetra_rp-0.20.1 → tetra_rp-0.23.0}/src/tetra_rp/cli/commands/build_utils/handler_generator.py +0 -0
- {tetra_rp-0.20.1 → tetra_rp-0.23.0}/src/tetra_rp/cli/commands/resource.py +0 -0
- {tetra_rp-0.20.1 → tetra_rp-0.23.0}/src/tetra_rp/cli/commands/undeploy.py +0 -0
- {tetra_rp-0.20.1 → tetra_rp-0.23.0}/src/tetra_rp/cli/main.py +0 -0
- {tetra_rp-0.20.1 → tetra_rp-0.23.0}/src/tetra_rp/cli/utils/__init__.py +0 -0
- {tetra_rp-0.20.1 → tetra_rp-0.23.0}/src/tetra_rp/cli/utils/app.py +0 -0
- {tetra_rp-0.20.1 → tetra_rp-0.23.0}/src/tetra_rp/cli/utils/conda.py +0 -0
- {tetra_rp-0.20.1 → tetra_rp-0.23.0}/src/tetra_rp/cli/utils/ignore.py +0 -0
- {tetra_rp-0.20.1 → tetra_rp-0.23.0}/src/tetra_rp/cli/utils/skeleton.py +0 -0
- {tetra_rp-0.20.1 → tetra_rp-0.23.0}/src/tetra_rp/cli/utils/skeleton_template/.env.example +0 -0
- {tetra_rp-0.20.1 → tetra_rp-0.23.0}/src/tetra_rp/cli/utils/skeleton_template/.flashignore +0 -0
- {tetra_rp-0.20.1 → tetra_rp-0.23.0}/src/tetra_rp/cli/utils/skeleton_template/.gitignore +0 -0
- {tetra_rp-0.20.1 → tetra_rp-0.23.0}/src/tetra_rp/cli/utils/skeleton_template/README.md +0 -0
- {tetra_rp-0.20.1 → tetra_rp-0.23.0}/src/tetra_rp/cli/utils/skeleton_template/main.py +3 -3
- {tetra_rp-0.20.1 → tetra_rp-0.23.0}/src/tetra_rp/cli/utils/skeleton_template/requirements.txt +0 -0
- {tetra_rp-0.20.1 → tetra_rp-0.23.0}/src/tetra_rp/cli/utils/skeleton_template/workers/__init__.py +0 -0
- {tetra_rp-0.20.1 → tetra_rp-0.23.0}/src/tetra_rp/client.py +0 -0
- {tetra_rp-0.20.1 → tetra_rp-0.23.0}/src/tetra_rp/config.py +0 -0
- {tetra_rp-0.20.1 → tetra_rp-0.23.0}/src/tetra_rp/core/__init__.py +0 -0
- {tetra_rp-0.20.1 → tetra_rp-0.23.0}/src/tetra_rp/core/api/__init__.py +0 -0
- {tetra_rp-0.20.1 → tetra_rp-0.23.0}/src/tetra_rp/core/deployment.py +0 -0
- {tetra_rp-0.20.1 → tetra_rp-0.23.0}/src/tetra_rp/core/discovery.py +0 -0
- {tetra_rp-0.20.1 → tetra_rp-0.23.0}/src/tetra_rp/core/exceptions.py +0 -0
- {tetra_rp-0.20.1 → tetra_rp-0.23.0}/src/tetra_rp/core/resources/__init__.py +0 -0
- {tetra_rp-0.20.1 → tetra_rp-0.23.0}/src/tetra_rp/core/resources/base.py +0 -0
- {tetra_rp-0.20.1 → tetra_rp-0.23.0}/src/tetra_rp/core/resources/cloud.py +0 -0
- {tetra_rp-0.20.1 → tetra_rp-0.23.0}/src/tetra_rp/core/resources/environment.py +0 -0
- {tetra_rp-0.20.1 → tetra_rp-0.23.0}/src/tetra_rp/core/resources/live_serverless.py +0 -0
- {tetra_rp-0.20.1 → tetra_rp-0.23.0}/src/tetra_rp/core/resources/network_volume.py +0 -0
- {tetra_rp-0.20.1 → tetra_rp-0.23.0}/src/tetra_rp/core/resources/resource_manager.py +0 -0
- {tetra_rp-0.20.1 → tetra_rp-0.23.0}/src/tetra_rp/core/resources/template.py +0 -0
- {tetra_rp-0.20.1 → tetra_rp-0.23.0}/src/tetra_rp/core/utils/__init__.py +0 -0
- {tetra_rp-0.20.1 → tetra_rp-0.23.0}/src/tetra_rp/core/utils/backoff.py +0 -0
- {tetra_rp-0.20.1 → tetra_rp-0.23.0}/src/tetra_rp/core/utils/constants.py +0 -0
- {tetra_rp-0.20.1 → tetra_rp-0.23.0}/src/tetra_rp/core/utils/file_lock.py +0 -0
- {tetra_rp-0.20.1 → tetra_rp-0.23.0}/src/tetra_rp/core/utils/http.py +0 -0
- {tetra_rp-0.20.1 → tetra_rp-0.23.0}/src/tetra_rp/core/utils/lru_cache.py +0 -0
- {tetra_rp-0.20.1 → tetra_rp-0.23.0}/src/tetra_rp/core/utils/singleton.py +0 -0
- {tetra_rp-0.20.1 → tetra_rp-0.23.0}/src/tetra_rp/core/validation.py +0 -0
- {tetra_rp-0.20.1 → tetra_rp-0.23.0}/src/tetra_rp/execute_class.py +0 -0
- {tetra_rp-0.20.1 → tetra_rp-0.23.0}/src/tetra_rp/logger.py +0 -0
- {tetra_rp-0.20.1 → tetra_rp-0.23.0}/src/tetra_rp/protos/__init__.py +0 -0
- {tetra_rp-0.20.1 → tetra_rp-0.23.0}/src/tetra_rp/protos/remote_execution.py +0 -0
- {tetra_rp-0.20.1 → tetra_rp-0.23.0}/src/tetra_rp/runtime/__init__.py +0 -0
- {tetra_rp-0.20.1 → tetra_rp-0.23.0}/src/tetra_rp/runtime/config.py +0 -0
- {tetra_rp-0.20.1 → tetra_rp-0.23.0}/src/tetra_rp/runtime/exceptions.py +0 -0
- {tetra_rp-0.20.1 → tetra_rp-0.23.0}/src/tetra_rp/runtime/generic_handler.py +0 -0
- {tetra_rp-0.20.1 → tetra_rp-0.23.0}/src/tetra_rp/runtime/manifest_fetcher.py +0 -0
- {tetra_rp-0.20.1 → tetra_rp-0.23.0}/src/tetra_rp/runtime/models.py +0 -0
- {tetra_rp-0.20.1 → tetra_rp-0.23.0}/src/tetra_rp/runtime/serialization.py +0 -0
- {tetra_rp-0.20.1 → tetra_rp-0.23.0}/src/tetra_rp/runtime/state_manager_client.py +0 -0
- {tetra_rp-0.20.1 → tetra_rp-0.23.0}/src/tetra_rp/stubs/__init__.py +0 -0
- {tetra_rp-0.20.1 → tetra_rp-0.23.0}/src/tetra_rp/stubs/live_serverless.py +0 -0
- {tetra_rp-0.20.1 → tetra_rp-0.23.0}/src/tetra_rp/stubs/load_balancer_sls.py +0 -0
- {tetra_rp-0.20.1 → tetra_rp-0.23.0}/src/tetra_rp/stubs/registry.py +0 -0
- {tetra_rp-0.20.1 → tetra_rp-0.23.0}/src/tetra_rp/stubs/serverless.py +0 -0
- {tetra_rp-0.20.1 → tetra_rp-0.23.0}/src/tetra_rp.egg-info/dependency_links.txt +0 -0
- {tetra_rp-0.20.1 → tetra_rp-0.23.0}/src/tetra_rp.egg-info/entry_points.txt +0 -0
- {tetra_rp-0.20.1 → tetra_rp-0.23.0}/src/tetra_rp.egg-info/requires.txt +0 -0
- {tetra_rp-0.20.1 → tetra_rp-0.23.0}/src/tetra_rp.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: tetra_rp
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.23.0
|
|
4
4
|
Summary: A Python library for distributed inference and serving of machine learning models
|
|
5
5
|
Author-email: Marut Pandya <pandyamarut@gmail.com>, Patrick Rachford <prachford@icloud.com>, Dean Quinanola <dean.quinanola@runpod.io>
|
|
6
6
|
License: MIT
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "tetra_rp"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.23.0"
|
|
4
4
|
description = "A Python library for distributed inference and serving of machine learning models"
|
|
5
5
|
authors = [
|
|
6
6
|
{ name = "Marut Pandya", email = "pandyamarut@gmail.com" },
|
|
@@ -87,10 +87,6 @@ filterwarnings = [
|
|
|
87
87
|
]
|
|
88
88
|
|
|
89
89
|
[tool.ruff]
|
|
90
|
-
# Exclude tetra-examples directory since it's a separate repository
|
|
91
|
-
exclude = [
|
|
92
|
-
"tetra-examples/",
|
|
93
|
-
]
|
|
94
90
|
|
|
95
91
|
[tool.mypy]
|
|
96
92
|
# Basic configuration
|
|
@@ -112,7 +108,6 @@ pretty = true
|
|
|
112
108
|
|
|
113
109
|
# Exclude directories
|
|
114
110
|
exclude = [
|
|
115
|
-
"tetra-examples/",
|
|
116
111
|
"tests/", # Start by excluding tests, can add later
|
|
117
112
|
]
|
|
118
113
|
|
|
@@ -22,6 +22,7 @@ if TYPE_CHECKING:
|
|
|
22
22
|
CudaVersion,
|
|
23
23
|
DataCenter,
|
|
24
24
|
GpuGroup,
|
|
25
|
+
GpuType,
|
|
25
26
|
LiveLoadBalancer,
|
|
26
27
|
LiveServerless,
|
|
27
28
|
LoadBalancerSlsResource,
|
|
@@ -49,6 +50,7 @@ def __getattr__(name):
|
|
|
49
50
|
"CudaVersion",
|
|
50
51
|
"DataCenter",
|
|
51
52
|
"GpuGroup",
|
|
53
|
+
"GpuType",
|
|
52
54
|
"LiveLoadBalancer",
|
|
53
55
|
"LiveServerless",
|
|
54
56
|
"LoadBalancerSlsResource",
|
|
@@ -68,6 +70,7 @@ def __getattr__(name):
|
|
|
68
70
|
CudaVersion,
|
|
69
71
|
DataCenter,
|
|
70
72
|
GpuGroup,
|
|
73
|
+
GpuType,
|
|
71
74
|
LiveLoadBalancer,
|
|
72
75
|
LiveServerless,
|
|
73
76
|
LoadBalancerSlsResource,
|
|
@@ -88,6 +91,7 @@ def __getattr__(name):
|
|
|
88
91
|
"CudaVersion": CudaVersion,
|
|
89
92
|
"DataCenter": DataCenter,
|
|
90
93
|
"GpuGroup": GpuGroup,
|
|
94
|
+
"GpuType": GpuType,
|
|
91
95
|
"LiveLoadBalancer": LiveLoadBalancer,
|
|
92
96
|
"LiveServerless": LiveServerless,
|
|
93
97
|
"LoadBalancerSlsResource": LoadBalancerSlsResource,
|
|
@@ -112,6 +116,7 @@ __all__ = [
|
|
|
112
116
|
"CudaVersion",
|
|
113
117
|
"DataCenter",
|
|
114
118
|
"GpuGroup",
|
|
119
|
+
"GpuType",
|
|
115
120
|
"LiveLoadBalancer",
|
|
116
121
|
"LiveServerless",
|
|
117
122
|
"LoadBalancerSlsResource",
|
|
@@ -27,6 +27,7 @@ from ..utils.ignore import get_file_tree, load_ignore_patterns
|
|
|
27
27
|
from .build_utils.handler_generator import HandlerGenerator
|
|
28
28
|
from .build_utils.lb_handler_generator import LBHandlerGenerator
|
|
29
29
|
from .build_utils.manifest import ManifestBuilder
|
|
30
|
+
from .build_utils.mothership_handler_generator import generate_mothership_handler
|
|
30
31
|
from .build_utils.scanner import RemoteDecoratorScanner
|
|
31
32
|
|
|
32
33
|
logger = logging.getLogger(__name__)
|
|
@@ -285,26 +286,35 @@ def build_command(
|
|
|
285
286
|
scanner = RemoteDecoratorScanner(build_dir)
|
|
286
287
|
remote_functions = scanner.discover_remote_functions()
|
|
287
288
|
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
289
|
+
# Always build manifest (includes mothership even without @remote functions)
|
|
290
|
+
manifest_builder = ManifestBuilder(
|
|
291
|
+
app_name, remote_functions, scanner, build_dir=build_dir
|
|
292
|
+
)
|
|
293
|
+
manifest = manifest_builder.build()
|
|
294
|
+
manifest_path = build_dir / "flash_manifest.json"
|
|
295
|
+
manifest_path.write_text(json.dumps(manifest, indent=2))
|
|
296
|
+
|
|
297
|
+
# Copy manifest to .flash/ directory for deployment reference
|
|
298
|
+
# This avoids needing to extract from tarball during deploy
|
|
299
|
+
flash_dir = project_dir / ".flash"
|
|
300
|
+
deployment_manifest_path = flash_dir / "flash_manifest.json"
|
|
301
|
+
shutil.copy2(manifest_path, deployment_manifest_path)
|
|
294
302
|
|
|
295
|
-
|
|
296
|
-
|
|
303
|
+
# Generate handler files if there are resources
|
|
304
|
+
handler_paths = []
|
|
305
|
+
manifest_resources = manifest.get("resources", {})
|
|
297
306
|
|
|
307
|
+
if manifest_resources:
|
|
298
308
|
# Separate resources by type
|
|
299
309
|
# Use flag determined by isinstance() at scan time
|
|
300
310
|
lb_resources = {
|
|
301
311
|
name: data
|
|
302
|
-
for name, data in
|
|
312
|
+
for name, data in manifest_resources.items()
|
|
303
313
|
if data.get("is_load_balanced", False)
|
|
304
314
|
}
|
|
305
315
|
qb_resources = {
|
|
306
316
|
name: data
|
|
307
|
-
for name, data in
|
|
317
|
+
for name, data in manifest_resources.items()
|
|
308
318
|
if not data.get("is_load_balanced", False)
|
|
309
319
|
}
|
|
310
320
|
|
|
@@ -318,14 +328,43 @@ def build_command(
|
|
|
318
328
|
qb_gen = HandlerGenerator(manifest, build_dir)
|
|
319
329
|
handler_paths.extend(qb_gen.generate_handlers())
|
|
320
330
|
|
|
331
|
+
# Generate mothership handler if present in manifest
|
|
332
|
+
mothership_resources = {
|
|
333
|
+
name: data
|
|
334
|
+
for name, data in manifest_resources.items()
|
|
335
|
+
if data.get("is_mothership", False)
|
|
336
|
+
}
|
|
337
|
+
if mothership_resources:
|
|
338
|
+
for (
|
|
339
|
+
resource_name,
|
|
340
|
+
resource_data,
|
|
341
|
+
) in mothership_resources.items():
|
|
342
|
+
mothership_handler_path = (
|
|
343
|
+
build_dir / "handlers" / "handler_mothership.py"
|
|
344
|
+
)
|
|
345
|
+
generate_mothership_handler(
|
|
346
|
+
main_file=resource_data.get("main_file", "main.py"),
|
|
347
|
+
app_variable=resource_data.get(
|
|
348
|
+
"app_variable", "app"
|
|
349
|
+
),
|
|
350
|
+
output_path=mothership_handler_path,
|
|
351
|
+
)
|
|
352
|
+
handler_paths.append(str(mothership_handler_path))
|
|
353
|
+
|
|
354
|
+
if handler_paths:
|
|
321
355
|
progress.update(
|
|
322
356
|
manifest_task,
|
|
323
357
|
description=f"[green]✓ Generated {len(handler_paths)} handlers and manifest",
|
|
324
358
|
)
|
|
359
|
+
elif manifest_resources:
|
|
360
|
+
progress.update(
|
|
361
|
+
manifest_task,
|
|
362
|
+
description=f"[green]✓ Generated manifest with {len(manifest_resources)} resources",
|
|
363
|
+
)
|
|
325
364
|
else:
|
|
326
365
|
progress.update(
|
|
327
366
|
manifest_task,
|
|
328
|
-
description="[yellow]⚠ No
|
|
367
|
+
description="[yellow]⚠ No resources detected",
|
|
329
368
|
)
|
|
330
369
|
|
|
331
370
|
except (ImportError, SyntaxError) as e:
|
{tetra_rp-0.20.1 → tetra_rp-0.23.0}/src/tetra_rp/cli/commands/build_utils/lb_handler_generator.py
RENAMED
|
@@ -40,9 +40,6 @@ ROUTE_REGISTRY = {{
|
|
|
40
40
|
{registry}
|
|
41
41
|
}}
|
|
42
42
|
|
|
43
|
-
# Module-level state for /manifest endpoint
|
|
44
|
-
_state_client: Optional[StateManagerClient] = None
|
|
45
|
-
|
|
46
43
|
|
|
47
44
|
# Lifespan context manager for startup/shutdown
|
|
48
45
|
@asynccontextmanager
|
|
@@ -51,43 +48,46 @@ async def lifespan(app: FastAPI):
|
|
|
51
48
|
# Startup
|
|
52
49
|
logger.info("Starting {resource_name} endpoint")
|
|
53
50
|
|
|
54
|
-
# Check if this is the mothership and
|
|
51
|
+
# Check if this is the mothership and run reconciliation
|
|
52
|
+
# Note: Resources are now provisioned upfront by the CLI during deployment.
|
|
53
|
+
# This background task runs reconciliation on mothership startup to ensure
|
|
54
|
+
# all resources are still deployed and in sync with the manifest.
|
|
55
55
|
try:
|
|
56
56
|
from tetra_rp.runtime.mothership_provisioner import (
|
|
57
57
|
is_mothership,
|
|
58
|
-
|
|
58
|
+
reconcile_children,
|
|
59
59
|
get_mothership_url,
|
|
60
60
|
)
|
|
61
61
|
from tetra_rp.runtime.state_manager_client import StateManagerClient
|
|
62
62
|
|
|
63
63
|
if is_mothership():
|
|
64
64
|
logger.info("=" * 60)
|
|
65
|
-
logger.info("Mothership detected - Starting
|
|
66
|
-
logger.info("
|
|
65
|
+
logger.info("Mothership detected - Starting reconciliation task")
|
|
66
|
+
logger.info("Resources are provisioned upfront by the CLI")
|
|
67
|
+
logger.info("This task ensures all resources remain in sync")
|
|
67
68
|
logger.info("=" * 60)
|
|
68
69
|
try:
|
|
69
70
|
mothership_url = get_mothership_url()
|
|
70
71
|
logger.info(f"Mothership URL: {{mothership_url}}")
|
|
71
72
|
|
|
72
|
-
# Initialize State Manager client
|
|
73
|
+
# Initialize State Manager client for reconciliation
|
|
73
74
|
state_client = StateManagerClient()
|
|
74
|
-
global _state_client
|
|
75
|
-
_state_client = state_client
|
|
76
75
|
|
|
77
|
-
# Spawn background
|
|
76
|
+
# Spawn background reconciliation task (non-blocking)
|
|
77
|
+
# This will verify all resources from manifest are deployed
|
|
78
78
|
manifest_path = Path(__file__).parent / "flash_manifest.json"
|
|
79
79
|
task = asyncio.create_task(
|
|
80
|
-
|
|
80
|
+
reconcile_children(manifest_path, mothership_url, state_client)
|
|
81
81
|
)
|
|
82
82
|
# Add error callback to catch and log background task exceptions
|
|
83
83
|
task.add_done_callback(
|
|
84
|
-
lambda t: logger.error(f"
|
|
84
|
+
lambda t: logger.error(f"Reconciliation task failed: {{t.exception()}}")
|
|
85
85
|
if t.exception()
|
|
86
86
|
else None
|
|
87
87
|
)
|
|
88
88
|
|
|
89
89
|
except Exception as e:
|
|
90
|
-
logger.error(f"Failed to start
|
|
90
|
+
logger.error(f"Failed to start reconciliation task: {{e}}")
|
|
91
91
|
# Don't fail startup - continue serving traffic
|
|
92
92
|
|
|
93
93
|
except ImportError:
|
|
@@ -117,58 +117,6 @@ def ping():
|
|
|
117
117
|
return {{"status": "healthy"}}
|
|
118
118
|
|
|
119
119
|
|
|
120
|
-
# Manifest endpoint for service discovery
|
|
121
|
-
@app.get("/manifest")
|
|
122
|
-
async def manifest():
|
|
123
|
-
"""Return complete authoritative manifest for service discovery.
|
|
124
|
-
|
|
125
|
-
Fetches the full manifest from State Manager, allowing child endpoints
|
|
126
|
-
to synchronize their configuration.
|
|
127
|
-
|
|
128
|
-
Returns:
|
|
129
|
-
dict: Complete manifest with version, generated_at, project_name,
|
|
130
|
-
function_registry, resources, and routes
|
|
131
|
-
"""
|
|
132
|
-
try:
|
|
133
|
-
import os
|
|
134
|
-
from tetra_rp.runtime.mothership_provisioner import is_mothership
|
|
135
|
-
|
|
136
|
-
# Only mothership serves manifest
|
|
137
|
-
if not is_mothership():
|
|
138
|
-
return {{"error": "Only mothership serves manifest"}}, 403
|
|
139
|
-
|
|
140
|
-
# Check state client initialized
|
|
141
|
-
global _state_client
|
|
142
|
-
if _state_client is None:
|
|
143
|
-
return {{"error": "State Manager not initialized"}}, 500
|
|
144
|
-
|
|
145
|
-
# Get mothership ID
|
|
146
|
-
mothership_id = os.getenv("RUNPOD_ENDPOINT_ID")
|
|
147
|
-
if not mothership_id:
|
|
148
|
-
return {{"error": "RUNPOD_ENDPOINT_ID not set"}}, 500
|
|
149
|
-
|
|
150
|
-
# Fetch persisted manifest from State Manager (single source of truth)
|
|
151
|
-
persisted_manifest = await _state_client.get_persisted_manifest(mothership_id)
|
|
152
|
-
|
|
153
|
-
# First boot: no manifest yet, return minimal structure
|
|
154
|
-
if persisted_manifest is None:
|
|
155
|
-
return {{
|
|
156
|
-
"version": "1.0",
|
|
157
|
-
"generated_at": "",
|
|
158
|
-
"project_name": "",
|
|
159
|
-
"function_registry": {{}},
|
|
160
|
-
"resources": {{}},
|
|
161
|
-
"routes": {{}}
|
|
162
|
-
}}
|
|
163
|
-
|
|
164
|
-
# Return complete manifest
|
|
165
|
-
return persisted_manifest
|
|
166
|
-
|
|
167
|
-
except Exception as e:
|
|
168
|
-
logger.error(f"Failed to get manifest: {{e}}")
|
|
169
|
-
return {{"error": str(e)}}, 500
|
|
170
|
-
|
|
171
|
-
|
|
172
120
|
if __name__ == "__main__":
|
|
173
121
|
import uvicorn
|
|
174
122
|
# Local development server for testing
|