tetra-rp 0.12.0__py3-none-any.whl → 0.14.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 tetra-rp might be problematic. Click here for more details.

@@ -0,0 +1,347 @@
1
+ """Project skeleton creation utilities."""
2
+
3
+ from pathlib import Path
4
+ from typing import List
5
+
6
+ # Template files content
7
+ MAIN_PY_TEMPLATE = '''"""
8
+ Flash Application - Flash Server
9
+
10
+ This is the main entry point for your Flash application.
11
+ It runs a FastAPI server that coordinates GPU workers.
12
+ """
13
+
14
+ import asyncio
15
+ from fastapi import FastAPI
16
+ from pydantic import BaseModel
17
+ from dotenv import load_dotenv
18
+
19
+ from workers import ExampleWorker
20
+
21
+ # Load environment variables
22
+ load_dotenv()
23
+
24
+ # Create FastAPI app
25
+ app = FastAPI(title="Flash Application")
26
+
27
+
28
+ class ProcessRequest(BaseModel):
29
+ """Request model for processing endpoint."""
30
+ data: str
31
+
32
+
33
+ @app.get("/")
34
+ def home():
35
+ """Health check endpoint."""
36
+ return {"status": "ok", "message": "Flash application running"}
37
+
38
+
39
+ @app.get("/health")
40
+ def health():
41
+ """Health check endpoint."""
42
+ return {"healthy": True}
43
+
44
+
45
+ @app.post("/process")
46
+ async def process(request: ProcessRequest):
47
+ """
48
+ Process data using GPU worker.
49
+
50
+ Example request:
51
+ {
52
+ "data": "test input"
53
+ }
54
+ """
55
+ # Instantiate worker
56
+ worker = ExampleWorker()
57
+
58
+ # Call worker's process method
59
+ result = await worker.process({"input": request.data})
60
+
61
+ return result
62
+
63
+
64
+ if __name__ == "__main__":
65
+ import uvicorn
66
+ uvicorn.run(app, host="localhost", port=8888)
67
+ '''
68
+
69
+ WORKER_EXAMPLE_PY_TEMPLATE = '''"""
70
+ Example GPU Worker
71
+
72
+ This is an example of a GPU worker class that can be deployed
73
+ to RunPod serverless endpoints.
74
+ """
75
+
76
+ from tetra_rp import remote, LiveServerless
77
+
78
+
79
+ # Configure GPU resource
80
+ config = LiveServerless(
81
+ name="example_worker",
82
+ workersMax=3,
83
+ )
84
+
85
+
86
+ @remote(config)
87
+ class ExampleWorker:
88
+ """Example GPU worker for processing tasks."""
89
+
90
+ def __init__(self):
91
+ """Initialize the worker."""
92
+ print("ExampleWorker initialized")
93
+
94
+ def process(self, input_data: dict) -> dict:
95
+ """
96
+ Process input data and return result.
97
+
98
+ Args:
99
+ input_data: Dictionary with input parameters
100
+
101
+ Returns:
102
+ Dictionary with processing results
103
+ """
104
+ # Your GPU processing logic here
105
+ result = {
106
+ "status": "success",
107
+ "input": input_data,
108
+ "output": f"Processed: {input_data}"
109
+ }
110
+
111
+ return result
112
+ '''
113
+
114
+ WORKERS_INIT_PY_TEMPLATE = '''"""GPU Workers package."""
115
+
116
+ from .example_worker import ExampleWorker
117
+
118
+ __all__ = ["ExampleWorker"]
119
+ '''
120
+
121
+ ENV_EXAMPLE_TEMPLATE = """# RunPod API Configuration
122
+ RUNPOD_API_KEY=your_runpod_api_key_here
123
+
124
+ # Development settings
125
+ DEBUG=false
126
+ LOG_LEVEL=INFO
127
+ """
128
+
129
+ REQUIREMENTS_TXT_TEMPLATE = """# Core dependencies for Flash
130
+ tetra-rp>=0.12.0
131
+ fastapi>=0.104.0
132
+ uvicorn[standard]>=0.24.0
133
+ python-dotenv>=1.0.0
134
+ pydantic>=2.0.0
135
+ aiohttp>=3.9.0
136
+ """
137
+
138
+ GITIGNORE_TEMPLATE = """# Python
139
+ __pycache__/
140
+ *.py[cod]
141
+ *$py.class
142
+ *.so
143
+ .Python
144
+ env/
145
+ venv/
146
+ ENV/
147
+ build/
148
+ develop-eggs/
149
+ dist/
150
+ downloads/
151
+ eggs/
152
+ .eggs/
153
+ lib/
154
+ lib64/
155
+ parts/
156
+ sdist/
157
+ var/
158
+ wheels/
159
+ *.egg-info/
160
+ .installed.cfg
161
+ *.egg
162
+
163
+ # Environment
164
+ .env
165
+ .venv
166
+
167
+ # IDE
168
+ .vscode/
169
+ .idea/
170
+ *.swp
171
+ *.swo
172
+ *~
173
+
174
+ # Tetra
175
+ .tetra/
176
+
177
+ # OS
178
+ .DS_Store
179
+ Thumbs.db
180
+ """
181
+
182
+ FLASHIGNORE_TEMPLATE = """# Flash build ignores
183
+ # Similar to .gitignore but specifically for flash build command
184
+
185
+ # Version control
186
+ .git/
187
+ .gitignore
188
+
189
+ # Python artifacts
190
+ __pycache__/
191
+ *.pyc
192
+ *.pyo
193
+ *.pyd
194
+ .Python
195
+
196
+ # Virtual environments
197
+ .env
198
+ .venv/
199
+ env/
200
+ venv/
201
+ ENV/
202
+
203
+ # Build artifacts
204
+ .build/
205
+ .tetra/
206
+ *.tar.gz
207
+ *.egg-info/
208
+ dist/
209
+ build/
210
+
211
+ # IDE files
212
+ .vscode/
213
+ .idea/
214
+ *.swp
215
+ *.swo
216
+ *~
217
+
218
+ # OS files
219
+ .DS_Store
220
+ Thumbs.db
221
+
222
+ # Test files
223
+ tests/
224
+ test_*.py
225
+ *_test.py
226
+
227
+ # Documentation (optional - comment out to include)
228
+ # docs/
229
+ # *.md
230
+
231
+ # Logs
232
+ *.log
233
+ """
234
+
235
+ README_TEMPLATE = """# {{project_name}}
236
+
237
+ Flash application with Flash Server and GPU workers.
238
+
239
+ ## Setup
240
+
241
+ 1. Activate the conda environment:
242
+ ```bash
243
+ conda activate {{project_name}}
244
+ ```
245
+
246
+ 2. Configure your RunPod API key:
247
+ ```bash
248
+ cp .env.example .env
249
+ # Edit .env and add your RUNPOD_API_KEY
250
+ ```
251
+
252
+ 3. Run the development server:
253
+ ```bash
254
+ flash run
255
+ ```
256
+
257
+ ## Project Structure
258
+
259
+ ```
260
+ {{project_name}}/
261
+ ├── main.py # Flash Server (FastAPI)
262
+ ├── workers/ # GPU workers
263
+ │ ├── __init__.py
264
+ │ └── example_worker.py
265
+ ├── .env.example # Environment variables template
266
+ ├── requirements.txt # Python dependencies
267
+ └── README.md # This file
268
+ ```
269
+
270
+ ## Development
271
+
272
+ The Flash Server runs on `localhost:8888` and coordinates GPU workers.
273
+
274
+ ### Adding New Workers
275
+
276
+ 1. Create a new file in `workers/` directory
277
+ 2. Define a class with `@remote` decorator
278
+ 3. Import it in `workers/__init__.py`
279
+ 4. Use it in `main.py`
280
+
281
+ Example:
282
+ ```python
283
+ from tetra_rp import remote, LiveServerless
284
+
285
+ config = LiveServerless(name="my_worker", workersMax=3)
286
+
287
+ @remote(config)
288
+ class MyWorker:
289
+ def process(self, data):
290
+ return {{"result": f"Processed: {{data}}"}}
291
+ ```
292
+
293
+ ## Deployment
294
+
295
+ Deploy to production:
296
+ ```bash
297
+ flash deploy send production
298
+ ```
299
+
300
+ ## Documentation
301
+
302
+ - [Flash CLI Docs](./docs/)
303
+ - [Tetra Documentation](https://docs.tetra.dev)
304
+ """
305
+
306
+
307
+ def create_project_skeleton(project_dir: Path, force: bool = False) -> List[str]:
308
+ """
309
+ Create Flash project skeleton.
310
+
311
+ Args:
312
+ project_dir: Project directory path
313
+ force: Overwrite existing files
314
+
315
+ Returns:
316
+ List of created file paths
317
+ """
318
+ created_files = []
319
+
320
+ # Define project structure
321
+ files_to_create = {
322
+ "main.py": MAIN_PY_TEMPLATE,
323
+ "workers/__init__.py": WORKERS_INIT_PY_TEMPLATE,
324
+ "workers/example_worker.py": WORKER_EXAMPLE_PY_TEMPLATE,
325
+ ".env.example": ENV_EXAMPLE_TEMPLATE,
326
+ "requirements.txt": REQUIREMENTS_TXT_TEMPLATE,
327
+ ".gitignore": GITIGNORE_TEMPLATE,
328
+ ".flashignore": FLASHIGNORE_TEMPLATE,
329
+ "README.md": README_TEMPLATE.format(project_name=project_dir.name),
330
+ }
331
+
332
+ # Create files
333
+ for relative_path, content in files_to_create.items():
334
+ file_path = project_dir / relative_path
335
+
336
+ # Create parent directories if needed
337
+ file_path.parent.mkdir(parents=True, exist_ok=True)
338
+
339
+ # Skip existing files unless force is True
340
+ if file_path.exists() and not force:
341
+ continue
342
+
343
+ # Write file
344
+ file_path.write_text(content)
345
+ created_files.append(str(file_path.relative_to(project_dir)))
346
+
347
+ return created_files
tetra_rp/client.py CHANGED
@@ -1,3 +1,4 @@
1
+ import os
1
2
  import inspect
