bebechien commited on
Commit
1a70d3c
·
verified ·
1 Parent(s): e7dbe77

Fix repo_id

Browse files
Files changed (1) hide show
  1. ui.py +100 -48
ui.py CHANGED
@@ -1,5 +1,6 @@
1
  import gradio as gr
2
  from typing import Optional, Tuple, Generator, List, Any
 
3
  from config import AppConfig
4
  from engine import FunctionGemmaEngine
5
 
@@ -12,22 +13,49 @@ class UIController:
12
  """
13
 
14
  @staticmethod
15
- def init_session(profile: Optional[gr.OAuthProfile] = None) -> Tuple[Any, ...]:
16
  config = AppConfig()
17
  new_engine = FunctionGemmaEngine(config)
18
  username = profile.username if profile else None
19
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
20
  # Calculate initial interactivity state
21
- repo_update, push_update, zip_update = UIController.update_hub_interactive(new_engine, username)
 
 
 
 
 
 
 
22
 
23
  return (
24
  new_engine,
25
  new_engine.get_tools_json(),
26
  new_engine.config.MODEL_NAME,
27
  f"Ready. (Session {new_engine.session_id})",
28
- repo_update,
29
- push_update,
30
- zip_update,
 
31
  username
32
  )
33
 
@@ -76,23 +104,29 @@ class UIController:
76
  return gr.update(value=None, visible=False)
77
 
78
  @staticmethod
79
- def upload_model(engine: FunctionGemmaEngine, repo_name: str, oauth_token: Optional[gr.OAuthToken]) -> str:
80
  if oauth_token is None:
81
  return "❌ Error: You must log in (top right) to upload models."
82
- if not repo_name:
83
- return "❌ Error: Please enter a repository name."
 
 
 
 
 
84
 
85
  return engine.upload_model_to_hub(
86
- repo_name=repo_name,
87
  oauth_token=oauth_token.token,
88
  )
89
 
90
  @staticmethod
91
- def update_repo_preview(username: Optional[str], repo_name: str) -> str:
92
- if not username:
93
- return "⚠️ Sign in to see the target repository path."
94
- clean_repo = repo_name.strip() if repo_name else "..."
95
- return f"Target Repository: **`{username}/{clean_repo}`**"
 
96
 
97
  @staticmethod
98
  def update_hub_interactive(engine: Optional[FunctionGemmaEngine], username: Optional[str] = None):
@@ -100,9 +134,10 @@ class UIController:
100
  has_model_tuned = engine is not None and getattr(engine, 'has_model_tuned', False)
101
 
102
  return (
103
- gr.update(interactive=is_logged_in),
104
- gr.update(interactive=is_logged_in and has_model_tuned),
105
- gr.update(interactive=has_model_tuned)
 
106
  )
107
 
108
  # --- View / Layout Layer ---
@@ -190,17 +225,23 @@ def _render_export_tab(engine_state, username_state):
190
 
191
  with gr.Column():
192
  gr.Markdown("#### Option B: Save to Hugging Face Hub")
193
- gr.Markdown("Publish your fine-tuned model to your personal Hugging Face account.")
194
- repo_name_input = gr.Textbox(
195
- label="Target Repository Name", value="functiongemma-270m-it-tuning-lab", placeholder="e.g., functiongemma-270m-it-tuned", interactive=False
196
- )
 
 
 
 
 
 
197
  push_to_hub_btn = gr.Button("Save to Hugging Face Hub", variant="secondary", interactive=False)
198
  repo_id_preview = gr.Markdown("Target Repository: (Waiting for input...)")
199
  upload_status = gr.Markdown("")
200
 
201
  return {
202
  "zip_controls": [zip_btn, download_file],
203
- "hub_controls": [repo_name_input, push_to_hub_btn, repo_id_preview, upload_status]
204
  }
205
 
206
  # --- Main Build Function ---
@@ -218,19 +259,22 @@ def build_interface() -> gr.Blocks:
218
  export_ui = _render_export_tab(engine_state, username_state)
219
 
220
  # Helpers for UI State
221
- # 'action_buttons' now ONLY contains buttons that should always be enabled after a process
222
- # Zip and Push buttons are excluded here because their state depends on has_model_tuned
223
  run_btn, stop_btn, reload_btn, eval_btn = train_ui["buttons"]
224
  action_buttons = [reload_btn, run_btn, eval_btn]
225
 
226
- repo_input = export_ui["hub_controls"][0]
227
- push_btn = export_ui["hub_controls"][1]
 
 
228
  zip_btn = export_ui["zip_controls"][0]
229
 
 
 
 
230
  def lock_ui():
231
  """Locks all buttons (including Zip/Push) during processing"""
232
  return [gr.update(interactive=False) for _ in action_buttons] + \
233
- [gr.update(interactive=False), gr.update(interactive=False)]
234
 
235
  def unlock_ui():
236
  """Unlocks general action buttons only. Zip/Push are handled by update_hub_interactive"""
@@ -239,23 +283,25 @@ def build_interface() -> gr.Blocks:
239
  # --- Event Wiring ---
240
 
241
  # 1. Initialization
242
- demo.load(lock_ui, outputs=action_buttons + [push_btn, zip_btn]).then(
 
243
  fn=UIController.init_session,
244
- inputs=None,
245
  outputs=[
246
  engine_state,
247
  data_ui["tools_editor"],
248
  train_ui["model_input"],
249
  train_ui["outputs"][0], # log output
250
- repo_input,
 
251
  push_btn,
252
- zip_btn, # Update Zip state based on initial engine state
253
  username_state
254
  ]
255
  ).then(
256
  fn=UIController.update_repo_preview,
257
- inputs=[username_state, repo_input],
258
- outputs=[export_ui["hub_controls"][2]]
259
  ).then(unlock_ui, outputs=action_buttons)
260
 
261
  # 2. Data Tab
@@ -279,10 +325,10 @@ def build_interface() -> gr.Blocks:
279
  gr.update(visible=False),
280
  gr.update(interactive=False), # Lock Reload
281
  gr.update(interactive=False), # Lock Eval
282
- gr.update(interactive=False), # Lock Zip
283
  gr.update(visible=True) # Show Stop
284
  ),
285
- outputs=[run_btn, reload_btn, eval_btn, zip_btn, stop_btn]
286
  )
287
  train_run_event = train_run_event.then(
288
  fn=UIController.run_training,
@@ -300,7 +346,7 @@ def build_interface() -> gr.Blocks:
300
  # Final check determines if Zip/Push should unlock
301
  fn=UIController.update_hub_interactive,
302
  inputs=[engine_state, username_state],
303
- outputs=[repo_input, push_btn, zip_btn]
304
  )
305
 
306
  # 3b. Evaluation
@@ -308,7 +354,7 @@ def build_interface() -> gr.Blocks:
308
  fn=lambda: (
309
  gr.update(interactive=False), # Lock Run
310
  gr.update(interactive=False), # Lock Reload
311
- gr.update(visible=False), # Hide self (optional, or lock)
312
  gr.update(visible=True) # Show Stop
313
  ),
314
  outputs=[run_btn, reload_btn, eval_btn, stop_btn]
@@ -335,41 +381,47 @@ def build_interface() -> gr.Blocks:
335
  queue=False
336
  )
337
 
338
- reload_btn.click(lock_ui, outputs=action_buttons + [push_btn, zip_btn]).then(
339
  fn=UIController.handle_reset,
340
  inputs=[engine_state, train_ui["model_input"]],
341
  outputs=[train_ui["outputs"][0]]
342
  ).then(unlock_ui, outputs=action_buttons).then(
343
  fn=UIController.update_hub_interactive,
344
  inputs=[engine_state, username_state],
345
- outputs=[repo_input, push_btn, zip_btn]
346
  )
347
 
348
  # 4. Export Tab
349
- zip_btn.click(lock_ui, outputs=action_buttons + [push_btn, zip_btn]).then(
350
  fn=UIController.zip_model,
351
  inputs=[engine_state],
352
  outputs=[export_ui["zip_controls"][1]]
353
  ).then(unlock_ui, outputs=action_buttons).then(
354
  fn=UIController.update_hub_interactive,
355
  inputs=[engine_state, username_state],
356
- outputs=[repo_input, push_btn, zip_btn]
357
  )
358
 
359
- repo_input.change(
 
 
 
 
 
 
360
  fn=UIController.update_repo_preview,
361
- inputs=[username_state, repo_input],
362
- outputs=[export_ui["hub_controls"][2]]
363
  )
364
 
365
- push_btn.click(lock_ui, outputs=action_buttons + [push_btn, zip_btn]).then(
366
  fn=UIController.upload_model,
367
- inputs=[engine_state, repo_input],
368
- outputs=[export_ui["hub_controls"][3]]
369
  ).then(unlock_ui, outputs=action_buttons).then(
370
  fn=UIController.update_hub_interactive,
371
  inputs=[engine_state, username_state],
372
- outputs=[repo_input, push_btn, zip_btn]
373
  )
374
 
375
  return demo
 
1
  import gradio as gr
2
  from typing import Optional, Tuple, Generator, List, Any
3
+ from huggingface_hub import HfApi
4
  from config import AppConfig
5
  from engine import FunctionGemmaEngine
6
 
 
13
  """
