-
Notifications
You must be signed in to change notification settings - Fork 62
Expand file tree
/
Copy path_prefetch.py
More file actions
314 lines (284 loc) · 8.64 KB
/
Copy path_prefetch.py
File metadata and controls
314 lines (284 loc) · 8.64 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
"""
CLI commands for prefetching artifacts from remote registries.
"""
import typing
from pathlib import Path
import rich_click as click
from rich.console import Console
from flyte._resources import Accelerators
from flyte.cli._common import CommandBase
# Get all valid accelerator choices from the Accelerators literal type
ACCELERATOR_CHOICES = list(typing.get_args(Accelerators))
@click.group(name="prefetch")
def prefetch():
"""
Prefetch artifacts from remote registries.
These commands help you download and prefetch artifacts like HuggingFace models
to your Flyte storage for faster access during task execution.
"""
@prefetch.command(name="hf-model", cls=CommandBase)
@click.argument("repo", type=str)
@click.option(
"--raw-data-path",
type=str,
required=False,
default=None,
help=(
"Object store path to store the model. If not provided, the model will be stored using the default path "
"generated by Flyte storage layer."
),
)
@click.option(
"--artifact-name",
type=str,
required=False,
default=None,
help=(
"Artifact name to use for the stored model. Must only contain alphanumeric characters, "
"underscores, and hyphens. If not provided, the repo name will be used (replacing '.' with '-')."
),
)
@click.option(
"--architecture",
type=str,
help="Model architecture, as given in HuggingFace config.json.",
)
@click.option(
"--task",
default="auto",
type=str,
help=(
"Model task, e.g., `generate`, `classify`, `embed`, `score`, etc. "
"Refer to vLLM docs. `auto` will try to discover this automatically."
),
)
@click.option(
"--modality",
type=str,
multiple=True,
default=("text",),
help="Modalities supported by the model, e.g., `text`, `image`, `audio`, `video`. Can be specified multiple times.",
)
@click.option(
"--format",
"serial_format",
type=str,
help="Model serialization format, e.g., safetensors, onnx, torchscript, joblib, etc.",
)
@click.option(
"--model-type",
type=str,
help=(
"Model type, e.g., `transformer`, `xgboost`, `custom`, etc. "
"For HuggingFace models, this is auto-determined from config.json['model_type']."
),
)
@click.option(
"--short-description",
type=str,
help="Short description of the model.",
)
@click.option(
"--allow-pattern",
"allow_patterns",
type=str,
multiple=True,
help=(
"Glob pattern selecting which repo files to prefetch, e.g. `*Q4_K_M*` to pull one GGUF "
"quant out of a repo that ships many. Can be specified multiple times. Omit to prefetch "
"the whole repo. Ignored when `--shard-config` is set."
),
)
@click.option(
"--ignore-pattern",
"ignore_patterns",
type=str,
multiple=True,
help="Glob pattern excluded from the prefetch, applied after `--allow-pattern`. Can be specified multiple times.",
)
@click.option(
"--force",
type=int,
default=0,
help="Force store of the model. Increment value (`--force`=1, `--force`=2, ...) to force a new store.",
)
@click.option(
"--wait",
is_flag=True,
help="Wait for the model to be stored before returning.",
)
@click.option(
"--hf-token-key",
type=str,
default="HF_TOKEN",
help=(
"Name of the Flyte secret containing your HuggingFace token. "
"Note: This is not the HuggingFace token itself, but the name of the "
"secret in the Flyte secret store."
),
show_default=True,
)
@click.option(
"--cpu",
type=str,
default="2",
help="CPU request for the prefetch task (e.g., `2`, `4`, '2,4' for 2-4 CPUs).",
)
@click.option(
"--mem",
type=str,
default="8Gi",
help="Memory request for the prefetch task (e.g., `16Gi`, `64Gi`, '16Gi,64Gi' for 16-64GB).",
)
@click.option(
"--gpu",
type=click.Choice(ACCELERATOR_CHOICES),
default=None,
help=(
"The gpu to use for downloading and (optionally) sharding the model. "
"Format: '{type}:{quantity}' (e.g., `A100:8`, `L4:1`)."
),
)
@click.option(
"--disk",
type=str,
default="50Gi",
help="Disk storage request for the prefetch task (e.g., `100Gi`, `500Gi`).",
)
@click.option(
"--shm",
type=str,
default=None,
help="Shared memory request for the prefetch task (e.g., `100Gi`, `auto`).",
)
@click.option(
"--shard-config",
type=click.Path(exists=True, path_type=Path),
help=(
"Path to a YAML file containing sharding configuration. "
"The file should have `engine` (currently only `vllm`) and `args` keys."
),
)
@click.pass_obj
def hf_model(
cfg,
repo: str,
raw_data_path: str | None,
artifact_name: str | None,
architecture: str | None,
task: str,
modality: tuple[str, ...],
serial_format: str | None,
model_type: str | None,
short_description: str | None,
allow_patterns: tuple[str, ...],
ignore_patterns: tuple[str, ...],
force: int,
wait: bool,
hf_token_key: str,
cpu: str | None,
mem: str | None,
disk: str | None,
gpu: Accelerators | None,
shm: str | None,
shard_config: Path | None,
project: str | None,
domain: str | None,
):
"""
Prefetch a HuggingFace model to Flyte storage.
Downloads a model from the HuggingFace Hub and prefetches it to your configured
Flyte storage backend. This is useful for:
- Pre-fetching large models before running inference tasks
- Sharding models for tensor-parallel inference
- Avoiding repeated downloads during development
**Basic Usage:**
```bash
$ flyte prefetch hf-model meta-llama/Llama-2-7b-hf --hf-token-key HF_TOKEN
```
**With Sharding:**
Create a shard config file (shard_config.yaml):
```yaml
engine: vllm
args:
tensor_parallel_size: 8
dtype: auto
trust_remote_code: true
```
Then run:
```bash
$ flyte prefetch hf-model meta-llama/Llama-2-70b-hf \\
--shard-config shard_config.yaml \\
--gpu A100:8 \\
--hf-token-key HF_TOKEN
```
**Wait for Completion:**
```bash
$ flyte prefetch hf-model meta-llama/Llama-2-7b-hf --wait
```
"""
import yaml
from flyte._resources import Resources
from flyte.cli._run import initialize_config
from flyte.prefetch import ShardConfig, VLLMShardArgs
from flyte.prefetch import hf_model as prefetch_hf_model
# Initialize flyte config
cfg = initialize_config(
cfg.ctx,
project or cfg.config.task.project,
domain or cfg.config.task.domain,
)
# Parse shard config if provided
parsed_shard_config = None
if shard_config is not None:
with shard_config.open() as f:
shard_config_dict = yaml.safe_load(f)
args_dict = shard_config_dict.get("args", {})
parsed_shard_config = ShardConfig(
engine=shard_config_dict.get("engine", "vllm"),
args=VLLMShardArgs(**args_dict),
)
console = Console()
console.print("[bold green]Starting model prefetch task...")
# Parse cpu and mem for range syntax (e.g., "2, 4" -> ("2", "4"))
parsed_cpu: str | tuple[str, str] | None = cpu
if cpu is not None:
cpu_parts = cpu.split(", ")
if len(cpu_parts) > 1:
parsed_cpu = (cpu_parts[0], cpu_parts[1])
parsed_mem: str | tuple[str, str] | None = mem
if mem is not None:
mem_parts = mem.split(", ")
if len(mem_parts) > 1:
parsed_mem = (mem_parts[0], mem_parts[1])
run = prefetch_hf_model(
repo=repo,
raw_data_path=raw_data_path,
artifact_name=artifact_name,
architecture=architecture,
task=task,
modality=modality,
serial_format=serial_format,
model_type=model_type,
short_description=short_description,
shard_config=parsed_shard_config,
allow_patterns=list(allow_patterns) or None,
ignore_patterns=list(ignore_patterns) or None,
hf_token_key=hf_token_key,
resources=Resources(cpu=parsed_cpu, memory=parsed_mem, disk=disk, gpu=gpu, shm=shm),
force=force,
)
url = run.url
console.print(
f"🔄 Started run {run.name} to prefetch model from HuggingFace repo [bold]{repo}[/bold].\n"
f" Check the console for status at [link={url}]{url}[/link]"
)
if wait:
run.wait()
try:
model_path = run.outputs()[0].path
console.print("\n✅ Model prefetched successfully!")
console.print(f"Remote path: [cyan]{model_path}[/cyan]")
except Exception as e:
console.print("\n❌ Model prefetch failed!")
console.print(f"Error: {e}")