2
3
  import logging
3
4
  from functools import wraps
@@ -15,47 +16,69 @@ def remote(
15
16
  dependencies: Optional[List[str]] = None,
16
17
  system_dependencies: Optional[List[str]] = None,
17
18
  accelerate_downloads: bool = True,
18
- hf_models_to_cache: Optional[List[str]] = None,
19
+ local: bool = False,
19
20
  **extra,
20
21
  ):
21
22
  """
22
23
  Decorator to enable dynamic resource provisioning and dependency management for serverless functions.
23
24
 
24
25
  This decorator allows a function to be executed in a remote serverless environment, with support for
25
- dynamic resource provisioning and installation of required dependencies.
26
+ dynamic resource provisioning and installation of required dependencies. It can also bypass remote
27
+ execution entirely for local testing.
26
28
 
27
29
  Args:
28
30
  resource_config (ServerlessResource): Configuration object specifying the serverless resource
29
- to be provisioned or used.
31
+ to be provisioned or used. Not used when local=True.
30
32
  dependencies (List[str], optional): A list of pip package names to be installed in the remote
31
- environment before executing the function. Defaults to None.
33
+ environment before executing the function. Not used when local=True. Defaults to None.
32
34
  system_dependencies (List[str], optional): A list of system packages to be installed in the remote
33
- environment before executing the function. Defaults to None.
35
+ environment before executing the function. Not used when local=True. Defaults to None.
34
36
  accelerate_downloads (bool, optional): Enable download acceleration for dependencies and models.
35
- Defaults to True.
36
- hf_models_to_cache (List[str], optional): List of HuggingFace model IDs to pre-cache using
37
- download acceleration. Defaults to None.
37
+ Only applies to remote execution. Defaults to True.
38
+ local (bool, optional): Execute function/class locally instead of provisioning remote servers.
39
+ Returns the unwrapped function/class for direct local execution. Users must ensure all required
40
+ dependencies are already installed in their local environment. Defaults to False.
38
41
  extra (dict, optional): Additional parameters for the execution of the resource. Defaults to an empty dict.
39
42
 
40
43
  Returns:
41
- Callable: A decorator that wraps the target function, enabling remote execution with the
42
- specified resource configuration and dependencies.
44
+ Callable: A decorator that wraps the target function, enabling remote execution with the specified
45
+ resource configuration and dependencies, or returns the unwrapped function/class for local execution.
43
46
 
44
47
  Example:
45
48
  ```python
49
+ # Remote execution (production)
46
50
  @remote(
47
51
  resource_config=my_resource_config,
48
52
  dependencies=["numpy", "pandas"],
49
53
  accelerate_downloads=True,
50
- hf_models_to_cache=["gpt2", "bert-base-uncased"]
51
54
  )
52
55
  async def my_function(data):
53
56
  # Function logic here
54
57
  pass
58
+
59
+ # Local execution (testing/development)
60
+ # Note: Ensure numpy and pandas are installed locally first
61
+ @remote(
62
+ resource_config=my_resource_config,
63
+ dependencies=["numpy", "pandas"], # Only used for remote execution
64
+ local=True,
65
+ )
66
+ async def my_test_function(data):
67
+ # Runs locally - dependencies must be pre-installed
68
+ pass
55
69
  ```
56
70
  """
