kratadata commited on
Commit
9f4e218
·
verified ·
1 Parent(s): c2a7441

Upload folder via script

Browse files
Files changed (2) hide show
  1. velai/static/css/velai.css +68 -0
  2. velai/ui/training.py +68 -71
velai/static/css/velai.css CHANGED
@@ -14,4 +14,72 @@
14
 
15
  .vue-flow__resize-control.line {
16
  border-color: transparent !important;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
17
  }
 
14
 
15
  .vue-flow__resize-control.line {
16
  border-color: transparent !important;
17
+ }
18
+
19
+ /* LoRA dialog — match canvas graphite grey (#1c1d20), fit without scroll */
20
+ .velai-lora-dialog {
21
+ background: #1c1d20 !important;
22
+ color: #e5e7eb !important;
23
+ border: 1px solid #3c3e43;
24
+ box-shadow: 0 8px 24px rgba(0, 0, 0, 0.55);
25
+ max-height: min(92vh, 720px) !important;
26
+ overflow: hidden !important;
27
+ padding: 12px 16px !important;
28
+ }
29
+
30
+ .velai-lora-dialog .q-card__section,
31
+ .velai-lora-dialog > .q-card__section--vert {
32
+ padding: 0;
33
+ }
34
+
35
+ .velai-lora-train-body {
36
+ max-height: calc(min(92vh, 720px) - 96px);
37
+ overflow: hidden;
38
+ }
39
+
40
+ .velai-lora-upload .q-uploader__list {
41
+ max-height: 48px;
42
+ overflow: hidden;
43
+ }
44
+
45
+ .velai-lora-dialog .q-tab-panels,
46
+ .velai-lora-dialog .q-tab-panel {
47
+ background: transparent !important;
48
+ color: inherit;
49
+ }
50
+
51
+ .velai-lora-dialog .q-tabs {
52
+ background: transparent;
53
+ }
54
+
55
+ .velai-lora-dialog .q-tab {
56
+ color: #9ca3af;
57
+ }
58
+
59
+ .velai-lora-dialog .q-tab--active {
60
+ color: #e5e7eb;
61
+ }
62
+
63
+ .velai-lora-dialog .q-field__control,
64
+ .velai-lora-dialog .q-field__native,
65
+ .velai-lora-dialog .q-field__label {
66
+ color: #e5e7eb;
67
+ }
68
+
69
+ .velai-lora-dialog .q-uploader {
70
+ background: #2b2c2f;
71
+ color: #e5e7eb;
72
+ border: 1px solid #3c3e43;
73
+ }
74
+
75
+ .velai-lora-log {
76
+ background: #232427 !important;
77
+ color: #e5e7eb !important;
78
+ border: 1px solid #3c3e43;
79
+ }
80
+
81
+ .velai-lora-entry {
82
+ background: #2b2c2f !important;
83
+ color: #e5e7eb !important;
84
+ border: 1px solid #3c3e43;
85
  }
velai/ui/training.py CHANGED
@@ -22,74 +22,57 @@ class TrainingDialog:
22
  self.zip_name: str = ""
23
  self.uploaded_files: List[tuple[str, bytes]] = []
24
  self.is_ready_to_train = False
25
- self._advanced_open = False
26
  self.on_library_changed = on_library_changed
27
 
28
  self.dialog = ui.dialog().props("persistent")
29
 
30
- with self.dialog, ui.card().classes("w-[680px] max-w-[95vw]"):
31
- with ui.row().classes("w-full items-center justify-between"):
32
  with ui.column().classes("gap-0"):
33
- ui.label("Train / Manage LoRA").classes("text-xl font-bold")
34
- ui.label("Krea 2 Trainer").classes("text-sm text-gray-500")
35
  ui.button(icon="close", on_click=self.dialog.close).props("flat round dense")
36
 
37
  with ui.tabs().classes("w-full") as tabs:
38
  train_tab = ui.tab("Train")
39
  library_tab = ui.tab("My LoRAs")
40
 
41
- with ui.tab_panels(tabs, value=train_tab).classes("w-full"):
42
- with ui.tab_panel(train_tab):
43
  self._build_train_tab()
44
- with ui.tab_panel(library_tab):
45
  self._build_library_tab()
46
 
47
  def _build_train_tab(self) -> None:
