@huggingface/tasks 0.21.35 → 0.21.37
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.
- package/dist/commonjs/local-apps.js +2 -2
- package/dist/commonjs/local-apps.spec.js +2 -2
- package/dist/commonjs/model-libraries-snippets.d.ts.map +1 -1
- package/dist/commonjs/model-libraries-snippets.js +220 -69
- package/dist/commonjs/model-libraries-snippets.spec.js +31 -0
- package/dist/commonjs/model-libraries.js +1 -1
- package/dist/esm/local-apps.js +2 -2
- package/dist/esm/local-apps.spec.js +2 -2
- package/dist/esm/model-libraries-snippets.d.ts.map +1 -1
- package/dist/esm/model-libraries-snippets.js +220 -69
- package/dist/esm/model-libraries-snippets.spec.js +32 -1
- package/dist/esm/model-libraries.js +1 -1
- package/package.json +1 -1
- package/src/local-apps.spec.ts +2 -2
- package/src/local-apps.ts +2 -2
- package/src/model-libraries-snippets.spec.ts +46 -1
- package/src/model-libraries-snippets.ts +290 -72
- package/src/model-libraries.ts +1 -1
|
@@ -7,11 +7,12 @@ function nameWithoutNamespace(modelId) {
|
|
|
7
7
|
return splitted.length === 1 ? splitted[0] : splitted[1];
|
|
8
8
|
}
|
|
9
9
|
const escapeStringForJson = (str) => JSON.stringify(str).slice(1, -1); // slice is needed to remove surrounding quotes added by JSON.stringify
|
|
10
|
+
const isValidIdentifier = (str) => /^[A-Za-z_]\w*$/.test(str);
|
|
10
11
|
//#region snippets
|
|
11
12
|
export const adapters = (model) => [
|
|
12
13
|
`from adapters import AutoAdapterModel
|
|
13
14
|
|
|
14
|
-
model = AutoAdapterModel.from_pretrained("${model.config?.adapter_transformers?.model_name}")
|
|
15
|
+
model = AutoAdapterModel.from_pretrained("${escapeStringForJson(model.config?.adapter_transformers?.model_name ?? "fill-in-model-name")}")
|
|
15
16
|
model.load_adapter("${model.id}", set_active=True)`,
|
|
16
17
|
];
|
|
17
18
|
const allennlpUnknown = (model) => [
|
|
@@ -63,7 +64,7 @@ result, message = detector.detect_watermark(watermarked_audio, sr)`;
|
|
|
63
64
|
return [watermarkSnippet, detectorSnippet];
|
|
64
65
|
};
|
|
65
66
|
function get_base_diffusers_model(model) {
|
|
66
|
-
return model.cardData?.base_model?.toString() ?? "fill-in-base-model";
|
|
67
|
+
return escapeStringForJson(model.cardData?.base_model?.toString() ?? "fill-in-base-model");
|
|
67
68
|
}
|
|
68
69
|
function get_prompt_from_diffusers_model(model) {
|
|
69
70
|
const prompt = model.widgetData?.[0]?.text ?? model.cardData?.instance_prompt;
|
|
@@ -877,7 +878,8 @@ backbone = keras_hub.models.Backbone.from_preset("hf://${modelId}")
|
|
|
877
878
|
`;
|
|
878
879
|
export const keras_hub = (model) => {
|
|
879
880
|
const modelId = model.id;
|
|
880
|
-
|
|
881
|
+
// interpolated as a Python class name, so a non-identifier is treated as absent
|
|
882
|
+
const tasks = (model.config?.keras_hub?.tasks ?? []).filter(isValidIdentifier);
|
|
881
883
|
const snippets = [];
|
|
882
884
|
// First, generate tasks with examples
|
|
883
885
|
for (const [task, snippet] of Object.entries(_keras_hub_tasks_with_example)) {
|
|
@@ -944,77 +946,219 @@ audio = m.generate("This high quality TTS model works without a GPU")
|
|
|
944
946
|
import soundfile as sf
|
|
945
947
|
sf.write('output.wav', audio, 24000)`,
|
|
946
948
|
];
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
949
|
+
/**
|
|
950
|
+
* Detect LTX-2.5 (split weights + Gemma 4 TE file) vs LTX-2.3
|
|
951
|
+
* (monolith checkpoint + separate Gemma 3 root). Prefer explicit tags / ids /
|
|
952
|
+
* base_model refs; fall back to 2.3 for unmarked legacy cards.
|
|
953
|
+
*/
|
|
954
|
+
function _isLtx25Model(model) {
|
|
955
|
+
const refs = [model.id, ...(model.tags ?? [])];
|
|
956
|
+
const base = model.cardData?.base_model;
|
|
957
|
+
if (Array.isArray(base)) {
|
|
958
|
+
refs.push(...base);
|
|
959
|
+
}
|
|
960
|
+
else if (base) {
|
|
961
|
+
refs.push(base);
|
|
962
|
+
}
|
|
963
|
+
return refs.some((ref) => /ltx[-_]?2\.5/i.test(ref));
|
|
964
|
+
}
|
|
965
|
+
const _LTX_I2V_HINT = `# For image-to-video, add: --image path/to/image.jpg 0 0.8`;
|
|
966
|
+
const _LTX_GEMMA3_ROOT = "models/gemma-3-12b";
|
|
967
|
+
const _LTX_DEFAULT_PROMPT = "A beautiful sunset over the ocean";
|
|
968
|
+
function _ltxInstall(is25) {
|
|
969
|
+
// natten is only needed for the LTX-2.5 diffusion VAE (skipped automatically on Windows/macOS).
|
|
970
|
+
return `# Install the LTX-2 pipelines
|
|
950
971
|
git clone https://github.com/Lightricks/LTX-2.git
|
|
951
972
|
cd LTX-2
|
|
952
|
-
uv sync --frozen`;
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
973
|
+
uv sync ${is25 ? "--extra natten" : "--frozen"}`;
|
|
974
|
+
}
|
|
975
|
+
function _ltxRun(module, args, comment, options) {
|
|
976
|
+
const body = `uv run python -m ltx_pipelines.${module} \\\n ${args.join(" \\\n ")}`;
|
|
977
|
+
const parts = [`# ${comment}`, body];
|
|
978
|
+
if (options?.footer) {
|
|
979
|
+
parts.push(options.footer);
|
|
980
|
+
}
|
|
981
|
+
if (options?.hint) {
|
|
982
|
+
parts.push(_LTX_I2V_HINT);
|
|
983
|
+
}
|
|
984
|
+
return parts.join("\n");
|
|
985
|
+
}
|
|
986
|
+
const _LTX_DETAILING_LORA_REPO = "Lightricks/LTX-2.5-22b-IC-LoRA-Pixel-Spatial-Upscaler";
|
|
987
|
+
const _LTX_DETAILING_LORA_FILE = "ltx-2.5-22b-ic-lora-pixel-spatial-upscaler-x2-1.0.safetensors";
|
|
988
|
+
function _ltx25SplitArgs(paths) {
|
|
989
|
+
const args = [
|
|
990
|
+
`--transformer-path ${paths.transformer}`,
|
|
991
|
+
`--text-encoder-path ${paths.textEncoder}`,
|
|
992
|
+
`--video-vae-path ${paths.videoVae}`,
|
|
993
|
+
`--audio-vae-path ${paths.audioVae}`,
|
|
994
|
+
`--spatial-upsampler-path ${paths.spatialUpsampler}`,
|
|
995
|
+
];
|
|
996
|
+
if (paths.temporalUpsampler) {
|
|
997
|
+
args.push(`--temporal-upsampler-path ${paths.temporalUpsampler}`);
|
|
998
|
+
}
|
|
999
|
+
return args;
|
|
1000
|
+
}
|
|
1001
|
+
function _ltx25RepoPaths(localDir) {
|
|
1002
|
+
const shared = {
|
|
1003
|
+
transformer: `${localDir}/diffusion_models/<distilled-transformer>.safetensors`,
|
|
1004
|
+
textEncoder: `${localDir}/text_encoders/gemma4-12b-with-proj-ltx-2.5-bf16.safetensors`,
|
|
1005
|
+
videoVae: `${localDir}/vae/<video-vae>.safetensors`,
|
|
1006
|
+
audioVae: `${localDir}/vae/<audio-vae>.safetensors`,
|
|
1007
|
+
spatialUpsampler: `${localDir}/latent_upscale_models/<spatial-upsampler>.safetensors`,
|
|
1008
|
+
};
|
|
1009
|
+
return {
|
|
1010
|
+
distilled: shared,
|
|
1011
|
+
// DFR runs on the distilled transformer; detailing IC-LoRA is required separately.
|
|
1012
|
+
dfr: {
|
|
1013
|
+
...shared,
|
|
1014
|
+
temporalUpsampler: `${localDir}/latent_upscale_models/<temporal-upsampler>.safetensors`,
|
|
1015
|
+
},
|
|
1016
|
+
};
|
|
1017
|
+
}
|
|
1018
|
+
function _ltx25BasePlaceholderPaths() {
|
|
1019
|
+
return {
|
|
1020
|
+
transformer: "path/to/distilled-transformer.safetensors",
|
|
1021
|
+
textEncoder: "path/to/gemma4-12b-with-proj-ltx-2.5-bf16.safetensors",
|
|
1022
|
+
videoVae: "path/to/video-vae.safetensors",
|
|
1023
|
+
audioVae: "path/to/audio-vae.safetensors",
|
|
1024
|
+
spatialUpsampler: "path/to/spatial-upsampler.safetensors",
|
|
1025
|
+
};
|
|
1026
|
+
}
|
|
1027
|
+
function _ltx25Download(modelId, localDir, kind) {
|
|
1028
|
+
if (kind === "adapter") {
|
|
1029
|
+
return `# Download the adapter weights from this repo
|
|
1030
|
+
# (base components come from Lightricks/LTX-2.5 — see Files and versions)
|
|
1031
|
+
hf download ${modelId} --local-dir ${localDir}`;
|
|
1032
|
+
}
|
|
1033
|
+
const detailingDir = `models/${_LTX_DETAILING_LORA_REPO.split("/")[1]}`;
|
|
1034
|
+
return `# Download weights from this repo
|
|
1035
|
+
# Substitute filenames from this repo's "Files and versions" if they differ
|
|
1036
|
+
hf download ${modelId} \\
|
|
1037
|
+
diffusion_models/<distilled-transformer>.safetensors \\
|
|
1038
|
+
text_encoders/gemma4-12b-with-proj-ltx-2.5-bf16.safetensors \\
|
|
1039
|
+
vae/<video-vae>.safetensors \\
|
|
1040
|
+
vae/<audio-vae>.safetensors \\
|
|
1041
|
+
latent_upscale_models/<spatial-upsampler>.safetensors \\
|
|
1042
|
+
latent_upscale_models/<temporal-upsampler>.safetensors \\
|
|
1043
|
+
--local-dir ${localDir}
|
|
1044
|
+
# DFR requires the detailing IC-LoRA (separate repo; strength is fixed at 0.5)
|
|
1045
|
+
hf download ${_LTX_DETAILING_LORA_REPO} --local-dir ${detailingDir}`;
|
|
1046
|
+
}
|
|
1047
|
+
function _ltx23Download(modelId, localDir) {
|
|
1048
|
+
return `# Download the weights from this repo, plus the Gemma text encoder
|
|
1049
|
+
hf download ${modelId} --local-dir ${localDir}
|
|
1050
|
+
hf download google/gemma-3-12b-it-qat-q4_0-unquantized --local-dir ${_LTX_GEMMA3_ROOT}`;
|
|
1051
|
+
}
|
|
1052
|
+
function _ltx25Snippets(model, localDir, tags) {
|
|
1053
|
+
const install = _ltxInstall(true);
|
|
1054
|
+
const loraArg = `--lora ${localDir}/<weights>.safetensors 1.0`;
|
|
1055
|
+
const basePaths = _ltx25BasePlaceholderPaths();
|
|
1056
|
+
if (tags.includes("ic-lora")) {
|
|
1057
|
+
return [
|
|
1058
|
+
install,
|
|
1059
|
+
_ltx25Download(model.id, localDir, "adapter"),
|
|
1060
|
+
_ltxRun("ic_lora", [
|
|
1061
|
+
..._ltx25SplitArgs(basePaths),
|
|
1062
|
+
loraArg,
|
|
1063
|
+
"--video-conditioning reference.mp4 1.0",
|
|
1064
|
+
`--prompt "your prompt here"`,
|
|
1065
|
+
"--output-path output.mp4",
|
|
1066
|
+
], "Video-to-video with the IC-LoRA (runs on the distilled LTX-2.5 base)"),
|
|
1067
|
+
];
|
|
1068
|
+
}
|
|
1069
|
+
if (tags.includes("lora")) {
|
|
1070
|
+
return [
|
|
1071
|
+
install,
|
|
1072
|
+
_ltx25Download(model.id, localDir, "adapter"),
|
|
1073
|
+
_ltxRun("distilled", [..._ltx25SplitArgs(basePaths), loraArg, `--prompt "your prompt here"`, "--output-path output.mp4"], "Text/image-to-video with the LoRA on the distilled LTX-2.5 pipeline", { hint: true }),
|
|
1074
|
+
];
|
|
1075
|
+
}
|
|
1076
|
+
const { distilled, dfr } = _ltx25RepoPaths(localDir);
|
|
1077
|
+
const detailingDir = `models/${_LTX_DETAILING_LORA_REPO.split("/")[1]}`;
|
|
1078
|
+
return [
|
|
1079
|
+
install,
|
|
1080
|
+
_ltx25Download(model.id, localDir, "base"),
|
|
1081
|
+
_ltxRun("distilled", [
|
|
1082
|
+
..._ltx25SplitArgs(distilled),
|
|
1083
|
+
"--num-frames 121",
|
|
1084
|
+
`--prompt "${_LTX_DEFAULT_PROMPT}"`,
|
|
1085
|
+
"--output-path output.mp4",
|
|
1086
|
+
], "Distilled LTX-2.5 pipeline (fast)", { hint: true }),
|
|
1087
|
+
_ltxRun("dfr_pipeline", [
|
|
1088
|
+
..._ltx25SplitArgs(dfr),
|
|
1089
|
+
`--detailing-lora ${detailingDir}/${_LTX_DETAILING_LORA_FILE}`,
|
|
1090
|
+
"--spatial-upscalings 1",
|
|
1091
|
+
"--temporal-upscalings 1",
|
|
1092
|
+
"--height 1088",
|
|
1093
|
+
"--width 1920",
|
|
1094
|
+
"--num-frames 121",
|
|
1095
|
+
`--prompt "${_LTX_DEFAULT_PROMPT}"`,
|
|
1096
|
+
"--output-path output.mp4",
|
|
1097
|
+
], "DFR pipeline (higher detail fidelity; optional temporal 2x/4x)", {
|
|
1098
|
+
footer: "# For 4K: --spatial-upscalings 2 --width 3840 --height 2176",
|
|
1099
|
+
hint: true,
|
|
1100
|
+
}),
|
|
1101
|
+
];
|
|
1102
|
+
}
|
|
1103
|
+
function _ltx23Snippets(model, localDir, tags) {
|
|
1104
|
+
const install = _ltxInstall(false);
|
|
1105
|
+
const download = _ltx23Download(model.id, localDir);
|
|
1106
|
+
const loraArg = `--lora ${localDir}/<weights>.safetensors 1.0`;
|
|
1107
|
+
const gemma = `--gemma-root ${_LTX_GEMMA3_ROOT}`;
|
|
963
1108
|
if (tags.includes("ic-lora")) {
|
|
964
1109
|
return [
|
|
965
1110
|
install,
|
|
966
1111
|
download,
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
1112
|
+
_ltxRun("ic_lora", [
|
|
1113
|
+
"--distilled-checkpoint-path path/to/distilled_checkpoint.safetensors",
|
|
1114
|
+
"--spatial-upsampler-path path/to/spatial_upsampler.safetensors",
|
|
1115
|
+
gemma,
|
|
1116
|
+
loraArg,
|
|
1117
|
+
"--video-conditioning reference.mp4 1.0",
|
|
1118
|
+
`--prompt "your prompt here"`,
|
|
1119
|
+
"--output-path output.mp4",
|
|
1120
|
+
], "Video-to-video with the IC-LoRA (runs on the distilled base model)"),
|
|
976
1121
|
];
|
|
977
1122
|
}
|
|
978
|
-
// Standard LoRA applied on top of the base pipeline.
|
|
979
1123
|
if (tags.includes("lora")) {
|
|
980
1124
|
return [
|
|
981
1125
|
install,
|
|
982
1126
|
download,
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
${imageToVideoHint}`,
|
|
1127
|
+
_ltxRun("ti2vid_two_stages_hq", [
|
|
1128
|
+
"--checkpoint-path path/to/checkpoint.safetensors",
|
|
1129
|
+
"--distilled-lora path/to/distilled_lora.safetensors 0.8",
|
|
1130
|
+
"--spatial-upsampler-path path/to/spatial_upsampler.safetensors",
|
|
1131
|
+
gemma,
|
|
1132
|
+
loraArg,
|
|
1133
|
+
`--prompt "your prompt here"`,
|
|
1134
|
+
"--output-path output.mp4",
|
|
1135
|
+
], "Text/image-to-video with the LoRA on the HQ two-stage base pipeline", { hint: true }),
|
|
993
1136
|
];
|
|
994
1137
|
}
|
|
995
|
-
// Base model: the fast (distilled) and HQ (two-stage) pipelines. Substitute the
|
|
996
|
-
// .safetensors filenames with the ones listed under this repo's "Files and versions".
|
|
997
1138
|
return [
|
|
998
1139
|
install,
|
|
999
1140
|
download,
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
--output-path output.mp4
|
|
1016
|
-
${imageToVideoHint}`,
|
|
1141
|
+
_ltxRun("distilled", [
|
|
1142
|
+
`--distilled-checkpoint-path ${localDir}/<distilled-checkpoint>.safetensors`,
|
|
1143
|
+
`--spatial-upsampler-path ${localDir}/<spatial-upsampler>.safetensors`,
|
|
1144
|
+
gemma,
|
|
1145
|
+
`--prompt "${_LTX_DEFAULT_PROMPT}"`,
|
|
1146
|
+
"--output-path output.mp4",
|
|
1147
|
+
], "Fast pipeline (distilled model, no distilled LoRA needed)", { hint: true }),
|
|
1148
|
+
_ltxRun("ti2vid_two_stages_hq", [
|
|
1149
|
+
`--checkpoint-path ${localDir}/<checkpoint>.safetensors`,
|
|
1150
|
+
`--distilled-lora ${localDir}/<distilled-lora>.safetensors 0.8`,
|
|
1151
|
+
`--spatial-upsampler-path ${localDir}/<spatial-upsampler>.safetensors`,
|
|
1152
|
+
gemma,
|
|
1153
|
+
`--prompt "${_LTX_DEFAULT_PROMPT}"`,
|
|
1154
|
+
"--output-path output.mp4",
|
|
1155
|
+
], "HQ pipeline (two-stage, higher quality)", { hint: true }),
|
|
1017
1156
|
];
|
|
1157
|
+
}
|
|
1158
|
+
export const ltx = (model) => {
|
|
1159
|
+
const localDir = `models/${nameWithoutNamespace(model.id)}`;
|
|
1160
|
+
const tags = model.tags ?? [];
|
|
1161
|
+
return _isLtx25Model(model) ? _ltx25Snippets(model, localDir, tags) : _ltx23Snippets(model, localDir, tags);
|
|
1018
1162
|
};
|
|
1019
1163
|
export const lightning_ir = (model) => {
|
|
1020
1164
|
if (model.tags.includes("bi-encoder")) {
|
|
@@ -1170,7 +1314,7 @@ model = MeshAnything(args)`,
|
|
|
1170
1314
|
];
|
|
1171
1315
|
export const multimolecule = (model) => {
|
|
1172
1316
|
const widgetExample = model.widgetData?.[0];
|
|
1173
|
-
const exampleText = widgetExample?.text;
|
|
1317
|
+
const exampleText = escapeStringForJson(widgetExample?.text ?? "");
|
|
1174
1318
|
const maskToken = model.mask_token ?? "<mask>";
|
|
1175
1319
|
const sequence = exampleText?.replace(maskToken, "A");
|
|
1176
1320
|
const snippets = [`pip install multimolecule`];
|
|
@@ -1225,8 +1369,9 @@ openasr transcribe audio.wav --model ${modelId}`,
|
|
|
1225
1369
|
];
|
|
1226
1370
|
};
|
|
1227
1371
|
export const paddlenlp = (model) => {
|
|
1228
|
-
|
|
1229
|
-
|
|
1372
|
+
const architecture = model.config?.architectures?.[0];
|
|
1373
|
+
// interpolated as a Python class name, so a non-identifier is treated as absent
|
|
1374
|
+
if (architecture && isValidIdentifier(architecture)) {
|
|
1230
1375
|
return [
|
|
1231
1376
|
[
|
|
1232
1377
|
`from paddlenlp.transformers import AutoTokenizer, ${architecture}`,
|
|
@@ -1465,7 +1610,7 @@ const skopsPickle = (model, modelFile) => {
|
|
|
1465
1610
|
from skops.hub_utils import download
|
|
1466
1611
|
download("${model.id}", "path_to_folder")
|
|
1467
1612
|
model = joblib.load(
|
|
1468
|
-
"${modelFile}"
|
|
1613
|
+
"${escapeStringForJson(modelFile)}"
|
|
1469
1614
|
)
|
|
1470
1615
|
# only load pickle files from sources you trust
|
|
1471
1616
|
# read more about it here https://skops.readthedocs.io/en/stable/persistence.html`,
|
|
@@ -1478,7 +1623,7 @@ from skops.io import load
|
|
|
1478
1623
|
download("${model.id}", "path_to_folder")
|
|
1479
1624
|
# make sure model file is in skops format
|
|
1480
1625
|
# if model is a pickle file, make sure it's from a source you trust
|
|
1481
|
-
model = load("path_to_folder/${modelFile}")`,
|
|
1626
|
+
model = load("path_to_folder/${escapeStringForJson(modelFile)}")`,
|
|
1482
1627
|
];
|
|
1483
1628
|
};
|
|
1484
1629
|
const skopsJobLib = (model) => {
|
|
@@ -1731,11 +1876,17 @@ model = BACKBONE_REGISTRY.build("${model.id}")`,
|
|
|
1731
1876
|
const hasChatTemplate = (model) => model.config?.tokenizer_config?.chat_template !== undefined ||
|
|
1732
1877
|
model.config?.processor_config?.chat_template !== undefined ||
|
|
1733
1878
|
model.config?.chat_template_jinja !== undefined;
|
|
1879
|
+
// interpolated as a Python class name (and into RegExps in pruna_transformers), so a non-identifier is treated as absent
|
|
1880
|
+
const autoModelClass = (model) => {
|
|
1881
|
+
const autoModel = model.transformersInfo?.auto_model;
|
|
1882
|
+
return autoModel && isValidIdentifier(autoModel) ? autoModel : undefined;
|
|
1883
|
+
};
|
|
1734
1884
|
export const transformers = (model) => {
|
|
1735
1885
|
const info = model.transformersInfo;
|
|
1736
1886
|
if (!info) {
|
|
1737
1887
|
return [`# ⚠️ Type of model unknown`];
|
|
1738
1888
|
}
|
|
1889
|
+
const auto_model = autoModelClass(model) ?? "AutoModel";
|
|
1739
1890
|
const remote_code_snippet = model.tags.includes(TAG_CUSTOM_CODE) ? ", trust_remote_code=True" : "";
|
|
1740
1891
|
const autoSnippet = [];
|
|
1741
1892
|
if (info.processor) {
|
|
@@ -1744,7 +1895,7 @@ export const transformers = (model) => {
|
|
|
1744
1895
|
: info.processor === "AutoFeatureExtractor"
|
|
1745
1896
|
? "extractor"
|
|
1746
1897
|
: "processor";
|
|
1747
|
-
autoSnippet.push("# Load model directly", `from transformers import ${info.processor}, ${
|
|
1898
|
+
autoSnippet.push("# Load model directly", `from transformers import ${info.processor}, ${auto_model}`, "", `${processorVarName} = ${info.processor}.from_pretrained("${model.id}"` + remote_code_snippet + ")", `model = ${auto_model}.from_pretrained("${model.id}"` + remote_code_snippet + ', device_map="auto")');
|
|
1748
1899
|
if (model.tags.includes("conversational") && hasChatTemplate(model)) {
|
|
1749
1900
|
if (model.tags.includes("image-text-to-text")) {
|
|
1750
1901
|
autoSnippet.push("messages = [", [
|
|
@@ -1764,7 +1915,7 @@ export const transformers = (model) => {
|
|
|
1764
1915
|
}
|
|
1765
1916
|
}
|
|
1766
1917
|
else {
|
|
1767
|
-
autoSnippet.push("# Load model directly", `from transformers import ${
|
|
1918
|
+
autoSnippet.push("# Load model directly", `from transformers import ${auto_model}`, `model = ${auto_model}.from_pretrained("${model.id}"` + remote_code_snippet + ', device_map="auto")');
|
|
1768
1919
|
}
|
|
1769
1920
|
if (model.pipeline_tag && LIBRARY_TASK_MAPPING.transformers?.includes(model.pipeline_tag)) {
|
|
1770
1921
|
const pipelineSnippet = ["# Use a pipeline as a high-level helper"];
|
|
@@ -1840,7 +1991,7 @@ export const peft = (model) => {
|
|
|
1840
1991
|
`from peft import PeftModel
|
|
1841
1992
|
from transformers import AutoModelFor${pefttask}
|
|
1842
1993
|
|
|
1843
|
-
base_model = AutoModelFor${pefttask}.from_pretrained("${peftBaseModel}")
|
|
1994
|
+
base_model = AutoModelFor${pefttask}.from_pretrained("${escapeStringForJson(peftBaseModel)}")
|
|
1844
1995
|
model = PeftModel.from_pretrained(base_model, "${model.id}")`,
|
|
1845
1996
|
];
|
|
1846
1997
|
};
|
|
@@ -2230,18 +2381,18 @@ const pruna_diffusers = (model) => {
|
|
|
2230
2381
|
.trim());
|
|
2231
2382
|
};
|
|
2232
2383
|
const pruna_transformers = (model) => {
|
|
2233
|
-
const
|
|
2384
|
+
const auto_model = autoModelClass(model);
|
|
2234
2385
|
const transformersSnippets = transformers(model);
|
|
2235
2386
|
// Replace pipeline with PrunaModel
|
|
2236
2387
|
let processedSnippets = transformersSnippets.map((snippet) => snippet
|
|
2237
2388
|
.replace(/from transformers import pipeline/g, "from pruna import PrunaModel")
|
|
2238
2389
|
.replace(/pipeline\([^)]*\)/g, `PrunaModel.from_pretrained("${model.id}")`));
|
|
2239
2390
|
// Additional cleanup if auto_model info is available
|
|
2240
|
-
if (
|
|
2391
|
+
if (auto_model) {
|
|
2241
2392
|
processedSnippets = processedSnippets.map((snippet) => snippet
|
|
2242
|
-
.replace(new RegExp(`from transformers import ${
|
|
2243
|
-
.replace(new RegExp(`${
|
|
2244
|
-
.replace(new RegExp(`^.*from.*import.*(, *${
|
|
2393
|
+
.replace(new RegExp(`from transformers import ${auto_model}\n?`, "g"), "")
|
|
2394
|
+
.replace(new RegExp(`${auto_model}.from_pretrained`, "g"), "PrunaModel.from_pretrained")
|
|
2395
|
+
.replace(new RegExp(`^.*from.*import.*(, *${auto_model})+.*$`, "gm"), (line) => line.replace(new RegExp(`, *${auto_model}`, "g"), "")));
|
|
2245
2396
|
}
|
|
2246
2397
|
return processedSnippets;
|
|
2247
2398
|
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { describe, expect, it } from "vitest";
|
|
2
|
-
import { llama_cpp_python } from "./model-libraries-snippets.js";
|
|
2
|
+
import { adapters, diffusers, keras_hub, llama_cpp_python, multimolecule, paddlenlp, peft, sklearn, transformers, } from "./model-libraries-snippets.js";
|
|
3
3
|
describe("model-libraries-snippets", () => {
|
|
4
4
|
it("llama_cpp_python conversational", async () => {
|
|
5
5
|
const model = {
|
|
@@ -50,4 +50,35 @@ output = llm(
|
|
|
50
50
|
)
|
|
51
51
|
print(output)`);
|
|
52
52
|
});
|
|
53
|
+
// a repo owner can put anything in config.json / the model card, so every interpolated value
|
|
54
|
+
// must either be escaped (string literals) or rejected (bare identifiers)
|
|
55
|
+
describe("repo-controlled values are not injectable", () => {
|
|
56
|
+
const PAYLOAD = `")\nimport os; os.system("id`;
|
|
57
|
+
it.each([
|
|
58
|
+
["adapters", adapters, { config: { adapter_transformers: { model_name: PAYLOAD } } }],
|
|
59
|
+
["diffusers", diffusers, { tags: ["lora"], cardData: { base_model: PAYLOAD, instance_prompt: PAYLOAD } }],
|
|
60
|
+
["keras_hub", keras_hub, { config: { keras_hub: { tasks: [PAYLOAD, "TextClassifier"] } } }],
|
|
61
|
+
["paddlenlp", paddlenlp, { config: { architectures: [PAYLOAD] } }],
|
|
62
|
+
["peft", peft, { config: { peft: { base_model_name_or_path: PAYLOAD, task_type: "CAUSAL_LM" } } }],
|
|
63
|
+
["multimolecule", multimolecule, { widgetData: [{ text: PAYLOAD }] }],
|
|
64
|
+
["transformers", transformers, { transformersInfo: { auto_model: PAYLOAD, processor: "AutoTokenizer" } }],
|
|
65
|
+
[
|
|
66
|
+
"sklearn",
|
|
67
|
+
sklearn,
|
|
68
|
+
{ tags: ["skops"], config: { sklearn: { model: { file: PAYLOAD }, model_format: "pickle" } } },
|
|
69
|
+
],
|
|
70
|
+
])("%s", (_name, snippetFn, model) => {
|
|
71
|
+
const snippet = snippetFn({ id: "user/model", tags: [], inference: "", ...model }).join("\n");
|
|
72
|
+
expect(snippet).not.toContain(PAYLOAD);
|
|
73
|
+
});
|
|
74
|
+
it("keras_hub keeps the valid task next to a rejected one", () => {
|
|
75
|
+
const model = {
|
|
76
|
+
id: "user/model",
|
|
77
|
+
tags: [],
|
|
78
|
+
inference: "",
|
|
79
|
+
config: { keras_hub: { tasks: [PAYLOAD, "TextClassifier"] } },
|
|
80
|
+
};
|
|
81
|
+
expect(keras_hub(model).join("\n")).toContain("keras_hub.models.TextClassifier.from_preset");
|
|
82
|
+
});
|
|
83
|
+
});
|
|
53
84
|
});
|
|
@@ -760,7 +760,7 @@ export const MODEL_LIBRARIES_UI_ELEMENTS = {
|
|
|
760
760
|
countDownloads: `path_extension:"pt"`,
|
|
761
761
|
},
|
|
762
762
|
ltx: {
|
|
763
|
-
prettyLabel: "LTX
|
|
763
|
+
prettyLabel: "LTX-2",
|
|
764
764
|
repoName: "LTX-2",
|
|
765
765
|
repoUrl: "https://github.com/Lightricks/LTX-2",
|
|
766
766
|
docsUrl: "https://github.com/Lightricks/LTX-2",
|
package/package.json
CHANGED
package/src/local-apps.spec.ts
CHANGED
|
@@ -133,7 +133,7 @@ curl -X POST "http://localhost:8000/v1/chat/completions" \\
|
|
|
133
133
|
const snippet = snippetFunc(model);
|
|
134
134
|
|
|
135
135
|
expect(snippet[0].content).toContain(`llama serve -hf bartowski/Llama-3.2-3B-Instruct-GGUF:{{QUANT_TAG}}`);
|
|
136
|
-
expect(snippet[1].setup).toContain("npm install -g @
|
|
136
|
+
expect(snippet[1].setup).toContain("npm install -g @earendil-works/pi-coding-agent");
|
|
137
137
|
expect(snippet[1].content).toContain(`"id": "bartowski/Llama-3.2-3B-Instruct-GGUF:{{QUANT_TAG}}"`);
|
|
138
138
|
expect(snippet[2].content).toContain("pi");
|
|
139
139
|
});
|
|
@@ -155,7 +155,7 @@ curl -X POST "http://localhost:8000/v1/chat/completions" \\
|
|
|
155
155
|
|
|
156
156
|
expect(snippet[0].setup).toContain("uv tool install mlx-lm");
|
|
157
157
|
expect(snippet[0].content).toContain('mlx_lm.server --model "mlx-community/Llama-3.2-3B-Instruct-mlx"');
|
|
158
|
-
expect(snippet[1].setup).toContain("npm install -g @
|
|
158
|
+
expect(snippet[1].setup).toContain("npm install -g @earendil-works/pi-coding-agent");
|
|
159
159
|
expect(snippet[1].content).toContain('"baseUrl": "http://localhost:8080/v1"');
|
|
160
160
|
expect(snippet[1].content).toContain('"id": "mlx-community/Llama-3.2-3B-Instruct-mlx"');
|
|
161
161
|
expect(snippet[2].content).toContain("pi");
|
package/src/local-apps.ts
CHANGED
|
@@ -509,7 +509,7 @@ const snippetPi = (model: ModelData, filepath?: string): LocalAppSnippet[] => {
|
|
|
509
509
|
serverStep,
|
|
510
510
|
{
|
|
511
511
|
title: "Configure the model in Pi",
|
|
512
|
-
setup: "# Install Pi:\nnpm install -g @
|
|
512
|
+
setup: "# Install Pi:\nnpm install -g @earendil-works/pi-coding-agent",
|
|
513
513
|
content: `# Add to ~/.pi/agent/models.json:\n${modelsJson}`,
|
|
514
514
|
},
|
|
515
515
|
{
|
|
@@ -820,7 +820,7 @@ export const LOCAL_APPS = {
|
|
|
820
820
|
},
|
|
821
821
|
pi: {
|
|
822
822
|
prettyLabel: "Pi",
|
|
823
|
-
docsUrl: "https://github.com/
|
|
823
|
+
docsUrl: "https://github.com/earendil-works/pi",
|
|
824
824
|
mainTask: "text-generation",
|
|
825
825
|
displayOnModelPage: isToolCallingLocalAgentModel,
|
|
826
826
|
snippet: snippetPi,
|
|
@@ -1,6 +1,16 @@
|
|
|
1
1
|
import { describe, expect, it } from "vitest";
|
|
2
2
|
import type { ModelData } from "./model-data.js";
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
adapters,
|
|
5
|
+
diffusers,
|
|
6
|
+
keras_hub,
|
|
7
|
+
llama_cpp_python,
|
|
8
|
+
multimolecule,
|
|
9
|
+
paddlenlp,
|
|
10
|
+
peft,
|
|
11
|
+
sklearn,
|
|
12
|
+
transformers,
|
|
13
|
+
} from "./model-libraries-snippets.js";
|
|
4
14
|
|
|
5
15
|
describe("model-libraries-snippets", () => {
|
|
6
16
|
it("llama_cpp_python conversational", async () => {
|
|
@@ -55,4 +65,39 @@ output = llm(
|
|
|
55
65
|
)
|
|
56
66
|
print(output)`);
|
|
57
67
|
});
|
|
68
|
+
|
|
69
|
+
// a repo owner can put anything in config.json / the model card, so every interpolated value
|
|
70
|
+
// must either be escaped (string literals) or rejected (bare identifiers)
|
|
71
|
+
describe("repo-controlled values are not injectable", () => {
|
|
72
|
+
const PAYLOAD = `")\nimport os; os.system("id`;
|
|
73
|
+
|
|
74
|
+
it.each([
|
|
75
|
+
["adapters", adapters, { config: { adapter_transformers: { model_name: PAYLOAD } } }],
|
|
76
|
+
["diffusers", diffusers, { tags: ["lora"], cardData: { base_model: PAYLOAD, instance_prompt: PAYLOAD } }],
|
|
77
|
+
["keras_hub", keras_hub, { config: { keras_hub: { tasks: [PAYLOAD, "TextClassifier"] } } }],
|
|
78
|
+
["paddlenlp", paddlenlp, { config: { architectures: [PAYLOAD] } }],
|
|
79
|
+
["peft", peft, { config: { peft: { base_model_name_or_path: PAYLOAD, task_type: "CAUSAL_LM" } } }],
|
|
80
|
+
["multimolecule", multimolecule, { widgetData: [{ text: PAYLOAD }] }],
|
|
81
|
+
["transformers", transformers, { transformersInfo: { auto_model: PAYLOAD, processor: "AutoTokenizer" } }],
|
|
82
|
+
[
|
|
83
|
+
"sklearn",
|
|
84
|
+
sklearn,
|
|
85
|
+
{ tags: ["skops"], config: { sklearn: { model: { file: PAYLOAD }, model_format: "pickle" } } },
|
|
86
|
+
],
|
|
87
|
+
])("%s", (_name, snippetFn, model) => {
|
|
88
|
+
const snippet = snippetFn({ id: "user/model", tags: [], inference: "", ...model } as ModelData).join("\n");
|
|
89
|
+
|
|
90
|
+
expect(snippet).not.toContain(PAYLOAD);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it("keras_hub keeps the valid task next to a rejected one", () => {
|
|
94
|
+
const model = {
|
|
95
|
+
id: "user/model",
|
|
96
|
+
tags: [],
|
|
97
|
+
inference: "",
|
|
98
|
+
config: { keras_hub: { tasks: [PAYLOAD, "TextClassifier"] } },
|
|
99
|
+
};
|
|
100
|
+
expect(keras_hub(model as ModelData).join("\n")).toContain("keras_hub.models.TextClassifier.from_preset");
|
|
101
|
+
});
|
|
102
|
+
});
|
|
58
103
|
});
|