Spaces:
Runtime error
Runtime error
Refactor app.py to improve regex replacements and UI components
Browse filesEnhance the regex replacement logic in several functions to use lambda functions for better readability and maintainability. Update the UI to include a configuration-only generation button, allowing users to generate metadata without starting the training process. This improves user experience by providing clearer options and streamlining the configuration workflow.
app.py
CHANGED
|
@@ -12,7 +12,7 @@ import json
|
|
| 12 |
|
| 13 |
import gradio as gr
|
| 14 |
import importlib
|
| 15 |
-
import spaces
|
| 16 |
|
| 17 |
# Local modules
|
| 18 |
from download_qwen_image_models import download_all_models, DEFAULT_MODELS_DIR
|
|
@@ -164,12 +164,14 @@ def _sync_dataset_config_jsonl(path: str, output_json: str) -> None:
|
|
| 164 |
base = os.path.dirname(path)
|
| 165 |
cache = os.path.join(base, "cache").replace("\\", "/")
|
| 166 |
|
| 167 |
-
|
|
|
|
| 168 |
if new == txt and "image_jsonl_file" not in txt:
|
| 169 |
new = txt.rstrip("\n") + f'\nimage_jsonl_file = "{output_json}"\n'
|
| 170 |
|
| 171 |
if re.search(r"(?m)^\s*cache_directory\s*=", new):
|
| 172 |
-
|
|
|
|
| 173 |
else:
|
| 174 |
new = new.rstrip("\n") + f'\ncache_directory = "{cache}"\n'
|
| 175 |
|
|
@@ -414,7 +416,7 @@ def _prepare_script(
|
|
| 414 |
)
|
| 415 |
|
| 416 |
for pat, val in replacements.items():
|
| 417 |
-
txt = re.sub(pat, val, txt, flags=re.MULTILINE)
|
| 418 |
|
| 419 |
# Inject CONTROL_FOLDER_i if provided (uncomment/override or append)
|
| 420 |
for i in range(8):
|
|
@@ -442,9 +444,10 @@ def _prepare_script(
|
|
| 442 |
|
| 443 |
# Point model paths to the selected models_root
|
| 444 |
def _replace_model_path(txt: str, key: str, rel: str) -> str:
|
|
|
|
| 445 |
return re.sub(
|
| 446 |
rf"--{key} \"[^\"]+\"",
|
| 447 |
-
|
| 448 |
txt,
|
| 449 |
)
|
| 450 |
|
|
@@ -455,9 +458,19 @@ def _prepare_script(
|
|
| 455 |
txt = _replace_model_path(txt, "dit", f"dit/{dit_filename}")
|
| 456 |
|
| 457 |
# Replace working dir for metadata generation to runtime /auto
|
| 458 |
-
txt = re.sub(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 459 |
# Ensure musubi-tuner path matches runtime location
|
| 460 |
-
txt = re.sub(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 461 |
|
| 462 |
# ZeroGPU compatibility: avoid spawning via 'accelerate launch'.
|
| 463 |
# Run the training module directly in-process so GPU stays attached
|
|
@@ -477,7 +490,8 @@ def _prepare_script(
|
|
| 477 |
txt = re.sub(r"--save_every_n_epochs\s+\d+",
|
| 478 |
f"--save_every_n_epochs {override_save_every}", txt)
|
| 479 |
if override_run_name:
|
| 480 |
-
|
|
|
|
| 481 |
|
| 482 |
# Inject prefix/suffix flags for metadata creation
|
| 483 |
extra_lines: List[str] = []
|
|
@@ -526,7 +540,7 @@ def _prepare_script(
|
|
| 526 |
pattern = rf"(?m)^\s*{name}\s*=.*$"
|
| 527 |
replacement = f'{name}="{value}"' if not str(value).isdigit() else f'{name}={value}'
|
| 528 |
if re.search(pattern, txt):
|
| 529 |
-
txt = re.sub(pattern, replacement, txt)
|
| 530 |
else:
|
| 531 |
txt = f"{replacement}\n" + txt
|
| 532 |
|
|
@@ -655,7 +669,7 @@ def _startup_install_musubi_deps() -> None:
|
|
| 655 |
print("[QIE] WARN: musubi-tuner installation failed. Continuing.")
|
| 656 |
|
| 657 |
|
| 658 |
-
@spaces.GPU
|
| 659 |
def run_training(
|
| 660 |
output_name: str,
|
| 661 |
caption: str,
|
|
@@ -723,8 +737,8 @@ def run_training(
|
|
| 723 |
# Auto-generate dataset directory name
|
| 724 |
import time
|
| 725 |
ds_name = f"dataset_{int(time.time())}"
|
| 726 |
-
ds_dir = os.path.join(DATA_ROOT_RUNTIME, ds_name)
|
| 727 |
-
run_out_dir = os.path.join(ds_dir, output_name.strip())
|
| 728 |
img_folder_name = DEFAULT_IMAGE_FOLDER
|
| 729 |
img_dir = os.path.join(ds_dir, img_folder_name)
|
| 730 |
os.makedirs(img_dir, exist_ok=True)
|
|
@@ -1013,8 +1027,6 @@ def build_ui() -> gr.Blocks:
|
|
| 1013 |
cr_w = gr.Number(label="Control resolution W", value=1024, precision=0)
|
| 1014 |
cr_h = gr.Number(label="Control resolution H", value=1024, precision=0)
|
| 1015 |
te_bs = gr.Number(label="TE cache batch size", value=16, precision=0)
|
| 1016 |
-
with gr.Row():
|
| 1017 |
-
config_only = gr.Checkbox(label="設定のみ生成 (学習しない)", value=False)
|
| 1018 |
|
| 1019 |
with gr.Accordion("Target Image", elem_classes=["pad-section_0"]):
|
| 1020 |
with gr.Group():
|
|
@@ -1112,11 +1124,13 @@ def build_ui() -> gr.Blocks:
|
|
| 1112 |
|
| 1113 |
# Models root / OUTPUT_DIR_BASE / DATASET_CONFIG are auto-resolved at runtime; no user input needed.
|
| 1114 |
|
| 1115 |
-
|
| 1116 |
-
|
|
|
|
|
|
|
| 1117 |
scripts_files = gr.Files(label="Scripts & Config (live)", interactive=False)
|
| 1118 |
-
logs = gr.Textbox(label="Logs", lines=20)
|
| 1119 |
ckpt_files = gr.Files(label="Checkpoints (live)", interactive=False)
|
|
|
|
| 1120 |
with gr.Row():
|
| 1121 |
refresh_scripts_btn = gr.Button("ファイルを再取得", variant="secondary")
|
| 1122 |
|
|
@@ -1133,6 +1147,9 @@ def build_ui() -> gr.Blocks:
|
|
| 1133 |
ctrl6_files.change(fn=_files_to_gallery, inputs=ctrl6_files, outputs=ctrl6_gallery)
|
| 1134 |
ctrl7_files.change(fn=_files_to_gallery, inputs=ctrl7_files, outputs=ctrl7_gallery)
|
| 1135 |
|
|
|
|
|
|
|
|
|
|
| 1136 |
run_btn.click(
|
| 1137 |
fn=run_training,
|
| 1138 |
inputs=[
|
|
@@ -1147,7 +1164,25 @@ def build_ui() -> gr.Blocks:
|
|
| 1147 |
ctrl7_files, ctrl7_prefix, ctrl7_suffix,
|
| 1148 |
lr_input, dim_input,
|
| 1149 |
tr_w, tr_h, train_bs, cr_w, cr_h, te_bs,
|
| 1150 |
-
seed_input, max_epochs, save_every,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1151 |
],
|
| 1152 |
outputs=[logs, ckpt_files, scripts_files, run_out_dir_box],
|
| 1153 |
)
|
|
|
|
| 12 |
|
| 13 |
import gradio as gr
|
| 14 |
import importlib
|
| 15 |
+
# import spaces
|
| 16 |
|
| 17 |
# Local modules
|
| 18 |
from download_qwen_image_models import download_all_models, DEFAULT_MODELS_DIR
|
|
|
|
| 164 |
base = os.path.dirname(path)
|
| 165 |
cache = os.path.join(base, "cache").replace("\\", "/")
|
| 166 |
|
| 167 |
+
image_line = f'image_jsonl_file = "{output_json}"'
|
| 168 |
+
new = re.sub(r"(?m)^\s*image_jsonl_file\s*=.*$", lambda _m, r=image_line: r, txt)
|
| 169 |
if new == txt and "image_jsonl_file" not in txt:
|
| 170 |
new = txt.rstrip("\n") + f'\nimage_jsonl_file = "{output_json}"\n'
|
| 171 |
|
| 172 |
if re.search(r"(?m)^\s*cache_directory\s*=", new):
|
| 173 |
+
cache_line = f'cache_directory = "{cache}"'
|
| 174 |
+
new = re.sub(r"(?m)^\s*cache_directory\s*=.*$", lambda _m, r=cache_line: r, new)
|
| 175 |
else:
|
| 176 |
new = new.rstrip("\n") + f'\ncache_directory = "{cache}"\n'
|
| 177 |
|
|
|
|
| 416 |
)
|
| 417 |
|
| 418 |
for pat, val in replacements.items():
|
| 419 |
+
txt = re.sub(pat, lambda _m, v=val: v, txt, flags=re.MULTILINE)
|
| 420 |
|
| 421 |
# Inject CONTROL_FOLDER_i if provided (uncomment/override or append)
|
| 422 |
for i in range(8):
|
|
|
|
| 444 |
|
| 445 |
# Point model paths to the selected models_root
|
| 446 |
def _replace_model_path(txt: str, key: str, rel: str) -> str:
|
| 447 |
+
repl = f"--{key} \"{models_root.rstrip('/')}/{rel}\""
|
| 448 |
return re.sub(
|
| 449 |
rf"--{key} \"[^\"]+\"",
|
| 450 |
+
lambda _m, r=repl: r,
|
| 451 |
txt,
|
| 452 |
)
|
| 453 |
|
|
|
|
| 458 |
txt = _replace_model_path(txt, "dit", f"dit/{dit_filename}")
|
| 459 |
|
| 460 |
# Replace working dir for metadata generation to runtime /auto
|
| 461 |
+
txt = re.sub(
|
| 462 |
+
r"^cd\s+/workspace/auto\s*$",
|
| 463 |
+
lambda _m: f"cd {AUTO_DIR_RUNTIME}",
|
| 464 |
+
txt,
|
| 465 |
+
flags=re.MULTILINE,
|
| 466 |
+
)
|
| 467 |
# Ensure musubi-tuner path matches runtime location
|
| 468 |
+
txt = re.sub(
|
| 469 |
+
r"^cd\s+/musubi-tuner\s*$",
|
| 470 |
+
lambda _m: f"cd {MUSUBI_TUNER_DIR_RUNTIME}",
|
| 471 |
+
txt,
|
| 472 |
+
flags=re.MULTILINE,
|
| 473 |
+
)
|
| 474 |
|
| 475 |
# ZeroGPU compatibility: avoid spawning via 'accelerate launch'.
|
| 476 |
# Run the training module directly in-process so GPU stays attached
|
|
|
|
| 490 |
txt = re.sub(r"--save_every_n_epochs\s+\d+",
|
| 491 |
f"--save_every_n_epochs {override_save_every}", txt)
|
| 492 |
if override_run_name:
|
| 493 |
+
repl = f"RUN_NAME={_bash_quote(override_run_name)}"
|
| 494 |
+
txt = re.sub(r"^RUN_NAME=.*$", lambda _m, r=repl: r, txt, flags=re.MULTILINE)
|
| 495 |
|
| 496 |
# Inject prefix/suffix flags for metadata creation
|
| 497 |
extra_lines: List[str] = []
|
|
|
|
| 540 |
pattern = rf"(?m)^\s*{name}\s*=.*$"
|
| 541 |
replacement = f'{name}="{value}"' if not str(value).isdigit() else f'{name}={value}'
|
| 542 |
if re.search(pattern, txt):
|
| 543 |
+
txt = re.sub(pattern, lambda _m, r=replacement: r, txt)
|
| 544 |
else:
|
| 545 |
txt = f"{replacement}\n" + txt
|
| 546 |
|
|
|
|
| 669 |
print("[QIE] WARN: musubi-tuner installation failed. Continuing.")
|
| 670 |
|
| 671 |
|
| 672 |
+
# @spaces.GPU
|
| 673 |
def run_training(
|
| 674 |
output_name: str,
|
| 675 |
caption: str,
|
|
|
|
| 737 |
# Auto-generate dataset directory name
|
| 738 |
import time
|
| 739 |
ds_name = f"dataset_{int(time.time())}"
|
| 740 |
+
ds_dir = os.path.abspath(os.path.join(DATA_ROOT_RUNTIME, ds_name))
|
| 741 |
+
run_out_dir = os.path.abspath(os.path.join(ds_dir, output_name.strip()))
|
| 742 |
img_folder_name = DEFAULT_IMAGE_FOLDER
|
| 743 |
img_dir = os.path.join(ds_dir, img_folder_name)
|
| 744 |
os.makedirs(img_dir, exist_ok=True)
|
|
|
|
| 1027 |
cr_w = gr.Number(label="Control resolution W", value=1024, precision=0)
|
| 1028 |
cr_h = gr.Number(label="Control resolution H", value=1024, precision=0)
|
| 1029 |
te_bs = gr.Number(label="TE cache batch size", value=16, precision=0)
|
|
|
|
|
|
|
| 1030 |
|
| 1031 |
with gr.Accordion("Target Image", elem_classes=["pad-section_0"]):
|
| 1032 |
with gr.Group():
|
|
|
|
| 1124 |
|
| 1125 |
# Models root / OUTPUT_DIR_BASE / DATASET_CONFIG are auto-resolved at runtime; no user input needed.
|
| 1126 |
|
| 1127 |
+
with gr.Row():
|
| 1128 |
+
run_btn = gr.Button("Start Training", variant="primary")
|
| 1129 |
+
config_btn = gr.Button("設定のみ生成", variant="secondary")
|
| 1130 |
+
run_out_dir_box = gr.Textbox(label="出力フォルダ", lines=1, interactive=False)
|
| 1131 |
scripts_files = gr.Files(label="Scripts & Config (live)", interactive=False)
|
|
|
|
| 1132 |
ckpt_files = gr.Files(label="Checkpoints (live)", interactive=False)
|
| 1133 |
+
logs = gr.Textbox(label="Logs", lines=20)
|
| 1134 |
with gr.Row():
|
| 1135 |
refresh_scripts_btn = gr.Button("ファイルを再取得", variant="secondary")
|
| 1136 |
|
|
|
|
| 1147 |
ctrl6_files.change(fn=_files_to_gallery, inputs=ctrl6_files, outputs=ctrl6_gallery)
|
| 1148 |
ctrl7_files.change(fn=_files_to_gallery, inputs=ctrl7_files, outputs=ctrl7_gallery)
|
| 1149 |
|
| 1150 |
+
config_only_off = gr.State(False)
|
| 1151 |
+
config_only_on = gr.State(True)
|
| 1152 |
+
|
| 1153 |
run_btn.click(
|
| 1154 |
fn=run_training,
|
| 1155 |
inputs=[
|
|
|
|
| 1164 |
ctrl7_files, ctrl7_prefix, ctrl7_suffix,
|
| 1165 |
lr_input, dim_input,
|
| 1166 |
tr_w, tr_h, train_bs, cr_w, cr_h, te_bs,
|
| 1167 |
+
seed_input, max_epochs, save_every, config_only_off,
|
| 1168 |
+
],
|
| 1169 |
+
outputs=[logs, ckpt_files, scripts_files, run_out_dir_box],
|
| 1170 |
+
)
|
| 1171 |
+
config_btn.click(
|
| 1172 |
+
fn=run_training,
|
| 1173 |
+
inputs=[
|
| 1174 |
+
output_name, caption, images_input, main_prefix, main_suffix,
|
| 1175 |
+
ctrl0_files, ctrl0_prefix, ctrl0_suffix,
|
| 1176 |
+
ctrl1_files, ctrl1_prefix, ctrl1_suffix,
|
| 1177 |
+
ctrl2_files, ctrl2_prefix, ctrl2_suffix,
|
| 1178 |
+
ctrl3_files, ctrl3_prefix, ctrl3_suffix,
|
| 1179 |
+
ctrl4_files, ctrl4_prefix, ctrl4_suffix,
|
| 1180 |
+
ctrl5_files, ctrl5_prefix, ctrl5_suffix,
|
| 1181 |
+
ctrl6_files, ctrl6_prefix, ctrl6_suffix,
|
| 1182 |
+
ctrl7_files, ctrl7_prefix, ctrl7_suffix,
|
| 1183 |
+
lr_input, dim_input,
|
| 1184 |
+
tr_w, tr_h, train_bs, cr_w, cr_h, te_bs,
|
| 1185 |
+
seed_input, max_epochs, save_every, config_only_on,
|
| 1186 |
],
|
| 1187 |
outputs=[logs, ckpt_files, scripts_files, run_out_dir_box],
|
| 1188 |
)
|