xiaoyao9184 commited on
Commit
d7deabd
·
verified ·
1 Parent(s): f47e6df

Synced repo using 'sync_with_huggingface' Github Action

Browse files
Files changed (3) hide show
  1. app.py +606 -0
  2. requirements.txt +13 -0
  3. run.py +7 -0
app.py ADDED
@@ -0,0 +1,606 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ import builtins
3
+ import logging
4
+ import os
5
+ import sys
6
+ import shutil
7
+ import uuid
8
+ import re
9
+ import contextvars
10
+ import requests
11
+ import gradio as gr
12
+ from huggingface_hub import HfApi, whoami
13
+ from onnxruntime_genai.models.builder import create_model
14
+ from dataclasses import dataclass, field
15
+ from pathlib import Path
16
+ from typing import Optional, Tuple, Callable
17
+ from enum import Enum
18
+ from tqdm import tqdm
19
+ from contextlib import suppress
20
+
21
+
22
+ class ExecutionProvider(Enum):
23
+ CPU = "cpu"
24
+ CUDA = "cuda"
25
+ ROCM = "rocm"
26
+ DML = "dml"
27
+ WEBGPU = "webgpu"
28
+ NVTENSORRT = "NvTensorRtRtx"
29
+
30
+ class Precision(Enum):
31
+ INT4 = "int4"
32
+ BF16 = "bf16"
33
+ FP16 = "fp16"
34
+ FP32 = "fp32"
35
+
36
+ EXECUTION_PROVIDERS = tuple(x.value for x in ExecutionProvider)
37
+
38
+ PRECISION_MODES = tuple(x.value for x in Precision)
39
+
40
+ EXECUTION_PROVIDER_PATH_MAPPING = {
41
+ ExecutionProvider.CPU: "cpu_and_mobile",
42
+ ExecutionProvider.CUDA: "cuda",
43
+ ExecutionProvider.DML: "directml"
44
+ }
45
+
46
+ NAME_EXTRA_OPTIONS_MAPPING = {
47
+ (ExecutionProvider.CPU,Precision.INT4): {
48
+ "rtn-block-32": {
49
+ "int4_algo_config": "rtn",
50
+ "int4_block_size": 32
51
+ },
52
+ "rtn-block-32-acc-level-4": {
53
+ "int4_algo_config": "rtn",
54
+ "int4_block_size": 32,
55
+ "int4_accuracy_level": 4
56
+ }
57
+ },
58
+
59
+ (ExecutionProvider.CUDA,Precision.FP16): {
60
+ "": {},
61
+ },
62
+ (ExecutionProvider.CUDA,Precision.INT4): {
63
+ "rtn-block-32": {
64
+ "int4_algo_config": "rtn",
65
+ "int4_block_size": 32
66
+ },
67
+ },
68
+
69
+ (ExecutionProvider.DML,Precision.INT4): {
70
+ "awq-block-128": {
71
+ "int4_algo_config": "awq",
72
+ "int4_block_size": 128
73
+ },
74
+ }
75
+ }
76
+
77
+ @dataclass
78
+ class Config:
79
+ """Application configuration."""
80
+
81
+ _id: Optional[str] = field(default=None, init=False)
82
+ _logger: Optional[logging.Logger] = field(default=None, init=False)
83
+ _logger_path: Optional[Path] = field(default=None, init=False)
84
+
85
+ hf_token: str
86
+ hf_username: str
87
+ is_using_user_token: bool
88
+ ignore_converted: bool = False
89
+
90
+ hf_base_url: str = "https://huggingface.co"
91
+ output_path: Path = Path("./models")
92
+ cache_path: Path = Path("./cache")
93
+ log_path: Path = Path("./logs")
94
+
95
+ @classmethod
96
+ def from_env(cls) -> "Config":
97
+ """Create config from environment variables and secrets."""
98
+ system_token = os.getenv("HF_TOKEN")
99
+
100
+ if system_token and system_token.startswith("/run/secrets/") and os.path.isfile(system_token):
101
+ with open(system_token, "r") as f:
102
+ system_token = f.read().strip()
103
+
104
+ hf_username = (
105
+ os.getenv("SPACE_AUTHOR_NAME") or whoami(token=system_token)["name"]
106
+ )
107
+
108
+ output_dir = os.getenv("OUTPUT_DIR") or "./models"
109
+ cache_dir = os.getenv("HUGGINGFACE_HUB_CACHE") or os.getenv("CACHE_DIR") or "./cache"
110
+ log_dir = os.getenv("LOG_DIR") or "./logs"
111
+
112
+ return cls(
113
+ hf_token=system_token,
114
+ hf_username=hf_username,
115
+ is_using_user_token=False,
116
+ ignore_converted=os.getenv("IGNORE_CONVERTED", "false") == "true",
117
+ output_path=Path(output_dir),
118
+ cache_path=Path(cache_dir),
119
+ log_path=Path(log_dir)
120
+ )
121
+
122
+ @property
123
+ def id(self):
124
+ if not self._id:
125
+ self._id = str(uuid.uuid4())
126
+ return self._id
127
+
128
+ @property
129
+ def logger(self) -> logging.Logger:
130
+ """Get logger."""
131
+ if not self._logger:
132
+ logger = logging.getLogger(self.id)
133
+ logger.setLevel(logging.INFO)
134
+ if not logger.handlers:
135
+ handler = logging.FileHandler(self.logger_path)
136
+ handler.setFormatter(logging.Formatter("[%(levelname)s] - %(message)s"))
137
+ logger.addHandler(handler)
138
+ self._logger = logger
139
+ return self._logger
140
+
141
+ @property
142
+ def logger_path(self) -> Path:
143
+ """Get logger path."""
144
+ if not self._logger_path:
145
+ logger_path = self.log_path / f"{self.id}.log"
146
+ logger_path.parent.mkdir(exist_ok=True)
147
+ self._logger_path = logger_path
148
+ return self._logger_path
149
+
150
+ def token(self, user_token):
151
+ """Update token."""
152
+ if user_token:
153
+ hf_username = whoami(token=user_token)["name"]
154
+ else:
155
+ hf_username = (
156
+ os.getenv("SPACE_AUTHOR_NAME") or whoami(token=self.hf_token)["name"]
157
+ )
158
+
159
+ hf_token = user_token or self.hf_token
160
+
161
+ if not hf_token:
162
+ raise ValueError(
163
+ "When the user token is not provided, the system token must be set."
164
+ )
165
+
166
+ self.hf_token = hf_token
167
+ self.hf_username = hf_username
168
+ self.is_using_user_token = bool(user_token)
169
+
170
+ class ProgressLogger:
171
+ """Logger with progress update."""
172
+
173
+ def __init__(self, logger: logging.Logger, updater: Callable[[int], None]):
174
+ self.logger = logger
175
+ self.updater = updater
176
+ self.last_progress = 1
177
+ self.last_message = None
178
+ self.write_count = 0
179
+
180
+ def update(self, percent):
181
+ if percent >= self.last_progress:
182
+ self.updater(percent - self.last_progress)
183
+ else:
184
+ self.updater(self.last_progress - percent)
185
+ self.last_progress = min(self.last_progress, percent)
186
+
187
+ def print(self, *args, **kwargs):
188
+ self.last_message = " ".join(str(arg) for arg in args)
189
+ if self.logger:
190
+ self.logger.info(self.last_message.removeprefix("\r"))
191
+
192
+ if self.last_message.startswith("\rProgress:"):
193
+ with suppress(Exception):
194
+ percent_str = self.last_message.strip().split()[-1].strip('%')
195
+ percent = float(percent_str)
196
+ self.update(percent)
197
+ self.last_progress = percent
198
+
199
+ def write(self, text, write):
200
+ match = re.search(r"pre-uploaded: \d+/\d+ \(([\d.]+)M/([\d.]+)M\)", text)
201
+ if match:
202
+ with suppress(Exception):
203
+ current = float(match.group(1))
204
+ total = float(match.group(2))
205
+ percent = current / total * 100
206
+ self.update(percent)
207
+ self.write_count += 1
208
+ # 60 count for each second
209
+ if self.write_count > 60:
210
+ self.write_count = 0
211
+ write(text)
212
+
213
+ class ModelConverter:
214
+ """Handles model conversion and upload operations."""
215
+
216
+ def __init__(self, config: Config, context: contextvars.ContextVar):
217
+ self.config = config
218
+ self.api = HfApi(token=config.hf_token)
219
+ self.context = context
220
+
221
+ def list_tasks(self):
222
+ for execution_provider in EXECUTION_PROVIDERS:
223
+ ep = ExecutionProvider(execution_provider)
224
+ for precision in PRECISION_MODES:
225
+ p = Precision(precision)
226
+ name_extra_options_map = NAME_EXTRA_OPTIONS_MAPPING.get((ep, p), {})
227
+ for name in name_extra_options_map.keys():
228
+ path_names = [ep.value, p.value]
229
+ if name:
230
+ path_names.append(name)
231
+ path_name = "-".join(path_names)
232
+ task_name = path_name
233
+
234
+ yield {
235
+ f"{task_name}": {
236
+ "🔁 Conversion": "⏳",
237
+ "📤 Upload": "⏳"
238
+ }
239
+ }
240
+
241
+ def convert_model(
242
+ self, input_model_id: str, output_model_id: str, progress_updater: Callable[[int], None]
243
+ ) -> Tuple[bool, Optional[str]]:
244
+ """Convert the model to ONNX format."""
245
+ input_dir = ""
246
+ cache_dir = str(self.config.cache_path.absolute())
247
+ output_dir = str(self.config.output_path.absolute() / output_model_id)
248
+
249
+ yield f"🧠 Model id: {output_model_id}"
250
+
251
+ for execution_provider in (progress_provider := tqdm(EXECUTION_PROVIDERS, disable=False)):
252
+ progress_provider.set_description(f" Execution provider: {execution_provider}")
253
+
254
+ ep = ExecutionProvider(execution_provider)
255
+ path_provider = EXECUTION_PROVIDER_PATH_MAPPING.get(ep, ep.value)
256
+
257
+ for precision in (progress_precision := tqdm(PRECISION_MODES, disable=False)):
258
+ progress_precision.set_description(f" Precision: {precision}")
259
+
260
+ p = Precision(precision)
261
+ name_extra_options_map = NAME_EXTRA_OPTIONS_MAPPING.get((ep, p), {})
262
+
263
+ for name in (progress_name := tqdm(name_extra_options_map.keys(), disable=False, initial=1, total=len(name_extra_options_map))):
264
+ progress_name.set_description(f" Name: {name}")
265
+
266
+ path_names = [ep.value, p.value]
267
+ if name:
268
+ path_names.append(name)
269
+ path_name = "-".join(path_names)
270
+ task_name = path_name
271
+
272
+ output_path = os.path.join(
273
+ output_dir,
274
+ path_provider,
275
+ path_name,
276
+ )
277
+
278
+ extra_options = name_extra_options_map[name]
279
+ extra_options['hf_token'] = "false" if self.config.hf_token == None else self.config.hf_token
280
+
281
+ try:
282
+ yield {
283
+ f"{task_name}": {
284
+ "🔁 Conversion": "🟢"
285
+ }
286
+ }
287
+ self.context.set(ProgressLogger(self.config.logger, progress_updater))
288
+ for progress_fake in (_ := tqdm(range(100), disable=False)):
289
+ if progress_fake == 0:
290
+ create_model(
291
+ input_model_id, input_dir, output_path, precision, execution_provider, cache_dir, **extra_options
292
+ )
293
+ yield {
294
+ f"{task_name}": {
295
+ "🔁 Conversion": "✅"
296
+ }
297
+ }
298
+ except Exception as e:
299
+ yield {
300
+ f"{task_name}": {
301
+ "🔁 Conversion": "❌"
302
+ }
303
+ }
304
+ raise e
305
+ return output_dir
306
+
307
+ def upload_model(
308
+ self, input_model_id: str, output_model_id: str, progress_updater: Callable[[int], None]
309
+ ) -> Optional[str]:
310
+ """Upload the converted model to Hugging Face."""
311
+ model_folder_path = self.config.output_path / output_model_id
312
+ hf_model_url = f"{self.config.hf_base_url}/{output_model_id}"
313
+
314
+ try:
315
+ self.api.create_repo(output_model_id, exist_ok=True, private=False)
316
+ yield f"🤗 Hugging Face model [{output_model_id}]({hf_model_url})"
317
+
318
+ readme_path = f"{model_folder_path}/README.md"
319
+ if not os.path.exists(readme_path):
320
+ with open(readme_path, "w") as file:
321
+ file.write(self.generate_readme(input_model_id))
322
+ self.context.set(ProgressLogger(self.config.logger, progress_updater))
323
+ self.api.upload_file(
324
+ repo_id=output_model_id,
325
+ path_or_fileobj=readme_path,
326
+ path_in_repo="README.md"
327
+ )
328
+ yield f"🪪 Model card [README.md]({hf_model_url}/README.md)"
329
+
330
+ for execution_provider in (progress_provider := tqdm(EXECUTION_PROVIDERS, disable=False)):
331
+ ep = ExecutionProvider(execution_provider)
332
+ path_provider = EXECUTION_PROVIDER_PATH_MAPPING.get(ep, ep.value)
333
+ for precision in (progress_precision := tqdm(PRECISION_MODES, disable=False)):
334
+ p = Precision(precision)
335
+ name_extra_options_map = NAME_EXTRA_OPTIONS_MAPPING.get((ep, p), {})
336
+ for name in (progress_name := tqdm(name_extra_options_map.keys(), disable=False, initial=1, total=len(name_extra_options_map))):
337
+ path_names = [ep.value, p.value]
338
+ if name:
339
+ path_names.append(name)
340
+ path_name = "-".join(path_names)
341
+ task_name = path_name
342
+
343
+ allow_patterns = os.path.join(
344
+ path_provider,
345
+ path_name,
346
+ "**"
347
+ )
348
+ folder_path = str(model_folder_path)
349
+
350
+ try:
351
+ yield {
352
+ f"{task_name}": {
353
+ "📤 Upload": "🟢"
354
+ }
355
+ }
356
+ self.context.set(ProgressLogger(self.config.logger, progress_updater))
357
+ for progress_fake in (_ := tqdm(range(100), disable=False)):
358
+ if progress_fake == 0:
359
+ self.api.upload_large_folder(
360
+ repo_id=output_model_id, folder_path=folder_path, allow_patterns=allow_patterns,
361
+ repo_type="model", print_report_every=1
362
+ )
363
+ yield {
364
+ f"{task_name}": {
365
+ "📤 Upload": "✅"
366
+ }
367
+ }
368
+ except Exception as e:
369
+ yield {
370
+ f"{task_name}": {
371
+ "📤 Upload": "❌"
372
+ }
373
+ }
374
+ raise e
375
+ return hf_model_url
376
+ except Exception as e:
377
+ raise e
378
+ finally:
379
+ shutil.rmtree(model_folder_path, ignore_errors=True)
380
+
381
+ def generate_readme(self, imi: str):
382
+ return (
383
+ "---\n"
384
+ "library_name: onnxruntime-genai\n"
385
+ "base_model:\n"
386
+ f"- {imi}\n"
387
+ "---\n\n"
388
+ f"# {imi.split('/')[-1]} (ONNX Runtime GenAI)\n\n"
389
+ f"This is an ONNX Runtime GenAI version of [{imi}](https://huggingface.co/{imi}). "
390
+ "It was automatically converted and uploaded using "
391
+ "[this space](https://huggingface.co/spaces/xiaoyao9184/convert-to-genai).\n"
392
+ )
393
+
394
+ class MessageHolder:
395
+ """hold messages for model conversion and upload operations."""
396
+
397
+ def __init__(self):
398
+ self.str_messages = []
399
+ self.dict_messages = {}
400
+
401
+ def add(self, msg):
402
+ if isinstance(msg, str):
403
+ self.str_messages.append(msg)
404
+ else:
405
+ # msg: {
406
+ # f"{execution_provider}-{precision}-{name}": {
407
+ # "🔁 Conversion": "⏳",
408
+ # "📤 Upload": "⏳"
409
+ # }
410
+ # }
411
+ for name, value in msg.items():
412
+ if name not in self.dict_messages:
413
+ self.dict_messages[name] = value
414
+ self.dict_messages[name].update(value)
415
+ return self
416
+
417
+ def markdown(self):
418
+ all_keys = list(dict.fromkeys(
419
+ key for value in self.dict_messages.values() for key in value
420
+ ))
421
+
422
+ header = "| Name | " + " | ".join(all_keys) + " |"
423
+ divider = "|------|" + "|".join(["------"] * len(all_keys)) + "|"
424
+ rows = []
425
+ for name, steps in self.dict_messages.items():
426
+ row = [f"`{name}`"]
427
+ for key in all_keys:
428
+ row.append(steps.get(key, ""))
429
+ rows.append("| " + " | ".join(row) + " |")
430
+
431
+ lines = []
432
+ for msg in self.str_messages:
433
+ lines.append("")
434
+ lines.append(msg)
435
+ if rows:
436
+ lines.append("")
437
+ lines.append(header)
438
+ lines.append(divider)
439
+ lines.extend(rows)
440
+
441
+ return "\n".join(lines)
442
+
443
+ class RedirectHandler(logging.Handler):
444
+ """Handles logging redirection to progress logger."""
445
+
446
+ def __init__(self, context: contextvars.ContextVar = None):
447
+ super().__init__(logging.NOTSET)
448
+ self.context = context
449
+
450
+ def emit(self, record: logging.LogRecord):
451
+ progress_logger = self.context.get(None)
452
+
453
+ if progress_logger:
454
+ try:
455
+ progress_logger.logger.handle(record)
456
+ except Exception as e:
457
+ logging.getLogger(__name__).debug(f"Failed to forward log: {e}")
458
+ else:
459
+ logging.getLogger(__name__).handle(record)
460
+
461
+ if __name__ == "__main__":
462
+ # context progress logger
463
+ progress_logger_ctx = contextvars.ContextVar("progress_logger", default=None)
464
+
465
+ # default config log_path
466
+ config = Config.from_env()
467
+ log_path = config.log_path / 'ui.log'
468
+ logger = logging.getLogger(__name__)
469
+ logger.setLevel(logging.INFO)
470
+ logger.addHandler(logging.FileHandler(log_path))
471
+ logger.info("Gradio UI started")
472
+
473
+ with gr.Blocks() as demo:
474
+ gr_user_config = gr.State(config)
475
+ gr.Markdown("## 🤗 Convert HuggingFace Models to ONNX (ONNX Runtime GenAI Version)")
476
+ gr_input_model_id = gr.Textbox(label="Model ID", info="e.g. microsoft/Phi-3-mini-4k-instruct")
477
+ gr_user_token = gr.Textbox(label="HF Token (Optional)", type="password", visible=False)
478
+ gr_same_repo = gr.Checkbox(label="Upload to same repo (if you own it)", visible=False, info="Do you want to upload the ONNX weights to the same repository?")
479
+ gr_proceed = gr.Button("Convert and Upload", interactive=False)
480
+ gr_result = gr.Markdown("")
481
+
482
+ gr_input_model_id.change(
483
+ fn=lambda x: [gr.update(visible=x != ""), gr.update(interactive=x != "")],
484
+ inputs=[gr_input_model_id],
485
+ outputs=[gr_user_token, gr_proceed],
486
+ api_name=False
487
+ )
488
+
489
+ def change_user_token(input_model_id, user_hf_token, user_config):
490
+ # update hf_token
491
+ try:
492
+ user_config.token(user_hf_token)
493
+ except Exception as e:
494
+ gr.Error(str(e), duration=5)
495
+ if user_hf_token != "":
496
+ if user_config.hf_username == input_model_id.split("/")[0]:
497
+ return [gr.update(visible=True), user_config]
498
+ return [gr.update(visible=False), user_config]
499
+ gr_user_token.change(
500
+ fn=change_user_token,
501
+ inputs=[gr_input_model_id, gr_user_token, gr_user_config],
502
+ outputs=[gr_same_repo, gr_user_config],
503
+ api_name=False
504
+ )
505
+
506
+ def click_proceed(input_model_id, same_repo, user_config, progress=gr.Progress(track_tqdm=True)):
507
+ converter = ModelConverter(user_config, progress_logger_ctx)
508
+ holder = MessageHolder()
509
+
510
+ input_model_id = input_model_id.strip()
511
+ model_name = input_model_id.split("/")[-1]
512
+ output_model_id = f"{user_config.hf_username}/{model_name}"
513
+
514
+ if not same_repo:
515
+ output_model_id += "-onnx-genai"
516
+ if not same_repo and converter.api.repo_exists(output_model_id):
517
+ yield gr.update(interactive=True), "This model has already been converted! 🎉"
518
+ if user_config.ignore_converted:
519
+ yield gr.update(interactive=True), "Ignore it, continue..."
520
+ else:
521
+ return
522
+
523
+ # update markdown
524
+ for task in converter.list_tasks():
525
+ yield gr.update(interactive=False), holder.add(task).markdown()
526
+
527
+ # update log
528
+ logger = user_config.logger
529
+ logger_path = user_config.logger_path
530
+ logger.info(f"Log file: {logger_path}")
531
+ yield gr.update(interactive=False), \
532
+ holder.add(f"# 📄 Log file [{user_config.id}](./gradio_api/file={logger_path})").markdown()
533
+
534
+ # update counter
535
+ with suppress(Exception):
536
+ requests.get("https://counterapi.com/api/xiaoyao9184.github.com/view/convert-to-genai")
537
+
538
+ # update markdown
539
+ logger.info("Conversion started...")
540
+ gen = converter.convert_model(
541
+ input_model_id, output_model_id, lambda n=-1: progress.update(n)
542
+ )
543
+ try:
544
+ while True:
545
+ msg = next(gen)
546
+ yield gr.update(interactive=False), holder.add(msg).markdown()
547
+ except StopIteration as e:
548
+ output_dir = e.value
549
+ yield gr.update(interactive=True), \
550
+ holder.add(f"🔁 Conversion successful✅! 📁 output to {output_dir}").markdown()
551
+ except Exception as e:
552
+ logger.exception(e)
553
+ yield gr.update(interactive=True), holder.add("🔁 Conversion failed🚫").markdown()
554
+ return
555
+
556
+ # update markdown
557
+ logger.info("Upload started...")
558
+ gen = converter.upload_model(input_model_id, output_model_id, lambda n=-1: progress.update(n))
559
+ try:
560
+ while True:
561
+ msg = next(gen)
562
+ yield gr.update(interactive=False), holder.add(msg).markdown()
563
+ except StopIteration as e:
564
+ output_model_url = f"{user_config.hf_base_url}/{output_model_id}"
565
+ yield gr.update(interactive=True), \
566
+ holder.add(f"📤 Upload successful✅! 📦 Go to [{output_model_id}]({output_model_url})").markdown()
567
+ except Exception as e:
568
+ logger.exception(e)
569
+ yield gr.update(interactive=True), holder.add("📤 Upload failed🚫").markdown()
570
+ return
571
+ gr_proceed.click(
572
+ fn=click_proceed,
573
+ inputs=[gr_input_model_id, gr_same_repo, gr_user_config],
574
+ outputs=[gr_proceed, gr_result]
575
+ )
576
+
577
+ if __name__ == "__main__":
578
+ # redirect builtins.print to context progress logger
579
+ def context_aware_print(*args, **kwargs):
580
+ progress_logger = progress_logger_ctx.get(None)
581
+ if progress_logger:
582
+ progress_logger.print(*args, **kwargs)
583
+ else:
584
+ builtins._original_print(*args, **kwargs)
585
+ builtins._original_print = builtins.print
586
+ builtins.print = context_aware_print
587
+
588
+ # redirect sys.stdout.write to context progress logger
589
+ def context_aware_write(text):
590
+ progress_logger = progress_logger_ctx.get(None)
591
+ if progress_logger:
592
+ progress_logger.write(text.rstrip(), sys.stdout._original_write)
593
+ else:
594
+ sys.stdout._original_write(text)
595
+ sys.stdout._original_write = sys.stdout.write
596
+ sys.stdout.write = context_aware_write
597
+
598
+ # redirect logger to context progress logger
599
+ handler = RedirectHandler(progress_logger_ctx)
600
+ for logger in [logging.getLogger("onnxruntime"), logging.getLogger("huggingface_hub.hf_api")]:
601
+ logger.handlers.clear()
602
+ logger.addHandler(handler)
603
+ logger.setLevel(logger.level)
604
+ logger.propagate = False
605
+
606
+ demo.launch(server_name="0.0.0.0", allowed_paths=[os.path.realpath(config.log_path.parent)])
requirements.txt ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ torch==2.7.0
2
+ transformers==4.52.4
3
+ onnx==1.18.0
4
+ onnxruntime==1.22.0
5
+ onnxruntime-genai==0.8.2
6
+ gradio==5.34.2
7
+
8
+ # need for cpu-int4-rtn-block-32
9
+ neural-compressor==2.4.1
10
+ # fix neural-compressor dependency
11
+ numpy==2.2.6
12
+ # need by transformers
13
+ tokenizers==0.21.1
run.py ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ # NOTE: copy from gradio bin
2
+ import re
3
+ import sys
4
+ from gradio.cli import cli
5
+ if __name__ == '__main__':
6
+ sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
7
+ sys.exit(cli())