57
71
 
58
72
  def decorator(func_or_class):
73
+ if os.getenv("RUNPOD_POD_ID") or os.getenv("RUNPOD_ENDPOINT_ID"):
74
+ # Worker mode when running on RunPod platform
75
+ return func_or_class
76
+
77
+ # Local execution mode - execute without provisioning remote servers
78
+ if local:
79
+ return func_or_class
80
+
81
+ # Remote execution mode
59
82
  if inspect.isclass(func_or_class):
60
83
  # Handle class decoration
61
84
  return create_remote_class(
@@ -64,11 +87,10 @@ def remote(
64
87
  dependencies,
65
88
  system_dependencies,
66
89
  accelerate_downloads,
67
- hf_models_to_cache,
68
90
  extra,
69
91
  )
70
92
  else:
71
- # Handle function decoration (unchanged)
93
+ # Handle function decoration
72
94
  @wraps(func_or_class)
73
95
  async def wrapper(*args, **kwargs):
74
96
  resource_manager = ResourceManager()
@@ -82,7 +104,6 @@ def remote(
82
104
  dependencies,
83
105
  system_dependencies,
84
106
  accelerate_downloads,
85
- hf_models_to_cache,
86
107
  *args,
87
108
  **kwargs,
88
109
  )
tetra_rp/config.py ADDED
@@ -0,0 +1,29 @@
1
+ """Configuration management for tetra-rp CLI."""
2
+
3
+ from pathlib import Path
4
+ from typing import NamedTuple
5
+
6
+
7
+ class TetraPaths(NamedTuple):
8
+ """Paths for tetra-rp configuration and data."""
9
+
10
+ tetra_dir: Path
11
+ config_file: Path
12
+ deployments_file: Path
13
+
14
+ def ensure_tetra_dir(self) -> None:
15
+ """Ensure the .tetra directory exists."""
16
+ self.tetra_dir.mkdir(exist_ok=True)
17
+
18
+
19
+ def get_paths() -> TetraPaths:
20
+ """Get standardized paths for tetra-rp configuration."""
21
+ tetra_dir = Path.cwd() / ".tetra"
22
+ config_file = tetra_dir / "config.json"
23
+ deployments_file = tetra_dir / "deployments.json"
24
+
25
+ return TetraPaths(
26
+ tetra_dir=tetra_dir,
27
+ config_file=config_file,
28
+ deployments_file=deployments_file,
29
+ )
@@ -16,6 +16,9 @@ class CpuInstanceType(str, Enum):
16
16
  - cpu5g: Not available
17
17
  """
18
18
 
19
+ ANY = "any"
20
+ """Any CPU"""
21
+
19
22
  # 3rd Generation General Purpose (RAM multiplier: 4.0)
20
23
 
21
24
  CPU3G_1_4 = "cpu3g-1-4"
@@ -58,6 +61,11 @@ class CpuInstanceType(str, Enum):
58
61
  CPU5C_8_16 = "cpu5c-8-16"
59
62
  """8 vCPU, 16GB RAM, max 120GB container disk"""
60
63
 
64
+ @classmethod
65
+ def all(cls) -> List["CpuInstanceType"]:
66
+ """Returns all CPU Instance Types."""
67
+ return [c for c in cls if c != cls.ANY]
68
+
61
69
 
62
70
  def calculate_max_disk_size(instance_type: CpuInstanceType) -> int:
63
71
  """
@@ -104,6 +112,7 @@ def calculate_max_disk_size(instance_type: CpuInstanceType) -> int:
104
112
  CPU_INSTANCE_DISK_LIMITS = {
105
113
  instance_type: calculate_max_disk_size(instance_type)
106
114
  for instance_type in CpuInstanceType
115
+ if instance_type != CpuInstanceType.ANY
107
116
  }
108
117
 
109
118
 
@@ -6,7 +6,7 @@ This module contains all CPU-related serverless functionality, separate from GPU
6
6
 
7
7
  from typing import List, Optional
8
8
 
9
- from pydantic import field_serializer, model_validator
9
+ from pydantic import field_serializer, model_validator, field_validator
10
10
 
11
11
  from .cpu import (
12
12
  CpuInstanceType,
@@ -105,7 +105,7 @@ class CpuServerlessEndpoint(CpuEndpointMixin, ServerlessEndpoint):
105
105
  Represents a CPU-only serverless endpoint distinct from a live serverless.
106
106
  """
107
107
 
108
- instanceIds: Optional[List[CpuInstanceType]] = [CpuInstanceType.CPU3G_2_8]
108
+ instanceIds: Optional[List[CpuInstanceType]] = [CpuInstanceType.ANY]
109
109
 
110
110
  def _create_new_template(self) -> PodTemplate:
111
111
  """Create a new PodTemplate with CPU-appropriate disk sizing."""
@@ -133,6 +133,14 @@ class CpuServerlessEndpoint(CpuEndpointMixin, ServerlessEndpoint):
133
133
  # Apply CPU-specific disk sizing
134
134
  self._apply_cpu_disk_sizing(self.template)
135
135
 
136
+ @field_validator("instanceIds")
137
+ @classmethod
138
+ def validate_cpus(cls, value: List[CpuInstanceType]) -> List[CpuInstanceType]:
139
+ """Expand ANY to all GPU groups"""
140
+ if value == [CpuInstanceType.ANY]:
141
+ return CpuInstanceType.all()
142
+ return value
143
+
136
144
  @model_validator(mode="after")
137
145
  def set_serverless_template(self):
138
146
  # Sync CPU-specific fields first
tetra_rp/execute_class.py CHANGED
@@ -203,7 +203,6 @@ def create_remote_class(
203
203
  dependencies: Optional[List[str]],
204
204
  system_dependencies: Optional[List[str]],
205
205
  accelerate_downloads: bool,
206
- hf_models_to_cache: Optional[List[str]],
207
206
  extra: dict,
208
207
  ):
209
208
  """
@@ -222,7 +221,6 @@ def create_remote_class(
222
221
  self._dependencies = dependencies or []
223
222
  self._system_dependencies = system_dependencies or []
224
223
  self._accelerate_downloads = accelerate_downloads
225
- self._hf_models_to_cache = hf_models_to_cache
226
224
  self._extra = extra
227
225
  self._constructor_args = args
228
226
  self._constructor_kwargs = kwargs
@@ -307,7 +305,6 @@ def create_remote_class(
307
305
  dependencies=self._dependencies,
308
306
  system_dependencies=self._system_dependencies,
309
307
  accelerate_downloads=self._accelerate_downloads,
310
- hf_models_to_cache=self._hf_models_to_cache,
311
308
  instance_id=self._instance_id,
312
309
  create_new_instance=not hasattr(
313
310
  self, "_stub"
@@ -81,10 +81,6 @@ class FunctionRequest(BaseModel):
81
81
  default=True,
82
82
  description="Enable download acceleration for dependencies and models",
83
83
  )
84
- hf_models_to_cache: Optional[List[str]] = Field(
85
- default=None,
86
- description="List of HuggingFace model IDs to pre-cache using acceleration",
87
- )
88
84
 
89
85
  @model_validator(mode="after")
90
86
  def validate_execution_requirements(self) -> "FunctionRequest":
@@ -68,7 +68,6 @@ class LiveServerlessStub(RemoteExecutorStub):
68
68
  dependencies,
69
69
  system_dependencies,
70
70
  accelerate_downloads,
71
- hf_models_to_cache,
72
71
  *args,
73
72
  **kwargs,
74
73
  ):
@@ -79,7 +78,6 @@ class LiveServerlessStub(RemoteExecutorStub):
79
78
  "dependencies": dependencies,
80
79
  "system_dependencies": system_dependencies,
81
80
  "accelerate_downloads": accelerate_downloads,
82
- "hf_models_to_cache": hf_models_to_cache,
83
81
  }
84
82
 
85
83
  # Thread-safe cache access
@@ -110,7 +108,7 @@ class LiveServerlessStub(RemoteExecutorStub):
110
108
 
111
109
  if response.stdout:
112
110
  for line in response.stdout.splitlines():
113
- log.info(f"Remote | {line}")
111
+ print(line)
114
112
 
115
113
  if response.success:
116
114
  if response.result is None:
@@ -31,7 +31,6 @@ def _create_live_serverless_stub(resource, **extra):
31
31
  dependencies,
32
32
  system_dependencies,
33
33
  accelerate_downloads,
34
- hf_models_to_cache,
35
34
  *args,
36
35
  **kwargs,
37
36
  ) -> dict:
@@ -43,7 +42,6 @@ def _create_live_serverless_stub(resource, **extra):
43
42
  dependencies,
44
43
  system_dependencies,
45
44
  accelerate_downloads,
46
- hf_models_to_cache,
47
45
  *args,
48
46
  **kwargs,
49
47
  )
