Skip to content

Commit 1b147a2

Browse files
authored
Merge pull request #1841 from transformerlab/fix/local-model-vllm
Use tlab registry models with interactive tasks
2 parents 4fea393 + 1f86d96 commit 1b147a2

13 files changed

Lines changed: 241 additions & 31 deletions

File tree

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.26",
35+
"transformerlab==0.1.27",
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.26",
27+
"transformerlab==0.1.27",
2828
"transformerlab-inference==0.2.52",
2929
"uvicorn==0.35.0",
3030
"watchfiles==1.0.5",

api/transformerlab/galleries/channels/stable/latest/interactive-gallery.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@
104104
{
105105
"field_name": "Model Name",
106106
"env_var": "MODEL_NAME",
107-
"field_type": "str",
107+
"field_type": "model",
108108
"required": true,
109109
"placeholder": "e.g. meta-llama/Llama-2-7b-chat-hf",
110110
"help_text": "HuggingFace model identifier"

api/transformerlab/galleries/channels/stable/latest/manifest.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
2-
"bundle_version": "20260421193056",
2+
"bundle_version": "20260424160054",
33
"channel": "stable",
4-
"released_at": "2026-04-21T19:30:56.442725+00:00",
4+
"released_at": "2026-04-24T16:00:54.223569+00:00",
55
"files": {
66
"task-gallery.json": 25,
77
"interactive-gallery.json": 9,

api/transformerlab/galleries/examples/interactive-vllm/run.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
import time
1010
import urllib.request
1111
import urllib.error
12+
from lab import lab
13+
1214

1315
LOG_FILES = [
1416
pathlib.Path("/tmp/vllm.log"),
@@ -121,6 +123,12 @@ def main() -> None:
121123
tp_size = os.environ.get("TP_SIZE", "1")
122124
env = os.environ.copy()
123125

126+
# If a TLab registry model was selected, download it and use the local path.
127+
if os.environ.get("MODEL_REGISTRY_TLAB") == "1":
128+
print(f"Downloading registry model '{model_name}'...", flush=True)
129+
model_name = lab.download_registry_model(model_name)
130+
print(f"Model downloaded to: {model_name}", flush=True)
131+
124132
_touch_logs()
125133

126134
vllm_python = os.path.expanduser("~/vllm-venv/bin/python")

api/transformerlab/galleries/src/interactive/interactive.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@
104104
{
105105
"field_name": "Model Name",
106106
"env_var": "MODEL_NAME",
107-
"field_type": "str",
107+
"field_type": "model",
108108
"required": true,
109109
"placeholder": "e.g. meta-llama/Llama-2-7b-chat-hf",
110110
"help_text": "HuggingFace model identifier"

api/transformerlab/routers/model.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,45 @@ async def model_local_list():
3838
return models
3939

4040

41+
@router.get("/model/registry_versions")
42+
@cached(key="models:registry_versions", ttl="1h", tags=["models", "models:registry_versions", "asset_versions"])
43+
async def model_registry_versions():
44+
"""List model registry versions as selectable model IDs."""
45+
from transformerlab.services import asset_version_service
46+
47+
groups = await asset_version_service.list_groups("model")
48+
versions: list[dict] = []
49+
50+
for group in groups:
51+
group_id = group.get("group_id")
52+
group_name = group.get("group_name") or group_id
53+
if not group_id:
54+
continue
55+
56+
group_versions = await asset_version_service.list_versions("model", group_id)
57+
for version in group_versions:
58+
model_id = version.get("asset_id")
59+
version_label = version.get("version_label")
60+
if not model_id or not version_label:
61+
continue
62+
63+
tag = version.get("tag")
64+
tag_suffix = f" ({tag})" if tag else ""
65+
versions.append(
66+
{
67+
"model_id": model_id,
68+
"name": f"{group_name}/{version_label}{tag_suffix}",
69+
"group_id": group_id,
70+
"group_name": group_name,
71+
"version_label": version_label,
72+
"tag": tag,
73+
"created_at": version.get("created_at"),
74+
}
75+
)
76+
77+
return versions
78+
79+
4180
@router.get("/model/create")
4281
async def model_local_create(id: str, name: str, json_data={}):
4382
# Use filesystem instead of database

lab-sdk/pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
44

55
[project]
66
name = "transformerlab"
7-
version = "0.1.26"
7+
version = "0.1.27"
88
description = "Python SDK for Transformer Lab"
99
readme = "README.md"
1010
requires-python = ">=3.10"

lab-sdk/src/lab/lab_facade.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -648,6 +648,18 @@ def finish(
648648
# Important: update cached_jobs only when all completion fields are already written.
649649
_run_async(self._job.update_status(JobStatus.COMPLETE)) # type: ignore[union-attr]
650650

651+
def download_registry_model(self, model_id: str) -> str:
652+
"""Download a TLab registry model to ~/tmp/<model_id> and return the local path."""
653+
return _run_async(self.async_download_registry_model(model_id))
654+
655+
async def async_download_registry_model(self, model_id: str) -> str:
656+
"""Download model from TLab registry storage to ~/tmp/<model_id> and return the local path."""
657+
model = await ModelService.get(model_id)
658+
remote_dir = await model.get_dir()
659+
local_path = os.path.expanduser(f"~/tmp/{model_id}")
660+
await storage.copy_dir(remote_dir, local_path)
661+
return local_path
662+
651663
def save_artifact(
652664
self,
653665
source_path: Union[str, Any],

lab-sdk/tests/test_lab_facade.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1244,3 +1244,43 @@ async def test_copy_file_mounts_without_lab_init(tmp_path, monkeypatch):
12441244
assert os.path.isfile(dest)
12451245
with open(dest) as f:
12461246
assert f.read() == "hi"
1247+
1248+
1249+
def test_download_registry_model(tmp_path, monkeypatch):
1250+
_fresh(monkeypatch)
1251+
home = tmp_path / ".tfl_home"
1252+
ws = tmp_path / ".tfl_ws"
1253+
home.mkdir()
1254+
ws.mkdir()
1255+
monkeypatch.setenv("TFL_HOME_DIR", str(home))
1256+
monkeypatch.setenv("TFL_WORKSPACE_DIR", str(ws))
1257+
1258+
import asyncio
1259+
import os
1260+
import lab.storage as storage_module
1261+
from lab.lab_facade import Lab
1262+
from lab.model import Model
1263+
1264+
model_id = "test_registry_model"
1265+
asyncio.run(Model.create(model_id))
1266+
1267+
lab = Lab()
1268+
lab.init(experiment_id="test_exp")
1269+
1270+
copy_calls = []
1271+
1272+
async def fake_copy_dir(src: str, dest: str) -> None:
1273+
copy_calls.append((src, dest))
1274+
1275+
monkeypatch.setattr(storage_module, "copy_dir", fake_copy_dir)
1276+
1277+
result = lab.download_registry_model(model_id)
1278+
1279+
expected_dest = os.path.expanduser(f"~/tmp/{model_id}")
1280+
assert result == expected_dest
1281+
assert len(copy_calls) == 1
1282+
1283+
_m = asyncio.run(Model.get(model_id))
1284+
expected_src = asyncio.run(_m.get_dir())
1285+
assert copy_calls[0][0] == expected_src
1286+
assert copy_calls[0][1] == expected_dest

0 commit comments

Comments
 (0)