14
 
15
  @staticmethod
16
+ def init_session(profile: Optional[gr.OAuthProfile] = None, token: Optional[gr.OAuthToken] = None) -> Tuple[Any, ...]:
17
  config = AppConfig()
18
  new_engine = FunctionGemmaEngine(config)
19
  username = profile.username if profile else None
20
 
21
+ # Fetch available namespaces (User + Orgs) if logged in
22
+ namespaces = []
23
+ default_owner = None
24
+
25
+ if token:
26
+ try:
27
+ api = HfApi(token=token.token)
28
+ user_info = api.whoami()
29
+ # Add user's own namespace first
30
+ namespaces.append(user_info['name'])
31
+ # Add organizations
32
+ if 'orgs' in user_info:
33
+ namespaces.extend([org['name'] for org in user_info['orgs']])
34
+
35
+ default_owner = namespaces[0] if namespaces else None
36
+ except Exception as e:
37
+ print(f"Error fetching namespaces: {e}")
38
+ pass
39
+
40
  # Calculate initial interactivity state
41
+ repo_update, org_interactivity, push_update, zip_update = UIController.update_hub_interactive(new_engine, username)
42
+
43
+ # Combine interactivity with the fetched choices for the Org Dropdown
44
+ final_org_update = gr.update(
45
+ choices=namespaces,
46
+ value=default_owner,
47
+ interactive=org_interactivity['interactive']
48
+ )
49
 
