wuhp commited on
Commit
0596125
·
verified ·
1 Parent(s): dbd6fa0

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +197 -100
app.py CHANGED
@@ -112,7 +112,7 @@ def get_container_logs_action(repo_id, profile, token):
112
  if not (repo_id and profile and token):
113
  return "⚠️ Please log in and create a Space first."
114
  # Add a short delay before fetching run logs, build might just finish
115
- time.sleep(5)
116
  return _fetch_space_logs_level(repo_id, "run", token.token)
117
 
118
 
@@ -159,18 +159,32 @@ STATE_CHECKING_LOGS_BUILD = "checking_logs_build"
159
  STATE_CHECKING_LOGS_RUN = "checking_logs_run"
160
  STATE_DEBUGGING_CODE = "debugging_code"
161
  STATE_UPLOADING_FIXED_APP_PY = "uploading_fixed_app_py"
162
- STATE_COMPLETE = "complete"
163
 
164
  MAX_DEBUG_ATTEMPTS = 3
165
 
166
- def update_chat(history, bot_message):
167
- """Helper to add a bot message and yield state."""
168
- # Ensure last user message is in history
169
- if history and history[-1][1] is None:
170
- history[-1][1] = "" # Add empty bot response to last user message if none exists
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
171
 
172
- history.append([None, bot_message])
173
- return history
174
 
