Skip to content

Commit 836c1fd

Browse files
authored
Merge branch 'main' into add/chunk-file-uploads
2 parents f672a36 + 359897c commit 836c1fd

23 files changed

Lines changed: 2503 additions & 536 deletions

api/localprovider_pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ dependencies = [
3232
"soundfile==0.13.1",
3333
"tensorboardX==2.6.2.2",
3434
"timm==1.0.15",
35-
"transformerlab==0.1.24",
35+
"transformerlab==0.1.25",
3636
"transformerlab-inference==0.2.52",
3737
"transformers==4.57.1",
3838
"wandb==0.23.1",

api/pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ dependencies = [
2424
"python-dotenv==1.1.0",
2525
"python-multipart==0.0.20",
2626
"sqlalchemy[asyncio]==2.0.40",
27-
"transformerlab==0.1.24",
27+
"transformerlab==0.1.25",
2828
"transformerlab-inference==0.2.52",
2929
"uvicorn==0.35.0",
3030
"watchfiles==1.0.5",

api/test/api/test_job_save_to_registry.py

Lines changed: 109 additions & 206 deletions
Large diffs are not rendered by default.

api/transformerlab/routers/experiment/jobs.py

Lines changed: 101 additions & 117 deletions
Large diffs are not rendered by default.

api/transformerlab/schemas/compute_providers.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,18 @@
66
from transformerlab.shared.models.models import ProviderType, AcceleratorType
77

88

9+
class ProviderResourceGroup(BaseModel):
10+
"""Schema for provider resource group configuration."""
11+
12+
id: str = Field(..., min_length=1, max_length=100)
13+
name: str = Field(..., min_length=1, max_length=100)
14+
cpus: Optional[str] = None
15+
memory: Optional[str] = None
16+
disk_space: Optional[str] = None
17+
accelerators: Optional[str] = None
18+
num_nodes: Optional[int] = None
19+
20+
921
class ProviderConfigBase(BaseModel):
1022
"""Base schema for provider configuration."""
1123

@@ -34,6 +46,7 @@ class ProviderConfigBase(BaseModel):
3446

3547
# Accelerators supported by this provider
3648
supported_accelerators: Optional[List[AcceleratorType]] = Field(default=None)
49+
resource_groups: Optional[List[ProviderResourceGroup]] = Field(default=None)
3750

3851
# Additional provider-specific config
3952
extra_config: Dict[str, Any] = Field(default_factory=dict)

api/transformerlab/services/compute_provider/cluster_naming.py

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,32 @@
33
from typing import Optional
44

55

6+
# Cap cluster names to 41 characters to stay withing the limit of
7+
# the strictest provider (dstack).
8+
# This means capping the basename portion to 28 characters (use 25 to be safe)
9+
# to leave room for a ``-job-<short_id>`` suffix (13 chars).
10+
_MAX_BASENAME_LENGTH = 25
11+
12+
613
def sanitize_cluster_basename(base_name: Optional[str]) -> str:
7-
"""Return a filesystem-safe cluster base name."""
14+
"""Return a cluster base name that is safe across compute providers.
15+
16+
Some providers have restrictions on resource names.
17+
Sanitize generated name so the result is:
18+
- lowercased
19+
- underscores are replaced with hyphens
20+
- name is guaranteed to start with a letter
21+
- no longer than 41 characters
22+
"""
823
if not base_name:
924
return "remote-template"
10-
normalized = "".join(ch if ch.isalnum() or ch in ("-", "_") else "-" for ch in base_name.strip())
11-
normalized = normalized.strip("-_")
12-
return normalized or "remote-template"
25+
lowered = base_name.strip().lower()
26+
normalized = "".join(ch if (ch.isalnum() and ch.isascii()) or ch == "-" else "-" for ch in lowered)
27+
normalized = normalized.strip("-")
28+
if not normalized:
29+
return "remote-template"
30+
if not normalized[0].isalpha():
31+
normalized = f"t-{normalized}"
32+
if len(normalized) > _MAX_BASENAME_LENGTH:
33+
normalized = normalized[:_MAX_BASENAME_LENGTH].rstrip("-")
34+
return normalized

api/transformerlab/services/compute_provider/launch_sweep.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -278,12 +278,18 @@ async def launch_sweep_jobs(
278278
if azure_sas:
279279
env_vars["AZURE_STORAGE_SAS_TOKEN"] = azure_sas
280280

281-
if request.file_mounts is True and request.task_id:
282-
setup_commands.append(COPY_FILE_MOUNTS_SETUP)
283-
284281
if provider.type == ProviderType.RUNPOD.value:
285282
setup_commands.append("curl -LsSf https://astral.sh/uv/install.sh | sh")
286283

284+
if provider.type != ProviderType.LOCAL.value:
285+
setup_commands.append("pip install -q transformerlab")
286+
287+
if request.enable_profiling_torch:
288+
setup_commands.append("pip install -q torch")
289+
290+
if request.file_mounts is True and request.task_id:
291+
setup_commands.append(COPY_FILE_MOUNTS_SETUP)
292+
287293
if request.github_repo_url:
288294
workspace_dir = await get_workspace_dir()
289295
github_pat = await read_github_pat_from_workspace(workspace_dir, user_id=user_id)

api/transformerlab/services/compute_provider/launch_template.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -254,8 +254,6 @@ async def launch_template_on_provider(
254254
if azure_sas:
255255
env_vars["AZURE_STORAGE_SAS_TOKEN"] = azure_sas
256256

257-
if request.file_mounts is True and request.task_id:
258-
setup_commands.append(COPY_FILE_MOUNTS_SETUP)
259257
# Ensure transformerlab SDK is available on remote machines for live_status tracking and other helpers.
260258
# This runs after AWS credentials are configured so we have access to any remote storage if needed.
261259
if provider.type != ProviderType.LOCAL.value:
@@ -264,6 +262,8 @@ async def launch_template_on_provider(
264262
# Install torch as well if torch profiler is enabled
265263
if request.enable_profiling_torch:
266264
setup_commands.append("pip install -q torch")
265+
if request.file_mounts is True and request.task_id:
266+
setup_commands.append(COPY_FILE_MOUNTS_SETUP)
267267
# For RunPod providers, ensure uv is available and configured to use the
268268
# system Python. This allows user commands to invoke `uv` directly.
269269
if provider.type == ProviderType.RUNPOD.value:

api/transformerlab/services/remote_provider_queue.py

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import logging
44
import os
55
import uuid
6-
from datetime import datetime, timezone
6+
from datetime import datetime
77
from typing import Optional
88

99
from pydantic import BaseModel
@@ -98,23 +98,15 @@ async def _poll_pending_remote_entries() -> list[JobQueue]:
9898
async def _mark_entry_dispatched(entry_id: str) -> None:
9999
"""Transition a job_queue row from PENDING to DISPATCHED."""
100100
async with async_session() as session:
101-
stmt = (
102-
update(JobQueue)
103-
.where(JobQueue.id == entry_id)
104-
.values(status="DISPATCHED", updated_at=datetime.now(timezone.utc))
105-
)
101+
stmt = update(JobQueue).where(JobQueue.id == entry_id).values(status="DISPATCHED", updated_at=datetime.utcnow())
106102
await session.execute(stmt)
107103
await session.commit()
108104

109105

110106
async def _mark_entry_failed(entry_id: str) -> None:
111107
"""Transition a job_queue row to FAILED (could not reconstruct work item)."""
112108
async with async_session() as session:
113-
stmt = (
114-
update(JobQueue)
115-
.where(JobQueue.id == entry_id)
116-
.values(status="FAILED", updated_at=datetime.now(timezone.utc))
117-
)
109+
stmt = update(JobQueue).where(JobQueue.id == entry_id).values(status="FAILED", updated_at=datetime.utcnow())
118110
await session.execute(stmt)
119111
await session.commit()
120112

0 commit comments

Comments
 (0)