48
- ui.label("1. Upload dataset (.zip)").classes("font-bold mt-2")
49
- ui.label(
50
- f"Zip of PNG/JPG/WebP images. Optional same-stem .txt captions "
51
- f"(e.g. 001.jpg + 001.txt). Max {MAX_DATASET_ZIP_MB} MB "
52
- f"(fal accepts larger archives; we cap uploads here for reliability)."
53
- ).classes("text-xs text-gray-500 mb-2")
54
-
55
- self.upload = (
56
- ui.upload(
57
- label="Upload zip",
58
- auto_upload=True,
59
- on_upload=self.handle_upload,
60
- multiple=False,
61
- )
62
- .props("accept=.zip,application/zip")
63
- .classes("w-full")
64
- )
65
-
66
- self.file_list_label = ui.label("No zip uploaded yet").classes("text-xs text-gray-500 mt-1")
67
-
68
- ui.label("2. Configuration").classes("font-bold mt-4")
69
- with ui.grid(columns=2).classes("w-full gap-4"):
70
- self.lora_name = ui.input("LoRA name", value="My Krea LoRA").classes("w-full")
71
- self.steps = ui.select(
72
- {1000: "1000 steps (default)", 2000: "2000 steps"},
73
- value=1000,
74
- label="Training steps",
75
- ).classes("w-full")
76
- self.trigger_phrase = ui.input(
77
- "Trigger phrase",
78
- placeholder="e.g. ohwx person",
79
- ).classes("w-full col-span-2")
80
  ui.label(
81
- "Required if images have no .txt captions and auto-captioning is Off."
82
- ).classes("text-xs text-gray-500 col-span-2")
83
-
84
- with ui.expansion("Advanced options", icon="tune").classes("w-full mt-2") as advanced:
85
- advanced.bind_value(self, "_advanced_open")
86
- with ui.grid(columns=2).classes("w-full gap-4"):
87
- self.learning_rate = ui.number("Learning rate", value=0.0005, format="%.5f", min=0.00001, max=0.01)
88
- self.resolution = ui.select(
89
- {768: "768", 1024: "1024"},
90
- value=768,
91
- label="Resolution",
92
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
93
  self.auto_captioning = ui.select(
94
  {
95
  "Off": "Off",
@@ -99,29 +82,43 @@ class TrainingDialog:
99
  },
100
  value="Off",
101
  label="Auto captioning",
102
- ).classes("col-span-2")
 
 
 
 
 
 
 
 
 
 
103
 
104
- self.train_button = ui.button("Start Training", on_click=self.start_training).classes("w-full mt-6")
105
- self.train_button.bind_enabled_from(self, "is_ready_to_train")
 
106
 
107
- ui.label("3. Progress & logs").classes("font-bold mt-4")
108
- self.log = ui.log().classes("w-full h-48 bg-gray-100 p-2 rounded text-xs font-mono")
109
 
110
- self.result_container = ui.column().classes("w-full mt-4")
111
- self.result_container.set_visibility(False)
112
- with self.result_container:
113
- ui.label("Training complete — LoRA saved to your library.").classes(
114
- "text-lg font-bold text-green-600"
115
- )
116
- with ui.row().classes("w-full gap-2"):
117
- self.download_lora_btn = ui.button("Download LoRA", icon="download")
118
- self.download_config_btn = ui.button("Download Config", icon="download")
 
 
 
119
 
120
  def _build_library_tab(self) -> None:
121
- ui.label("LoRAs linked to this browser session").classes("text-sm text-gray-500 mb-2")
122
  ui.label(
123
  "They persist across reloads via your session cookie. Upload a .safetensors file or train a new one."
124
- ).classes("text-xs text-gray-500 mb-2")
125
 
126
  self.lora_upload = (
127
  ui.upload(
@@ -145,17 +142,17 @@ class TrainingDialog:
145
  entries = list_loras(self._user_storage())
146
  with self.library_container:
147
  if not entries:
148
- ui.label("No LoRAs yet. Train one or upload weights.").classes("text-sm text-gray-500")
149
  return
150
  for entry in entries:
151
- with ui.card().classes("w-full"):
152
  with ui.row().classes("w-full items-center justify-between"):
153
  with ui.column().classes("gap-0"):
154
  ui.label(entry.name).classes("font-bold")
155
  meta = f"{entry.source} · {entry.created_at[:19]}"
156
  if entry.trigger_phrase:
157
  meta += f" · trigger: {entry.trigger_phrase}"
158
- ui.label(meta).classes("text-xs text-gray-500")
159
  with ui.row().classes("gap-1"):
160
  ui.button(
161
  icon="download",
@@ -309,7 +306,7 @@ class TrainingDialog:
309
  original_name=lora_file.get("file_name") or "krea_lora.safetensors",
310
  mime_type="application/octet-stream",
311
  )
312
- name = (self.lora_name.value or "My Krea LoRA").strip()
313
  add_lora(
314
  self._user_storage(),
315
  name=name,
 
22
  self.zip_name: str = ""
23
  self.uploaded_files: List[tuple[str, bytes]] = []
24
  self.is_ready_to_train = False
 
25
  self.on_library_changed = on_library_changed
26
 
27
  self.dialog = ui.dialog().props("persistent")
28
 
29
+ with self.dialog, ui.card().classes("w-[720px] max-w-[95vw] velai-lora-dialog"):
30
+ with ui.row().classes("w-full items-center justify-between mb-1"):
31
  with ui.column().classes("gap-0"):
32
+ ui.label("Train / Manage LoRA").classes("text-lg font-bold")
33
+ ui.label("Krea 2 Trainer").classes("text-xs text-gray-400")
34
  ui.button(icon="close", on_click=self.dialog.close).props("flat round dense")
35
 
36
  with ui.tabs().classes("w-full") as tabs:
37
  train_tab = ui.tab("Train")
38
  library_tab = ui.tab("My LoRAs")
39
 
40
+ with ui.tab_panels(tabs, value=train_tab).classes("w-full bg-transparent"):
41
+ with ui.tab_panel(train_tab).classes("p-0"):
42
  self._build_train_tab()
43
+ with ui.tab_panel(library_tab).classes("p-0"):
44
  self._build_library_tab()
45
 
46
  def _build_train_tab(self) -> None:
47
+ with ui.column().classes("w-full gap-2 velai-lora-train-body"):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
48
  ui.label(
49
+ f"Dataset zip (PNG/JPG/WebP, optional .txt captions). Max {MAX_DATASET_ZIP_MB} MB."
50
+ ).classes("text-xs text-gray-400")
51
+
52
+ self.upload = (
53
+ ui.upload(
54
+ label="Upload zip",
55
+ auto_upload=True,
56
+ on_upload=self.handle_upload,
57
+ multiple=False,
 
 
58
  )
59
+ .props("accept=.zip,application/zip dense")
60
+ .classes("w-full velai-lora-upload")
61
+ )
62
+ self.file_list_label = ui.label("No zip uploaded yet").classes("text-xs text-gray-400")
63
+
64
+ with ui.grid(columns=2).classes("w-full gap-x-3 gap-y-2"):
65
+ self.lora_name = ui.input("LoRA name", value="My LoRA").classes("w-full").props("dense")
66
+ with ui.column().classes("w-full gap-0 justify-end"):
67
+ self.steps_label = ui.label("Training steps: 1000").classes("text-xs")
68
+ self.steps = ui.slider(min=1000, max=2000, step=1000, value=1000).props("w-full")
69
+ self.steps.on_value_change(
70
+ lambda e: setattr(self.steps_label, "text", f"Training steps: {int(e.value)}")
71
+ )
72
+ self.trigger_phrase = ui.input(
73
+ "Trigger phrase",
74
+ placeholder="e.g. ohwx person",
75
+ ).classes("w-full").props("dense")
76
  self.auto_captioning = ui.select(
77
  {
78
  "Off": "Off",
 
82
  },
83
  value="Off",
84
  label="Auto captioning",
85
+ ).classes("w-full").props("dense")
86
+ self.learning_rate = (
87
+ ui.number("Learning rate", value=0.0005, format="%.5f", min=0.00001, max=0.01)
88
+ .classes("w-full")
89
+ .props("dense")
90
+ )
91
+ self.resolution = ui.select(
92
+ {768: "768", 1024: "1024"},
93
+ value=768,
94
+ label="Resolution",
95
+ ).classes("w-full").props("dense")
96
 
97
+ ui.label(
98
+ "Trigger phrase required if images have no .txt captions and auto-captioning is Off."
99
+ ).classes("text-xs text-gray-400")
100
 
101
+ self.train_button = ui.button("Start Training", on_click=self.start_training).classes("w-full")
102
+ self.train_button.bind_enabled_from(self, "is_ready_to_train")
103
 
104
+ ui.label("Progress").classes("text-xs font-bold")
105
+ self.log = ui.log().classes("w-full h-28 velai-lora-log p-2 rounded text-xs font-mono")
106
+
107
+ self.result_container = ui.column().classes("w-full gap-1")
108
+ self.result_container.set_visibility(False)
109
+ with self.result_container:
110
+ ui.label("Training complete — LoRA saved to your library.").classes(
111
+ "text-sm font-bold text-green-400"
112
+ )
113
+ with ui.row().classes("w-full gap-2"):
114
+ self.download_lora_btn = ui.button("Download LoRA", icon="download").props("dense")
115
+ self.download_config_btn = ui.button("Download Config", icon="download").props("dense")
116
 
117
  def _build_library_tab(self) -> None:
118
+ ui.label("LoRAs linked to this browser session").classes("text-sm text-gray-400 mb-2")
119
  ui.label(
120
  "They persist across reloads via your session cookie. Upload a .safetensors file or train a new one."
121
+ ).classes("text-xs text-gray-400 mb-2")
122
 
123
  self.lora_upload = (
124
  ui.upload(
 
142
  entries = list_loras(self._user_storage())
143
  with self.library_container:
144
  if not entries:
145
+ ui.label("No LoRAs yet. Train one or upload weights.").classes("text-sm text-gray-400")
146
  return
147
  for entry in entries:
148
+ with ui.card().classes("w-full velai-lora-entry"):
149
  with ui.row().classes("w-full items-center justify-between"):
150
  with ui.column().classes("gap-0"):
151
  ui.label(entry.name).classes("font-bold")
152
  meta = f"{entry.source} · {entry.created_at[:19]}"
153
  if entry.trigger_phrase:
154
  meta += f" · trigger: {entry.trigger_phrase}"
155
+ ui.label(meta).classes("text-xs text-gray-400")
156
  with ui.row().classes("gap-1"):
157
  ui.button(
158
  icon="download",
 
306
  original_name=lora_file.get("file_name") or "krea_lora.safetensors",
307
  mime_type="application/octet-stream",
308
  )
309
+ name = (self.lora_name.value or "My LoRA name").strip()
310
  add_lora(
311
  self._user_storage(),
312
  name=name,