50
  return (
51
  new_engine,
52
  new_engine.get_tools_json(),
53
  new_engine.config.MODEL_NAME,
54
  f"Ready. (Session {new_engine.session_id})",
55
+ repo_update, # Model Name Input
56
+ final_org_update, # Owner Dropdown
57
+ push_update, # Push Button
58
+ zip_update, # Zip Button
59
  username
60
  )
61
 
 
104
  return gr.update(value=None, visible=False)
105
 
106
  @staticmethod
107
+ def upload_model(engine: FunctionGemmaEngine, owner: str, model_name: str, oauth_token: Optional[gr.OAuthToken]) -> str:
108
  if oauth_token is None:
109
  return "❌ Error: You must log in (top right) to upload models."
110
+ if not owner:
111
+ return "❌ Error: Please select an Owner (User or Organization)."
112
+ if not model_name:
113
+ return "❌ Error: Please enter a model name."
114
+
115
+ # Construct the full repo_id (e.g. "google/functiongemma-tuned")
116
+ full_repo_id = f"{owner}/{model_name.strip()}"
117
 
118
  return engine.upload_model_to_hub(
119
+ repo_name=full_repo_id,
120
  oauth_token=oauth_token.token,
121
  )
122
 
123
  @staticmethod
124
+ def update_repo_preview(owner: str, model_name: str) -> str:
125
+ if not owner:
126
+ return "Target Repository: (Select Owner First)"
127
+
128
+ clean_name = model_name.strip() if model_name else "..."
129
+ return f"Target Repository: **`{owner}/{clean_name}`**"
130
 