175
  def ai_workflow_chat(
176
  message: str,
@@ -188,7 +202,7 @@ def ai_workflow_chat(
188
  debug_attempts_state: int,
189
  app_description_state: str | None, # Persist initial request
190
  repo_name_state: str | None, # Persist chosen name
191
- generated_code_state: str | None, # Persist generated code between steps
192
  ) -> tuple[
193
  list[list[str | None]], # history
194
  str | None, # repo_id
@@ -207,15 +221,16 @@ def ai_workflow_chat(
207
  attempts = debug_attempts_state
208
  app_desc = app_description_state
209
  repo_name = repo_name_state
210
- generated_code = generated_code_state
 
211
 
212
  updated_preview = preview_html
213
  updated_build = build_logs
214
  updated_run = container_logs
215
 
216
- # Add user message to history for context
 
217
  history.append([message, None])
218
- # Yield immediately to show user message
219
  yield history, repo_id, state, updated_preview, updated_run, updated_build, attempts, app_desc, repo_name, generated_code
220
 
221
 
@@ -242,43 +257,50 @@ def ai_workflow_chat(
242
  history = update_chat(history, "Workflow reset.")
243
  # Reset all state variables
244
  yield history, None, STATE_IDLE, "<p>No Space created yet.</p>", "", "", 0, None, None, None
245
- return # End workflow
246
 
247
  elif generate_match:
248
  new_repo_name = generate_match.group(1)
249
  new_app_desc = message # Store the full request
250
  history = update_chat(history, f"Acknowledged: '{message}'. Starting workflow to create Space `{hf_profile.username}/{new_repo_name}`.")
251
  # Transition to creating space state, passing name and description
252
- yield history, repo_id, STATE_CREATING_SPACE, updated_preview, updated_run, updated_build, attempts, new_app_desc, new_repo_name, generated_code
 
 
 
 
 
253
 
254
  elif create_match:
255
  new_repo_name = create_match.group(1)
256
  history = update_chat(history, f"Acknowledged: '{message}'. Starting workflow to create Space `{hf_profile.username}/{new_repo_name}`.")
257
  # Transition to creating space state, just passing the name (desc will be default)
258
- yield history, repo_id, STATE_CREATING_SPACE, updated_preview, updated_run, updated_build, attempts, app_desc, new_repo_name, generated_code # Use existing app_desc or None
 
 
 
 
259
 
260
  elif "create" in message.lower() and not repo_id: # Generic create trigger
261
  history = update_chat(history, "Okay, what should the Space be called? (e.g., `my-awesome-app`)")
262
  # Transition to awaiting name state
263
- yield history, repo_id, STATE_AWAITING_REPO_NAME, updated_preview, updated_run, updated_build, attempts, app_desc, repo_name, generated_code # Stay in this state
 
 
264
 
265
  else:
266
  # Handle other chat messages if needed, or just respond unknown
267
  history = update_chat(history, "Command not recognized. Try 'generate me a gradio app called myapp', or 'reset'.")
268
- yield history, repo_id, state, updated_preview, updated_run, updated_build, attempts, app_desc, repo_name, generated_code
269
  # Stay in IDLE state
 
 
270
 
271
- elif state == STATE_AWAITING_REPO_NAME:
272
- new_repo_name = message.strip()
273
- if not new_repo_name or re.search(r'\s', new_repo_name): # Basic validation for repo name
274
- history = update_chat(history, "Invalid name. Please provide a single word/slug for the Space name.")
275
- yield history, repo_id, state, updated_preview, updated_run, updated_build, attempts, app_desc, repo_name, generated_code # Stay in this state
276
- else:
277
- history = update_chat(history, f"Using Space name `{new_repo_name}`. Creating Space `{hf_profile.username}/{new_repo_name}`...")
278
- # Transition to creating space state, pass the received name
279
- yield history, repo_id, STATE_CREATING_SPACE, updated_preview, updated_run, updated_build, attempts, app_desc, new_repo_name, generated_code
280
 
281
- elif state == STATE_CREATING_SPACE:
 
 
 
 
282
  # This state is triggered when we *already have* the repo_name in state
283
  if not repo_name: # Safety check
284
  history = update_chat(history, "Internal error: Repo name missing for creation. Resetting.")
@@ -288,15 +310,19 @@ def ai_workflow_chat(
288
  try:
289
  new_repo_id, iframe_html = create_space_action(repo_name, space_sdk, hf_profile, hf_token)
290
  updated_preview = iframe_html
291
- history = update_chat(history, f"✅ Space `{new_repo_id}` created.")
292
- # Transition to generating code state, update repo_id and preview
293
- yield history, new_repo_id, STATE_GENERATING_CODE, updated_preview, updated_run, updated_build, attempts, app_desc, repo_name, generated_code
 
 
 
294
 
295
  except Exception as e:
296
- history = update_chat(history, f"❌ Error creating space: {e}")
297
  # Reset state on failure
298
  yield history, None, STATE_IDLE, "<p>Error creating space.</p>", "", "", 0, None, None, None
299
 
 
300
  elif state == STATE_GENERATING_CODE:
301
  # Use the stored app description or a default
302
  prompt_desc = app_desc if app_desc else 'a Gradio image-blur test app with upload and slider controls'
@@ -308,7 +334,7 @@ Return **only** the python code block for app.py. Do not include any extra text,
308
  """
309
  try:
310
  history = update_chat(history, "🧠 Generating `app.py` code with Gemini...")
311
- yield history, repo_id, state, updated_preview, updated_run, updated_build, attempts, app_desc, repo_name, generated_code # Update UI to show "Generating..."
312
 
313
  code = call_gemini(prompt, gemini_api_key, gemini_model)
314
  # Clean markdown and whitespace
@@ -323,13 +349,17 @@ Return **only** the python code block for app.py. Do not include any extra text,
323
 
324
  history = update_chat(history, "✅ `app.py` code generated. Click 'Send' to upload.")
325
  # Transition to uploading state, store the generated code
326
- yield history, repo_id, STATE_UPLOADING_APP_PY, updated_preview, updated_run, updated_build, attempts, app_desc, repo_name, code # Pass code in state
 
 
 
327
 
328
  except Exception as e:
329
  history = update_chat(history, f"❌ Error generating code: {e}. Click 'reset'.")
330
  # Reset state on failure
331
  yield history, None, STATE_IDLE, updated_preview, updated_run, updated_build, 0, None, None, None
332
 
 
333
  elif state == STATE_UPLOADING_APP_PY:
334
  # Use the generated_code stored in state
335
  if not generated_code:
@@ -338,18 +368,22 @@ Return **only** the python code block for app.py. Do not include any extra text,
338
  return
339
 
340
  history = update_chat(history, "☁️ Uploading `app.py`...")
341
- yield history, repo_id, state, updated_preview, updated_run, updated_build, attempts, app_desc, repo_name, None # Yield to show message, clear generated_code state
342
 
343
  try:
344
  upload_file_to_space_action(io.StringIO(generated_code), "app.py", repo_id, hf_profile, hf_token)
345
  history = update_chat(history, "✅ Uploaded `app.py`. Click 'Send' to generate requirements.")
346
- # Transition to generating requirements
347
- yield history, repo_id, STATE_GENERATING_REQUIREMENTS, updated_preview, updated_run, updated_build, attempts, app_desc, repo_name, None # Keep generated_code as None
 
 
 
348
 
349
  except Exception as e:
350
  history = update_chat(history, f"❌ Error uploading app.py: {e}. Click 'reset'.")
351
  yield history, None, STATE_IDLE, updated_preview, updated_run, updated_build, 0, None, None, None
352
 
 
353
  elif state == STATE_GENERATING_REQUIREMENTS:
354
  history = update_chat(history, "📄 Generating `requirements.txt`...")
355
  yield history, repo_id, state, updated_preview, updated_run, updated_build, attempts, app_desc, repo_name, generated_code # Yield to show message
@@ -357,21 +391,28 @@ Return **only** the python code block for app.py. Do not include any extra text,
357
  # Simple heuristic for requirements based on SDK and common needs
358
  reqs_list = ["gradio"] if space_sdk == "gradio" else ["streamlit"]
359
  # Add common deps if likely used (could parse code, but simpler heuristic for demo)
360
- if "google.generativeai" in (generated_code or "") or gemini_api_key: # Check if Gemini was used for code or if key is set
361
  reqs_list.append("google-generativeai")
362
- if "requests" in (generated_code or ""):
363
  reqs_list.append("requests")
364
- reqs_list.append("huggingface_hub") # Needed for log fetching etc if done inside the space itself (though not in this current app's space code)
 
 
 
 
365
 
366
  reqs_content = "\n".join(reqs_list) + "\n"
367
 
368
  history = update_chat(history, "✅ `requirements.txt` generated. Click 'Send' to upload.")
369
  # Transition to uploading requirements, store content temporarily
370
- yield history, repo_id, STATE_UPLOADING_REQUIREMENTS, updated_preview, updated_run, updated_build, attempts, app_desc, repo_name, reqs_content # Pass content in state (abusing generated_code slot)
 
 
 
371
 
372
 
373
  elif state == STATE_UPLOADING_REQUIREMENTS:
374
- # Use content stored in state (abusing generated_code slot)
375
  reqs_content_to_upload = generated_code # Get content from state
376
  if not reqs_content_to_upload:
377
  history = update_chat(history, "Internal error: No requirements content to upload. Resetting.")
@@ -379,13 +420,17 @@ Return **only** the python code block for app.py. Do not include any extra text,
379
  return
380
 
381
  history = update_chat(history, "☁️ Uploading `requirements.txt`...")
382
- yield history, repo_id, state, updated_preview, updated_run, updated_build, attempts, app_desc, repo_name, None # Yield, clear temp state
383
 
384
  try:
385
  upload_file_to_space_action(io.StringIO(reqs_content_to_upload), "requirements.txt", repo_id, hf_profile, hf_token)
386
  history = update_chat(history, "✅ Uploaded `requirements.txt`. Click 'Send' to generate README.")
387
- # Transition to generating README
388
- yield history, repo_id, STATE_GENERATING_README, updated_preview, updated_run, updated_build, attempts, app_desc, repo_name, None
 
 
 
 
389
 
390
  except Exception as e:
391
  history = update_chat(history, f"❌ Error uploading requirements.txt: {e}. Click 'reset'.")
@@ -397,18 +442,23 @@ Return **only** the python code block for app.py. Do not include any extra text,
397
 
398
  # Generate a simple README based on app_desc or repo_name
399
  readme_title = repo_name if repo_name else "My Awesome Space"
400
- readme_description = app_desc if app_desc else "This Hugging Face Space hosts an AI-generated application."
 
401
 
402
  readme_content = f"# {readme_title}\n\n{readme_description}\n\n" \
403
- "This Space was automatically generated by an AI workflow.\n"
 
404
 
405
  history = update_chat(history, "✅ `README.md` generated. Click 'Send' to upload.")
406
  # Transition to uploading README, store content temporarily
407
- yield history, repo_id, STATE_UPLOADING_README, updated_preview, updated_run, updated_build, attempts, app_desc, repo_name, readme_content # Pass content in state
 
 
 
408
 
409
 
410
  elif state == STATE_UPLOADING_README:
411
- # Use content stored in state (abusing generated_code slot)
412
  readme_content_to_upload = generated_code # Get content from state
413
  if not readme_content_to_upload:
414
  history = update_chat(history, "Internal error: No README content to upload. Resetting.")
@@ -416,13 +466,17 @@ Return **only** the python code block for app.py. Do not include any extra text,
416
  return
417
 
418
  history = update_chat(history, "☁️ Uploading `README.md`...")
419
- yield history, repo_id, state, updated_preview, updated_run, updated_build, attempts, app_desc, repo_name, None # Yield, clear temp state
420
 
421
  try:
422
  upload_file_to_space_action(io.StringIO(readme_content_to_upload), "README.md", repo_id, hf_profile, hf_token)
423
- history = update_chat(history, "✅ Uploaded `README.md`. Files uploaded. Space is now building. Click 'Send' to check build logs.")
424
- # Transition to checking build logs
425
- yield history, repo_id, STATE_CHECKING_LOGS_BUILD, updated_preview, updated_run, updated_build, attempts, app_desc, repo_name, None
 
 
 
 
426
 
427
  except Exception as e:
428
  history = update_chat(history, f"❌ Error uploading README.md: {e}. Click 'reset'.")
@@ -440,13 +494,20 @@ Return **only** the python code block for app.py. Do not include any extra text,
440
  # Simple check: if build logs contain "Error" or "Exception", might indicate build issue.
441
  # More robust would involve checking build status via API, but logs are simpler for demo.
442
  # Assuming successful build leads to container logs check.
 
443
  if "Error" in updated_build or "Exception" in updated_build:
444
- history = update_chat(history, "⚠️ Build logs may contain errors. Please inspect. Click 'Send' to check container logs (app might still start).")
445
- yield history, repo_id, STATE_CHECKING_LOGS_RUN, updated_preview, updated_run, updated_build, attempts, app_desc, repo_name, generated_code # Transition to run logs check
 
 
 
446
 
447
  else:
448
  history = update_chat(history, "✅ Build logs fetched. Click 'Send' to check container logs.")
449
- yield history, repo_id, STATE_CHECKING_LOGS_RUN, updated_preview, updated_run, updated_build, attempts, app_desc, repo_name, generated_code # Transition to run logs check
 
 
 
450
 
451
 
452
  elif state == STATE_CHECKING_LOGS_RUN:
@@ -461,17 +522,24 @@ Return **only** the python code block for app.py. Do not include any extra text,
461
  attempts += 1
462
  history = update_chat(history, f"❌ Errors detected in container logs. Attempting debug fix #{attempts}/{MAX_DEBUG_ATTEMPTS}. Click 'Send' to proceed.")
463
  # Transition to debugging state, increment attempts
464
- yield history, repo_id, STATE_DEBUGGING_CODE, updated_preview, updated_run, updated_build, attempts, app_desc, repo_name, generated_code # Keep other states same
 
 
465
 
466
  elif ("Error" in updated_run or "Exception" in updated_run) and attempts >= MAX_DEBUG_ATTEMPTS:
467
  history = update_chat(history, f"❌ Errors detected in container logs. Max debug attempts ({MAX_DEBUG_ATTEMPTS}) reached. Please inspect logs manually or click 'reset'.")
468
- # Transition to complete/idle after failed attempts
469
- yield history, repo_id, STATE_COMPLETE, updated_preview, updated_run, updated_build, attempts, app_desc, repo_name, generated_code
 
 
470
 
471
  else:
472
  history = update_chat(history, "✅ App appears to be running successfully! Check the iframe above. Click 'reset' to start a new project.")
473
- # Transition to complete/idle on success
474
- yield history, repo_id, STATE_COMPLETE, updated_preview, updated_run, updated_build, attempts, app_desc, repo_name, generated_code
 
 
 
475
 
476
  elif state == STATE_DEBUGGING_CODE:
477
  history = update_chat(history, f"🧠 Calling Gemini to generate fix based on logs...")
@@ -501,7 +569,10 @@ Return **only** the python code block for app.py. Do not include any extra text,
501
 
502
  history = update_chat(history, "✅ Fix code generated. Click 'Send' to upload.")
503
  # Transition to uploading fixed code, pass the fixed code
504
- yield history, repo_id, STATE_UPLOADING_FIXED_APP_PY, updated_preview, updated_run, updated_build, attempts, app_desc, repo_name, fix_code # Pass fix_code in state
 
 
 
505
 
506
 
507
  except Exception as e:
@@ -510,7 +581,7 @@ Return **only** the python code block for app.py. Do not include any extra text,
510
  yield history, None, STATE_IDLE, updated_preview, updated_run, updated_build, 0, None, None, None
511
 
512
  elif state == STATE_UPLOADING_FIXED_APP_PY:
513
- # Use the fixed code stored in state (abusing generated_code slot)
514
  fixed_code_to_upload = generated_code # Get code from state
515
  if not fixed_code_to_upload:
516
  history = update_chat(history, "Internal error: No fixed code available to upload. Resetting.")
@@ -518,13 +589,17 @@ Return **only** the python code block for app.py. Do not include any extra text,
518
  return
519
 
520
  history = update_chat(history, "☁️ Uploading fixed `app.py`...")
521
- yield history, repo_id, state, updated_preview, updated_run, updated_build, attempts, app_desc, repo_name, None # Yield, clear temp state
522
 
523
  try:
524
  upload_file_to_space_action(io.StringIO(fixed_code_to_upload), "app.py", repo_id, hf_profile, hf_token)
525
  history = update_chat(history, "✅ Fixed `app.py` uploaded. Space will rebuild. Click 'Send' to check logs again.")
526
- # Transition back to checking run logs (after rebuild)
527
- yield history, repo_id, STATE_CHECKING_LOGS_RUN, updated_preview, updated_run, updated_build, attempts, app_desc, repo_name, None # Keep generated_code as None
 
 
 
 
528
 
529
  except Exception as e:
530
  history = update_chat(history, f"❌ Error uploading fixed app.py: {e}. Click 'reset'.")
@@ -533,22 +608,33 @@ Return **only** the python code block for app.py. Do not include any extra text,
533
  elif state == STATE_COMPLETE:
534
  # App is successfully deployed or failed after attempts.
535
  # User should click reset or start a new command.
536
- # Just yield the current state.
537
- yield history, repo_id, state, updated_preview, updated_run, updated_build, attempts, app_desc, repo_name, generated_code
 
 
538
 
539
- else: # Should not happen
540
- history = update_chat(history, f"Internal error: Unknown state '{state}'. Resetting.")
541
- yield history, None, STATE_IDLE, "<p>Unknown state error.</p>", "", "", 0, None, None, None
542
 
543
 
544
  except Exception as e:
545
  # Catch-all for unexpected exceptions in any state
546
- history = update_chat(history, f"Workflow step failed unexpectedly: {e}. Click 'Send' to re-attempt this step or 'reset'.")
547
- # Stay in the current state to allow re-attempt, or maybe go to idle?
548
- # Let's go to idle on unexpected errors to prevent getting stuck.
 
 
549
  yield history, None, STATE_IDLE, updated_preview, updated_run, updated_build, 0, None, None, None
550
 
551
 
 
 
 
 
 
 
 
552
  # --- Build the Gradio UI ---
553
 
554
  with gr.Blocks(title="AI-Powered HF Space App Builder") as ai_builder_tab:
@@ -563,7 +649,7 @@ with gr.Blocks(title="AI-Powered HF Space App Builder") as ai_builder_tab:
563
  debug_attempts = gr.State(0) # Counter for debug attempts
564
  app_description = gr.State(None) # Stores the user's original request string
565
  repo_name_state = gr.State(None) # Stores the parsed repo name
566
- generated_code_state = gr.State(None) # Temporarily stores generated code or file content
567
 
568
  with gr.Row():
569
  # Sidebar
@@ -574,10 +660,9 @@ with gr.Blocks(title="AI-Powered HF Space App Builder") as ai_builder_tab:
574
 
575
  # Initial load to check login status
576
  ai_builder_tab.load(show_profile, outputs=login_status)
577
- # Update status on login click
578
  login_btn.click(show_profile, outputs=login_status)
579
- # Store profile and token in state on login click
580
- login_btn.click(lambda p, t: (p, t), outputs=[hf_profile, hf_token])
581
 
582
  gr.Markdown("## Google AI Studio API Key")
583
  gemini_input = gr.Textbox(label="API Key", type="password", interactive=True) # Ensure interactive
@@ -591,9 +676,9 @@ with gr.Blocks(title="AI-Powered HF Space App Builder") as ai_builder_tab:
591
  choices=[
592
  ("Gemini 2.5 Flash Preview 04-17", "gemini-2.5-flash-preview-04-17"),
593
  ("Gemini 2.5 Pro Preview 03-25", "gemini-2.5-pro-preview-03-25"),
594
- ("Gemini 2.0 Flash", "gemini-2.0-flash"), # Add more recent models if available
595
- ("Gemini 2.0 Flash‑Lite", "gemini-2.0-flash-lite"),
596
- ("Gemini 1.5 Flash", "gemini-1.5-flash"),
597
  ],
598
  value="gemini-2.5-flash-preview-04-17",
599
  label="Select model",
@@ -603,6 +688,7 @@ with gr.Blocks(title="AI-Powered HF Space App Builder") as ai_builder_tab:
603
  model_selector.change(lambda m: m, inputs=model_selector, outputs=gemini_model)
604
 
605
  # Configure Gemini status on load and when key/model changes
 
606
  ai_builder_tab.load(
607
  configure_gemini,
608
  inputs=[gemini_key, gemini_model],
@@ -630,19 +716,20 @@ with gr.Blocks(title="AI-Powered HF Space App Builder") as ai_builder_tab:
630
 
631
  # Main content
632
  with gr.Column(scale=3):
633
- chatbot = gr.Chatbot()
 
634
  user_input = gr.Textbox(placeholder="Type your message…", interactive=True) # Ensure interactive
635
  send_btn = gr.Button("Send", interactive=False)
636
 
637
  # Logic to enable send button only when logged in and API key is set
638
- # This logic is handled reactively by the load and change events below
639
- def update_send_button_state(profile, token, key, model):
640
  is_logged_in = profile is not None and token is not None
641
- is_gemini_ready = key is not None and model is not None
642
- # Simple check - a more robust check would involve calling configure_gemini
643
- # but for UI responsiveness, this is okay.
644
  return gr.update(interactive=is_logged_in and is_gemini_ready)
645
 
 
646
  ai_builder_tab.load(
647
  update_send_button_state,
648
  inputs=[hf_profile, hf_token, gemini_key, gemini_model],
@@ -664,12 +751,12 @@ with gr.Blocks(title="AI-Powered HF Space App Builder") as ai_builder_tab:
664
  outputs=[send_btn]
665
  )
666
 
667
-
668
  iframe = gr.HTML("<p>No Space created yet.</p>")
669
  build_txt = gr.Textbox(label="Build Logs", lines=10, interactive=False, value="") # Initialize empty
670
  run_txt = gr.Textbox(label="Container Logs", lines=10, interactive=False, value="") # Initialize empty
671
 
672
  # The main event handler for the Send button
 
673
  send_btn.click(
674
  ai_workflow_chat,
675
  inputs=[
@@ -692,20 +779,30 @@ with gr.Blocks(title="AI-Powered HF Space App Builder") as ai_builder_tab:
692
  outputs=user_input
693
  )
694
 
695
- # Link state variables to UI status displays
 
696
  workflow.change(lambda s: s, inputs=workflow, outputs=status_text)
697
  repo_id.change(lambda r: r if r else "None", inputs=repo_id, outputs=repo_id_text)
698
- # Update logs and iframe when their state variables change (e.g., after fetch/create)
699
- iframe.change(lambda h: h, inputs=iframe, outputs=iframe) # Not strictly needed, already linked via click outputs
700
- build_txt.change(lambda t: t, inputs=build_txt, outputs=build_txt) # Not strictly needed
701
- run_txt.change(lambda t: t, inputs=run_txt, outputs=run_txt) # Not strictly needed
702
-
703
-
704
- # Add an initial message to the chatbot on load
705
- def greet():
706
- return [[None, "Welcome! Please log in to Hugging Face and provide your Google AI Studio API key to start building Spaces. Once ready, type 'generate me a gradio app called myapp' or 'create' to begin."]]
707
-
708
- ai_builder_tab.load(greet, outputs=chatbot)
 
 
 
 
 
 
 
 
 
709
 
710
 
711
  if __name__ == "__main__":
 
112
  if not (repo_id and profile and token):
113
  return "⚠️ Please log in and create a Space first."
114
  # Add a short delay before fetching run logs, build might just finish
115
+ time.sleep(5) # Added delay for robustness
116
  return _fetch_space_logs_level(repo_id, "run", token.token)
117
 
118
 
 
159
  STATE_CHECKING_LOGS_RUN = "checking_logs_run"
160
  STATE_DEBUGGING_CODE = "debugging_code"
161
  STATE_UPLOADING_FIXED_APP_PY = "uploading_fixed_app_py"
162
+ STATE_COMPLETE = "complete" # Added a final state
163
 
164
  MAX_DEBUG_ATTEMPTS = 3
165
 
166
+ def update_chat(history: list[list[str | None]], bot_message: str) -> list[list[str | None]]:
167
+ """Helper to set the bot's response for the last user message."""
168
+ # Assume the last entry was just added with history.append([message, None])
169
+ # If history is empty, this is an error in logic flow, but add safety.
170
+ if history:
171
+ # Ensure the last message is indeed a user message awaiting response
172
+ if history[-1][1] is None:
173
+ history[-1][1] = bot_message
174
+ else:
175
+ # This case means we're trying to add a bot response but the last message
176
+ # already has one. This might happen if a step is re-triggered.
177
+ # Append as a new bot-only message as a fallback.
178
+ # In a strict state machine, this might indicate a flow error.
179
+ # But for robustness, let's add it.
180
+ history.append([None, bot_message])
181
+ else:
182
+ # This shouldn't happen - update_chat should always be called after a user message is added.
183
+ print("Warning: update_chat called with empty history.")
184
+ history.append([None, bot_message]) # As a fallback, add a bot-only message
185
+
186
+ return history # Return the modified history list
187
 
 
 
188
 
189
  def ai_workflow_chat(
190
  message: str,
 
202
  debug_attempts_state: int,
203
  app_description_state: str | None, # Persist initial request
204
  repo_name_state: str | None, # Persist chosen name
205
+ generated_code_state: str | None, # Temporarily stores generated code or file content
206
  ) -> tuple[
207
  list[list[str | None]], # history
208
  str | None, # repo_id
 
221
  attempts = debug_attempts_state
222
  app_desc = app_description_state
223
  repo_name = repo_name_state
224
+ generated_code = generated_code_state # This slot is reused for different file contents
225
+
226
 
227
  updated_preview = preview_html
228
  updated_build = build_logs
229
  updated_run = container_logs
230
 
231
+ # Add user message to history with a placeholder for the bot's response
232
+ # We yield immediately after this to show the user's message
233
  history.append([message, None])
 
234
  yield history, repo_id, state, updated_preview, updated_run, updated_build, attempts, app_desc, repo_name, generated_code
235
 
236
 
 
257
  history = update_chat(history, "Workflow reset.")
258
  # Reset all state variables
259
  yield history, None, STATE_IDLE, "<p>No Space created yet.</p>", "", "", 0, None, None, None
260
+ return # End workflow for this trigger
261
 
262
  elif generate_match:
263
  new_repo_name = generate_match.group(1)
264
  new_app_desc = message # Store the full request
265
  history = update_chat(history, f"Acknowledged: '{message}'. Starting workflow to create Space `{hf_profile.username}/{new_repo_name}`.")
266
  # Transition to creating space state, passing name and description
267
+ state = STATE_CREATING_SPACE
268
+ repo_name = new_repo_name
269
+ app_desc = new_app_desc
270
+ # Yield state change and bot message
271
+ yield history, repo_id, state, updated_preview, updated_run, updated_build, attempts, app_desc, repo_name, generated_code
272
+
273
 
274
  elif create_match:
275
  new_repo_name = create_match.group(1)
276
  history = update_chat(history, f"Acknowledged: '{message}'. Starting workflow to create Space `{hf_profile.username}/{new_repo_name}`.")
277
  # Transition to creating space state, just passing the name (desc will be default)
278
+ state = STATE_CREATING_SPACE
279
+ repo_name = new_repo_name
280
+ # app_desc remains None or existing
281
+ # Yield state change and bot message
282
+ yield history, repo_id, state, updated_preview, updated_run, updated_build, attempts, app_desc, repo_name, generated_code
283
 
284
  elif "create" in message.lower() and not repo_id: # Generic create trigger
285
  history = update_chat(history, "Okay, what should the Space be called? (e.g., `my-awesome-app`)")
286
  # Transition to awaiting name state
287
+ state = STATE_AWAITING_REPO_NAME
288
+ # Yield state change and bot message
289
+ yield history, repo_id, state, updated_preview, updated_run, updated_build, attempts, app_desc, repo_name, generated_code
290
 
291
  else:
292
  # Handle other chat messages if needed, or just respond unknown
293
  history = update_chat(history, "Command not recognized. Try 'generate me a gradio app called myapp', or 'reset'.")
 
294
  # Stay in IDLE state
295
+ yield history, repo_id, state, updated_preview, updated_run, updated_build, attempts, app_desc, repo_name, generated_code
296
+ return # End workflow for this trigger
297
 
 
 
 
 
 
 
 
 
 
298
 
299
+ # --- Workflow steps triggered by state ---
300
+ # Note: These steps assume the state was transitioned into in the previous yield.
301
+ # A new click triggers the function again, and the state machine picks up here.
302
+
303
+ if state == STATE_CREATING_SPACE:
304
  # This state is triggered when we *already have* the repo_name in state
305
  if not repo_name: # Safety check
306
  history = update_chat(history, "Internal error: Repo name missing for creation. Resetting.")
 
310
  try:
311
  new_repo_id, iframe_html = create_space_action(repo_name, space_sdk, hf_profile, hf_token)
312
  updated_preview = iframe_html
313
+ repo_id = new_repo_id # Update repo_id state variable
314
+ history = update_chat(history, f"✅ Space `{repo_id}` created. Click 'Send' to generate and upload code.")
315
+ # Transition to generating code state
316
+ state = STATE_GENERATING_CODE
317
+ # Yield state change and bot message
318
+ yield history, repo_id, state, updated_preview, updated_run, updated_build, attempts, app_desc, repo_name, generated_code
319
 
320
  except Exception as e:
321
+ history = update_chat(history, f"❌ Error creating space: {e}. Click 'reset'.")
322
  # Reset state on failure
323
  yield history, None, STATE_IDLE, "<p>Error creating space.</p>", "", "", 0, None, None, None
324
 
325
+
326
  elif state == STATE_GENERATING_CODE:
327
  # Use the stored app description or a default
328
  prompt_desc = app_desc if app_desc else 'a Gradio image-blur test app with upload and slider controls'
 
334
  """
335
  try:
336
  history = update_chat(history, "🧠 Generating `app.py` code with Gemini...")
337
+ yield history, repo_id, state, updated_preview, updated_run, updated_build, attempts, app_desc, repo_name, generated_code # Yield to show "Generating..." message
338
 
339
  code = call_gemini(prompt, gemini_api_key, gemini_model)
340
  # Clean markdown and whitespace
 
349
 
350
  history = update_chat(history, "✅ `app.py` code generated. Click 'Send' to upload.")
351
  # Transition to uploading state, store the generated code
352
+ state = STATE_UPLOADING_APP_PY
353
+ generated_code = code # Store code in state
354
+ # Yield state change and bot message
355
+ yield history, repo_id, state, updated_preview, updated_run, updated_build, attempts, app_desc, repo_name, generated_code
356
 
357
  except Exception as e:
358
  history = update_chat(history, f"❌ Error generating code: {e}. Click 'reset'.")
359
  # Reset state on failure
360
  yield history, None, STATE_IDLE, updated_preview, updated_run, updated_build, 0, None, None, None
361
 
362
+
363
  elif state == STATE_UPLOADING_APP_PY:
364
  # Use the generated_code stored in state
365
  if not generated_code:
 
368
  return
369
 
370
  history = update_chat(history, "☁️ Uploading `app.py`...")
371
+ yield history, repo_id, state, updated_preview, updated_run, updated_build, attempts, app_desc, repo_name, generated_code # Yield to show message
372
 
373
  try:
374
  upload_file_to_space_action(io.StringIO(generated_code), "app.py", repo_id, hf_profile, hf_token)
375
  history = update_chat(history, "✅ Uploaded `app.py`. Click 'Send' to generate requirements.")
376
+ # Transition to generating requirements, clear the temporary code storage
377
+ state = STATE_GENERATING_REQUIREMENTS
378
+ generated_code = None # Clear temporary storage
379
+ # Yield state change and bot message
380
+ yield history, repo_id, state, updated_preview, updated_run, updated_build, attempts, app_desc, repo_name, generated_code
381
 
382
  except Exception as e:
383
  history = update_chat(history, f"❌ Error uploading app.py: {e}. Click 'reset'.")
384
  yield history, None, STATE_IDLE, updated_preview, updated_run, updated_build, 0, None, None, None
385
 
386
+
387
  elif state == STATE_GENERATING_REQUIREMENTS:
388
  history = update_chat(history, "📄 Generating `requirements.txt`...")
389
  yield history, repo_id, state, updated_preview, updated_run, updated_build, attempts, app_desc, repo_name, generated_code # Yield to show message
 
391
  # Simple heuristic for requirements based on SDK and common needs
392
  reqs_list = ["gradio"] if space_sdk == "gradio" else ["streamlit"]
393
  # Add common deps if likely used (could parse code, but simpler heuristic for demo)
394
+ if "google.generativeai" in str(generated_code_state) or gemini_api_key: # Check if Gemini was used for code generation OR if key is set
395
  reqs_list.append("google-generativeai")
396
+ if "requests" in str(generated_code_state):
397
  reqs_list.append("requests")
398
+ reqs_list.append("huggingface_hub") # Needed for log fetching etc if done inside the space itself (though not currently)
399
+ # Add Pillow for image processing if it's an image app (common requirement)
400
+ if "image" in str(app_desc).lower() or "upload" in str(app_desc).lower():
401
+ reqs_list.append("Pillow")
402
+
403
 
404
  reqs_content = "\n".join(reqs_list) + "\n"
405
 
406
  history = update_chat(history, "✅ `requirements.txt` generated. Click 'Send' to upload.")
407
  # Transition to uploading requirements, store content temporarily
408
+ state = STATE_UPLOADING_REQUIREMENTS
409
+ generated_code = reqs_content # Pass content in state (reusing generated_code slot)
410
+ # Yield state change and bot message
411
+ yield history, repo_id, state, updated_preview, updated_run, updated_build, attempts, app_desc, repo_name, generated_code
412
 
413
 
414
  elif state == STATE_UPLOADING_REQUIREMENTS:
415
+ # Use content stored in state (reusing generated_code slot)
416
  reqs_content_to_upload = generated_code # Get content from state
417
  if not reqs_content_to_upload:
418
  history = update_chat(history, "Internal error: No requirements content to upload. Resetting.")
 
420
  return
421
 
422
  history = update_chat(history, "☁️ Uploading `requirements.txt`...")
423
+ yield history, repo_id, state, updated_preview, updated_run, updated_build, attempts, app_desc, repo_name, generated_code # Yield, keep temp state
424
 
425
  try:
426
  upload_file_to_space_action(io.StringIO(reqs_content_to_upload), "requirements.txt", repo_id, hf_profile, hf_token)
427
  history = update_chat(history, "✅ Uploaded `requirements.txt`. Click 'Send' to generate README.")
428
+ # Transition to generating README, clear the temporary storage
429
+ state = STATE_GENERATING_README
430
+ generated_code = None # Clear temporary storage
431
+ # Yield state change and bot message
432
+ yield history, repo_id, state, updated_preview, updated_run, updated_build, attempts, app_desc, repo_name, generated_code
433
+
434
 
435
  except Exception as e:
436
  history = update_chat(history, f"❌ Error uploading requirements.txt: {e}. Click 'reset'.")
 
442
 
443
  # Generate a simple README based on app_desc or repo_name
444
  readme_title = repo_name if repo_name else "My Awesome Space"
445
+ # Use app_desc if available, otherwise a generic description
446
+ readme_description = app_desc if app_desc else f"This Hugging Face Space hosts an AI-generated {space_sdk} application."
447
 
448
  readme_content = f"# {readme_title}\n\n{readme_description}\n\n" \
449
+ "This Space was automatically generated by an AI workflow.\n\n" \
450
+ f"Built with the {space_sdk} SDK.\n"
451
 
452
  history = update_chat(history, "✅ `README.md` generated. Click 'Send' to upload.")
453
  # Transition to uploading README, store content temporarily
454
+ state = STATE_UPLOADING_README
455
+ generated_code = readme_content # Pass content in state
456
+ # Yield state change and bot message
457
+ yield history, repo_id, state, updated_preview, updated_run, updated_build, attempts, app_desc, repo_name, generated_code
458
 
459
 
460
  elif state == STATE_UPLOADING_README:
461
+ # Use content stored in state (reusing generated_code slot)
462
  readme_content_to_upload = generated_code # Get content from state
463
  if not readme_content_to_upload:
464
  history = update_chat(history, "Internal error: No README content to upload. Resetting.")
 
466
  return
467
 
468
  history = update_chat(history, "☁️ Uploading `README.md`...")
469
+ yield history, repo_id, state, updated_preview, updated_run, updated_build, attempts, app_desc, repo_name, generated_code # Yield, keep temp state
470
 
471
  try:
472
  upload_file_to_space_action(io.StringIO(readme_content_to_upload), "README.md", repo_id, hf_profile, hf_token)
473
+ history = update_chat(history, "✅ Uploaded `README.md`. All files uploaded. Space is now building. Click 'Send' to check build logs.")
474
+ # Transition to checking build logs, clear the temporary storage
475
+ state = STATE_CHECKING_LOGS_BUILD
476
+ generated_code = None # Clear temporary storage
477
+ # Yield state change and bot message
478
+ yield history, repo_id, state, updated_preview, updated_run, updated_build, attempts, app_desc, repo_name, generated_code
479
+
480
 
481
  except Exception as e:
482
  history = update_chat(history, f"❌ Error uploading README.md: {e}. Click 'reset'.")
 
494
  # Simple check: if build logs contain "Error" or "Exception", might indicate build issue.
495
  # More robust would involve checking build status via API, but logs are simpler for demo.
496
  # Assuming successful build leads to container logs check.
497
+ # Check updated_build content for errors
498
  if "Error" in updated_build or "Exception" in updated_build:
499
+ history = update_chat(history, "⚠️ Build logs may contain errors. Please inspect above. Click 'Send' to check container logs (app might still start).")
500
+ # Transition to run logs check
501
+ state = STATE_CHECKING_LOGS_RUN
502
+ # Yield state change and message, include updated build logs
503
+ yield history, repo_id, state, updated_preview, updated_run, updated_build, attempts, app_desc, repo_name, generated_code
504
 
505
  else:
506
  history = update_chat(history, "✅ Build logs fetched. Click 'Send' to check container logs.")
507
+ # Transition to run logs check
508
+ state = STATE_CHECKING_LOGS_RUN
509
+ # Yield state change and message, include updated build logs
510
+ yield history, repo_id, state, updated_preview, updated_run, updated_build, attempts, app_desc, repo_name, generated_code
511
 
512
 
513
  elif state == STATE_CHECKING_LOGS_RUN:
 
522
  attempts += 1
523
  history = update_chat(history, f"❌ Errors detected in container logs. Attempting debug fix #{attempts}/{MAX_DEBUG_ATTEMPTS}. Click 'Send' to proceed.")
524
  # Transition to debugging state, increment attempts
525
+ state = STATE_DEBUGGING_CODE
526
+ # Yield state change and message, include updated run logs
527
+ yield history, repo_id, state, updated_preview, updated_run, updated_build, attempts, app_desc, repo_name, generated_code
528
 
529
  elif ("Error" in updated_run or "Exception" in updated_run) and attempts >= MAX_DEBUG_ATTEMPTS:
530
  history = update_chat(history, f"❌ Errors detected in container logs. Max debug attempts ({MAX_DEBUG_ATTEMPTS}) reached. Please inspect logs manually or click 'reset'.")
531
+ # Transition to complete state after failed attempts
532
+ state = STATE_COMPLETE # Indicate workflow finished with errors
533
+ # Yield state change and message
534
+ yield history, repo_id, state, updated_preview, updated_run, updated_build, attempts, app_desc, repo_name, generated_code
535
 
536
  else:
537
  history = update_chat(history, "✅ App appears to be running successfully! Check the iframe above. Click 'reset' to start a new project.")
538
+ # Transition to complete state on success
539
+ state = STATE_COMPLETE # Indicate workflow finished successfully
540
+ # Yield state change and message
541
+ yield history, repo_id, state, updated_preview, updated_run, updated_build, attempts, app_desc, repo_name, generated_code
542
+
543
 
544
  elif state == STATE_DEBUGGING_CODE:
545
  history = update_chat(history, f"🧠 Calling Gemini to generate fix based on logs...")
 
569
 
570
  history = update_chat(history, "✅ Fix code generated. Click 'Send' to upload.")
571
  # Transition to uploading fixed code, pass the fixed code
572
+ state = STATE_UPLOADING_FIXED_APP_PY
573
+ generated_code = fix_code # Store fix_code in state
574
+ # Yield state change and message
575
+ yield history, repo_id, state, updated_preview, updated_run, updated_build, attempts, app_desc, repo_name, generated_code
576
 
577
 
578
  except Exception as e:
 
581
  yield history, None, STATE_IDLE, updated_preview, updated_run, updated_build, 0, None, None, None
582
 
583
  elif state == STATE_UPLOADING_FIXED_APP_PY:
584
+ # Use the fixed code stored in state (reusing generated_code slot)
585
  fixed_code_to_upload = generated_code # Get code from state
586
  if not fixed_code_to_upload:
587
  history = update_chat(history, "Internal error: No fixed code available to upload. Resetting.")
 
589
  return
590
 
591
  history = update_chat(history, "☁️ Uploading fixed `app.py`...")
592
+ yield history, repo_id, state, updated_preview, updated_run, updated_build, attempts, app_desc, repo_name, generated_code # Yield, keep temp state
593
 
594
  try:
595
  upload_file_to_space_action(io.StringIO(fixed_code_to_upload), "app.py", repo_id, hf_profile, hf_token)
596
  history = update_chat(history, "✅ Fixed `app.py` uploaded. Space will rebuild. Click 'Send' to check logs again.")
597
+ # Transition back to checking run logs (after rebuild), clear temporary storage
598
+ state = STATE_CHECKING_LOGS_RUN
599
+ generated_code = None # Clear temporary storage
600
+ # Yield state change and message
601
+ yield history, repo_id, state, updated_preview, updated_run, updated_build, attempts, app_desc, repo_name, generated_code
602
+
603
 
604
  except Exception as e:
605
  history = update_chat(history, f"❌ Error uploading fixed app.py: {e}. Click 'reset'.")
 
608
  elif state == STATE_COMPLETE:
609
  # App is successfully deployed or failed after attempts.
610
  # User should click reset or start a new command.
611
+ # Just yield the current state to update UI if needed.
612
+ # The message for STATE_COMPLETE is set in the state it transitions from.
613
+ pass # No state change needed, just yield current state at the end of try block
614
+
615
 
616
+ # If we reached here and the state wasn't handled (e.g., from a previous error state)
617
+ # or if a yield didn't happen in the previous block (logic error)
618
+ # The initial yield after adding the user message ensures something is always sent back.
619
 
620
 
621
  except Exception as e:
622
  # Catch-all for unexpected exceptions in any state
623
+ # This might mean the state machine logic itself failed or a core function raised unexpectedly.
624
+ error_message = f"Workflow step failed unexpectedly ({state}): {e}. Click 'Send' to re-attempt this step or 'reset'."
625
+ history = update_chat(history, error_message)
626
+ print(f"Critical Error in state {state}: {e}") # Print to server logs
627
+ # Transition to idle state on unexpected errors to prevent getting stuck.
628
  yield history, None, STATE_IDLE, updated_preview, updated_run, updated_build, 0, None, None, None
629
 
630
 
631
+ # Final yield after the try block ensures the final state is returned
632
+ # after a successful step completes or after an error is caught.
633
+ # This is particularly important for states like STATE_COMPLETE.
634
+ yield history, repo_id, state, updated_preview, updated_run, updated_build, attempts, app_desc, repo_name, generated_code
635
+
636
+
637
+
638
  # --- Build the Gradio UI ---
639
 
640
  with gr.Blocks(title="AI-Powered HF Space App Builder") as ai_builder_tab:
 
649
  debug_attempts = gr.State(0) # Counter for debug attempts
650
  app_description = gr.State(None) # Stores the user's original request string
651
  repo_name_state = gr.State(None) # Stores the parsed repo name
652
+ generated_code_state = gr.State(None) # Temporarily stores generated code or file content (reused)
653
 
654
  with gr.Row():
655
  # Sidebar
 
660
 
661
  # Initial load to check login status
662
  ai_builder_tab.load(show_profile, outputs=login_status)
663
+ # Update status and state on login click
664
  login_btn.click(show_profile, outputs=login_status)
665
+ login_btn.click(lambda p, t: (p, t), inputs=[login_btn], outputs=[hf_profile, hf_token]) # Use login_btn output directly
 
666
 
667
  gr.Markdown("## Google AI Studio API Key")
668
  gemini_input = gr.Textbox(label="API Key", type="password", interactive=True) # Ensure interactive
 
676
  choices=[
677
  ("Gemini 2.5 Flash Preview 04-17", "gemini-2.5-flash-preview-04-17"),
678
  ("Gemini 2.5 Pro Preview 03-25", "gemini-2.5-pro-preview-03-25"),
679
+ ("Gemini 1.5 Flash", "gemini-1.5-flash"), # Keep relevant models
680
+ ("Gemini 1.5 Pro", "gemini-1.5-pro"),
681
+ ("Gemini 1.0 Pro", "gemini-1.0-pro"),
682
  ],
683
  value="gemini-2.5-flash-preview-04-17",
684
  label="Select model",
 
688
  model_selector.change(lambda m: m, inputs=model_selector, outputs=gemini_model)
689
 
690
  # Configure Gemini status on load and when key/model changes
691
+ # Note: These handlers *update the status text*, they don't block the workflow.
692
  ai_builder_tab.load(
693
  configure_gemini,
694
  inputs=[gemini_key, gemini_model],
 
716
 
717
  # Main content
718
  with gr.Column(scale=3):
719
+ # Corrected Chatbot initialization
720
+ chatbot = gr.Chatbot(type='messages') # Added type='messages'
721
  user_input = gr.Textbox(placeholder="Type your message…", interactive=True) # Ensure interactive
722
  send_btn = gr.Button("Send", interactive=False)
723
 
724
  # Logic to enable send button only when logged in and API key is set
725
+ # This function determines the interactive state of the button
726
+ def update_send_button_state(profile: gr.OAuthProfile | None, token: gr.OAuthToken | None, key: str | None, model: str | None):
727
  is_logged_in = profile is not None and token is not None
728
+ is_gemini_ready = key is not None and model is not None # Basic check
729
+ # Could add a check if configure_gemini returned success last time
 
730
  return gr.update(interactive=is_logged_in and is_gemini_ready)
731
 
732
+ # Update button state on load and whenever relevant inputs change
733
  ai_builder_tab.load(
734
  update_send_button_state,
735
  inputs=[hf_profile, hf_token, gemini_key, gemini_model],
 
751
  outputs=[send_btn]
752
  )
753
 
 
754
  iframe = gr.HTML("<p>No Space created yet.</p>")
755
  build_txt = gr.Textbox(label="Build Logs", lines=10, interactive=False, value="") # Initialize empty
756
  run_txt = gr.Textbox(label="Container Logs", lines=10, interactive=False, value="") # Initialize empty
757
 
758
  # The main event handler for the Send button
759
+ # It maps inputs/outputs to the ai_workflow_chat generator function
760
  send_btn.click(
761
  ai_workflow_chat,
762
  inputs=[
 
779
  outputs=user_input
780
  )
781
 
782
+ # Link state variables to UI status displays (reactive updates)
783
+ # These update the UI components whenever the State variables they listen to change.
784
  workflow.change(lambda s: s, inputs=workflow, outputs=status_text)
785
  repo_id.change(lambda r: r if r else "None", inputs=repo_id, outputs=repo_id_text)
786
+ # The logs and iframe are updated directly by the `send_btn.click` output,
787
+ # but adding reactive updates from the state variables can sometimes help
788
+ # ensure consistency if state changes are yielded before the UI components are
789
+ # explicitly updated in the same yield tuple.
790
+ # iframe.change(lambda h: h, inputs=iframe, outputs=iframe) # Already linked via click outputs
791
+ # build_txt.change(lambda t: t, inputs=build_txt, outputs=build_txt) # Already linked
792
+ # run_txt.change(lambda t: t, inputs=run_txt, outputs=run_txt) # Already linked
793
+
794
+
795
+ # Add an initial message to the chatbot on load
796
+ # THIS CALL MUST BE INSIDE the with gr.Blocks() block
797
+ def greet():
798
+ # Returning a list of lists in the format [user_msg, bot_msg] for Chatbot
799
+ # An initial message is often just a bot message, format can be [None, bot_msg]
800
+ # but Chatbot(type='messages') expects {'role': ..., 'content': ...}
801
+ # For the initial message, the tuple format might still work, or we structure it.
802
+ # Let's try the tuple format first as it was used before.
803
+ return [[None, "Welcome! Please log in to Hugging Face and provide your Google AI Studio API key to start building Spaces. Once ready, type 'generate me a gradio app called myapp' or 'create' to begin."]]
804
+
805
+ ai_builder_tab.load(greet, outputs=chatbot)
806
 
807
 
808
  if __name__ == "__main__":