@@ -78,7 +76,6 @@ def _(resource, **extra):
78
76
  dependencies,
79
77
  system_dependencies,
80
78
  accelerate_downloads,
81
- hf_models_to_cache,
82
79
  *args,
83
80
  **kwargs,
84
81
  ) -> dict:
@@ -103,7 +100,6 @@ def _(resource, **extra):
103
100
  dependencies,
104
101
  system_dependencies,
105
102
  accelerate_downloads,
106
- hf_models_to_cache,
107
103
  *args,
108
104
  **kwargs,
109
105
  ) -> dict:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tetra_rp
3
- Version: 0.12.0
3
+ Version: 0.14.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
@@ -14,6 +14,10 @@ Requires-Dist: cloudpickle>=3.1.1
14
14
  Requires-Dist: runpod
15
15
  Requires-Dist: python-dotenv>=1.0.0
16
16
  Requires-Dist: pydantic>=2.0.0
17
+ Requires-Dist: rich>=14.0.0
18
+ Requires-Dist: typer>=0.12.0
19
+ Requires-Dist: questionary>=2.0.0
20
+ Requires-Dist: pathspec>=0.11.0
17
21
 
18
22
  # Tetra: Serverless computing for AI workloads
19
23
 
@@ -359,6 +363,7 @@ if __name__ == "__main__":
359
363
  ```python
360
364
  import asyncio
361
365
  from tetra_rp import remote, LiveServerless, GpuGroup, PodTemplate
366
+ import base64
362
367
 
363
368
  # Advanced GPU configuration with consolidated template overrides
364
369
  sd_config = LiveServerless(