131
  @staticmethod
132
  def update_hub_interactive(engine: Optional[FunctionGemmaEngine], username: Optional[str] = None):
 
134
  has_model_tuned = engine is not None and getattr(engine, 'has_model_tuned', False)
135
 
136
  return (
137
+ gr.update(interactive=is_logged_in), # Model Name Input
138
+ gr.update(interactive=is_logged_in), # Owner Dropdown
139
+ gr.update(interactive=is_logged_in and has_model_tuned), # Push Button
140
+ gr.update(interactive=has_model_tuned) # Zip Button
141
  )
142
 
143
  # --- View / Layout Layer ---
 
225
 
226
  with gr.Column():
227
  gr.Markdown("#### Option B: Save to Hugging Face Hub")
228
+ gr.Markdown("Publish your fine-tuned model to your personal Hugging Face account or an Organization.")
229
+
230
+ with gr.Row():
231
+ org_dropdown = gr.Dropdown(
232
+ label="Owner", choices=[], interactive=False, scale=1
233
+ )
234
+ model_name_input = gr.Textbox(
235
+ label="Model Name", value="functiongemma-270m-it-tuning-lab", placeholder="e.g., functiongemma-tuned", interactive=False, scale=2
236
+ )
237
+
238
  push_to_hub_btn = gr.Button("Save to Hugging Face Hub", variant="secondary", interactive=False)
239
  repo_id_preview = gr.Markdown("Target Repository: (Waiting for input...)")
240
  upload_status = gr.Markdown("")
241
 
242
  return {
243
  "zip_controls": [zip_btn, download_file],
244
+ "hub_controls": [org_dropdown, model_name_input, push_to_hub_btn, repo_id_preview, upload_status]
245
  }
246
 
247
  # --- Main Build Function ---
 
259
  export_ui = _render_export_tab(engine_state, username_state)
260
 
261
  # Helpers for UI State
 
 
262
  run_btn, stop_btn, reload_btn, eval_btn = train_ui["buttons"]
263
  action_buttons = [reload_btn, run_btn, eval_btn]
264
 
265
+ # Hub Controls
266
+ org_dropdown = export_ui["hub_controls"][0]
267
+ model_name_input = export_ui["hub_controls"][1]
268
+ push_btn = export_ui["hub_controls"][2]
269
  zip_btn = export_ui["zip_controls"][0]
270
 
271
+ # The list of Hub-related inputs that need updating
272
+ hub_inputs = [model_name_input, org_dropdown, push_btn, zip_btn]
273
+
274
  def lock_ui():
275
  """Locks all buttons (including Zip/Push) during processing"""
276
  return [gr.update(interactive=False) for _ in action_buttons] + \
277
+ [gr.update(interactive=False) for _ in hub_inputs]
278
 
279
  def unlock_ui():
280
  """Unlocks general action buttons only. Zip/Push are handled by update_hub_interactive"""
 
283
  # --- Event Wiring ---
284
 
285
  # 1. Initialization
286
+ # Note: init_session now returns updates for both model_name_input and org_dropdown
287
+ demo.load(lock_ui, outputs=action_buttons + hub_inputs).then(
288
  fn=UIController.init_session,
289
+ inputs=None, # Gradio automatically injects profile and token
290
  outputs=[
291
  engine_state,
292
  data_ui["tools_editor"],
293
  train_ui["model_input"],
294
  train_ui["outputs"][0], # log output
295
+ model_name_input, # Text update
296
+ org_dropdown, # Dropdown update (choices+val)
297
  push_btn,
298
+ zip_btn,
299
  username_state
300
  ]
301
  ).then(
302
  fn=UIController.update_repo_preview,
303
+ inputs=[org_dropdown, model_name_input],
304
+ outputs=[export_ui["hub_controls"][3]]
305
  ).then(unlock_ui, outputs=action_buttons)
306
 
307
  # 2. Data Tab
 
325
  gr.update(visible=False),
326
  gr.update(interactive=False), # Lock Reload
327
  gr.update(interactive=False), # Lock Eval
328
+ [gr.update(interactive=False) for _ in hub_inputs], # Lock Hub
329
  gr.update(visible=True) # Show Stop
330
  ),
331
+ outputs=[run_btn, reload_btn, eval_btn, *hub_inputs, stop_btn]
332
  )
333
  train_run_event = train_run_event.then(
334
  fn=UIController.run_training,
 
346
  # Final check determines if Zip/Push should unlock
347
  fn=UIController.update_hub_interactive,
348
  inputs=[engine_state, username_state],
349
+ outputs=hub_inputs
350
  )
351
 
352
  # 3b. Evaluation
 
354
  fn=lambda: (
355
  gr.update(interactive=False), # Lock Run
356
  gr.update(interactive=False), # Lock Reload
357
+ gr.update(visible=False), # Hide self
358
  gr.update(visible=True) # Show Stop
359
  ),
360
  outputs=[run_btn, reload_btn, eval_btn, stop_btn]
 
381
  queue=False
382
  )
383
 
384
+ reload_btn.click(lock_ui, outputs=action_buttons + hub_inputs).then(
385
  fn=UIController.handle_reset,
386
  inputs=[engine_state, train_ui["model_input"]],
387
  outputs=[train_ui["outputs"][0]]
388
  ).then(unlock_ui, outputs=action_buttons).then(
389
  fn=UIController.update_hub_interactive,
390
  inputs=[engine_state, username_state],
391
+ outputs=hub_inputs
392
  )
393
 
394
  # 4. Export Tab
395
+ zip_btn.click(lock_ui, outputs=action_buttons + hub_inputs).then(
396
  fn=UIController.zip_model,
397
  inputs=[engine_state],
398
  outputs=[export_ui["zip_controls"][1]]
399
  ).then(unlock_ui, outputs=action_buttons).then(
400
  fn=UIController.update_hub_interactive,
401
  inputs=[engine_state, username_state],
402
+ outputs=hub_inputs
403
  )
404
 
405
+ # Update preview when dropdown or text changes
406
+ org_dropdown.change(
407
+ fn=UIController.update_repo_preview,
408
+ inputs=[org_dropdown, model_name_input],
409
+ outputs=[export_ui["hub_controls"][3]]
410
+ )
411
+ model_name_input.change(
412
  fn=UIController.update_repo_preview,
413
+ inputs=[org_dropdown, model_name_input],
414
+ outputs=[export_ui["hub_controls"][3]]
415
  )
416
 
417
+ push_btn.click(lock_ui, outputs=action_buttons + hub_inputs).then(
418
  fn=UIController.upload_model,
419
+ inputs=[engine_state, org_dropdown, model_name_input], # oauth_token injected
420
+ outputs=[export_ui["hub_controls"][4]]
421
  ).then(unlock_ui, outputs=action_buttons).then(
422
  fn=UIController.update_hub_interactive,
423
  inputs=[engine_state, username_state],
424
+ outputs=hub_inputs
425
  )
426
 
427
  return demo