Nymbo commited on
Commit
63903e4
·
verified ·
1 Parent(s): d2ae72a

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +111 -109
app.py CHANGED
@@ -15,7 +15,7 @@ def encode_image(image_path):
15
  print("No image path provided")
16
  return None
17
 
18
- try:
19
  print(f"Encoding image from path: {image_path}")
20
 
21
  # If it's already a PIL Image
@@ -239,8 +239,6 @@ with gr.Blocks(theme="Nymbo/Nymbo_Theme") as demo:
239
  sources=["upload"]
240
  )
241
 
242
- # Note: We're removing the separate submit button since MultimodalTextbox has its own
243
-
244
  # Create accordion for settings
245
  with gr.Accordion("Settings", open=False):
246
  # System message
@@ -338,7 +336,6 @@ with gr.Blocks(theme="Nymbo/Nymbo_Theme") as demo:
338
  )
339
 
340
  # Featured models list
341
- # Updated to include multimodal models
342
  models_list = [
343
  "meta-llama/Llama-3.2-11B-Vision-Instruct",
344
  "meta-llama/Llama-3.3-70B-Instruct",
@@ -374,6 +371,42 @@ with gr.Blocks(theme="Nymbo/Nymbo_Theme") as demo:
374
 
375
  gr.Markdown("[View all Text-to-Text models](https://huggingface.co/models?inference_provider=all&pipeline_tag=text-generation&sort=trending) | [View all multimodal models](https://huggingface.co/models?inference_provider=all&pipeline_tag=image-text-to-text&sort=trending)")
376
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
377
  # Chat history state
378
  chat_history = gr.State([])
379
 
@@ -391,126 +424,99 @@ with gr.Blocks(theme="Nymbo/Nymbo_Theme") as demo:
391
 
392
  # Function for the chat interface
393
  def user(user_message, history):
394
- # Debug logging for troubleshooting
395
  print(f"User message received: {user_message}")
396
 
397
- # Skip if message is empty (no text and no files)
398
  if not user_message or (not user_message.get("text") and not user_message.get("files")):
399
  print("Empty message, skipping")
400
- return history
401
 
402
- # Prepare multimodal message format
403
  text_content = user_message.get("text", "").strip()
404
  files = user_message.get("files", [])
405
 
406
  print(f"Text content: {text_content}")
407
  print(f"Files: {files}")
408
 
409
- # If both text and files are empty, skip
410
- if not text_content and not files:
411
  print("No content to display")
412
  return history
 
 
 
 
 
413
 
414
- # Add message with images to history
415
- if files and len(files) > 0:
416
- # Add text message first if it exists
417
- if text_content:
418
- # Add a separate text message
419
- print(f"Adding text message: {text_content}")
420
- history.append([text_content, None])
421
-
422
- # Then add each image file separately
423
  for file_path in files:
424
- if file_path and isinstance(file_path, str):
425
  print(f"Adding image: {file_path}")
426
- # Add image as a separate message with no text
427
- history.append([f"![Image]({file_path})", None])
428
-
429
- return history
430
- else:
431
- # For text-only messages
432
- print(f"Adding text-only message: {text_content}")
433
- history.append([text_content, None])
434
- return history
435
 
436
  # Define bot response function
437
  def bot(history, system_msg, max_tokens, temperature, top_p, freq_penalty, seed, provider, api_key, custom_model, search_term, selected_model):
438
- # Check if history is valid
439
- if not history or len(history) == 0:
440
- print("No history to process")
441
- return history
 
 
 
 
 
 
 
 
 
 
 
 
 
442
 
443
- # Get the most recent message and detect if it's an image
444
- user_message = history[-1][0]
445
- print(f"Processing user message: {user_message}")
446
-
447
- is_image = False
448
- image_path = None
449
- text_content = user_message
450
-
451
- # Check if this is an image message (marked with ![Image])
452
- if isinstance(user_message, str) and user_message.startswith("![Image]("):
453
- is_image = True
454
- # Extract image path from markdown format ![Image](path)
455
- image_path = user_message.replace("![Image](", "").replace(")", "")
456
- print(f"Image detected: {image_path}")
457
- text_content = "" # No text for image-only messages
458
-
459
- # Look back for text context if this is an image
460
- text_context = ""
461
- if is_image and len(history) > 1:
462
- # Use the previous message as context if it's text
463
- prev_message = history[-2][0]
464
- if isinstance(prev_message, str) and not prev_message.startswith("![Image]("):
465
- text_context = prev_message
466
- print(f"Using text context from previous message: {text_context}")
467
-
468
- # Process message through respond function
469
- history[-1][1] = ""
470
-
471
- # Use either the image or text for the API
472
- if is_image:
473
- # For image messages
474
- for response in respond(
475
- text_context, # Text context from previous message if any
476
- [image_path], # Current image
477
- history[:-1], # Previous history
478
- system_msg,
479
- max_tokens,
480
- temperature,
481
- top_p,
482
- freq_penalty,
483
- seed,
484
- provider,
485
- api_key,
486
- custom_model,
487
- search_term,
488
- selected_model
489
- ):
490
- history[-1][1] = response
491
- yield history
492
- else:
493
- # For text-only messages
494
- for response in respond(
495
- text_content, # Text message
496
- None, # No image
497
- history[:-1], # Previous history
498
- system_msg,
499
- max_tokens,
500
- temperature,
501
- top_p,
502
- freq_penalty,
503
- seed,
504
- provider,
505
- api_key,
506
- custom_model,
507
- search_term,
508
- selected_model
509
- ):
510
- history[-1][1] = response
511
- yield history
512
-
513
- # Event handlers - only using the MultimodalTextbox's built-in submit functionality
514
  msg.submit(
515
  user,
516
  [msg, chatbot],
@@ -528,7 +534,6 @@ with gr.Blocks(theme="Nymbo/Nymbo_Theme") as demo:
528
  [msg]
529
  )
530
 
531
- # Connect the model filter to update the radio choices
532
  model_search_box.change(
533
  fn=filter_models,
534
  inputs=model_search_box,
@@ -536,7 +541,6 @@ with gr.Blocks(theme="Nymbo/Nymbo_Theme") as demo:
536
  )
537
  print("Model search box change event linked.")
538
 
539
- # Connect the featured model radio to update the custom model box
540
  featured_model_radio.change(
541
  fn=set_custom_model_from_radio,
542
  inputs=featured_model_radio,
@@ -544,7 +548,6 @@ with gr.Blocks(theme="Nymbo/Nymbo_Theme") as demo:
544
  )
545
  print("Featured model radio button change event linked.")
546
 
547
- # Connect the BYOK textbox to validate provider selection
548
  byok_textbox.change(
549
  fn=validate_provider,
550
  inputs=[byok_textbox, provider_radio],
@@ -552,7 +555,6 @@ with gr.Blocks(theme="Nymbo/Nymbo_Theme") as demo:
552
  )
553
  print("BYOK textbox change event linked.")
554
 
555
- # Also validate provider when the radio changes to ensure consistency
556
  provider_radio.change(
557
  fn=validate_provider,
558
  inputs=[byok_textbox, provider_radio],
@@ -564,4 +566,4 @@ print("Gradio interface initialized.")
564
 
565
  if __name__ == "__main__":
566
  print("Launching the demo application.")
567
- demo.launch(show_api=True)
 
15
  print("No image path provided")
16
  return None
17
 
18
+ try
19
  print(f"Encoding image from path: {image_path}")
20
 
21
  # If it's already a PIL Image
 
239
  sources=["upload"]
240
  )
241
 
 
 
242
  # Create accordion for settings
243
  with gr.Accordion("Settings", open=False):
244
  # System message
 
336
  )
337
 
338
  # Featured models list
 
339
  models_list = [
340
  "meta-llama/Llama-3.2-11B-Vision-Instruct",
341
  "meta-llama/Llama-3.3-70B-Instruct",
 
371
 
372
  gr.Markdown("[View all Text-to-Text models](https://huggingface.co/models?inference_provider=all&pipeline_tag=text-generation&sort=trending) | [View all multimodal models](https://huggingface.co/models?inference_provider=all&pipeline_tag=image-text-to-text&sort=trending)")
373
 
374
+ # MCP Support Information Accordion
375
+ with gr.Accordion("MCP Support (for LLMs)", open=False):
376
+ gr.Markdown("""
377
+ ### Model Context Protocol (MCP) Support
378
+
379
+ This application can function as an MCP Server, allowing compatible AI models and agents (like Claude Desktop or custom MCP clients) to use its text and image generation capabilities as a tool.
380
+
381
+ When MCP is enabled, Gradio automatically exposes the relevant functions (likely based on the `bot` function in this app) as MCP tools.
382
+
383
+ **To connect an MCP client to this server:**
384
+
385
+ 1. Ensure this Gradio application is running.
386
+ 2. Use the following URL for the MCP server in your client configuration:
387
+ - If running locally: `http://127.0.0.1:7860/gradio_api/mcp/sse`
388
+ - If deployed on Hugging Face Spaces: `https://YOUR_USERNAME-YOUR_SPACENAME.hf.space/gradio_api/mcp/sse` (replace with your actual Space URL)
389
+
390
+ **Example MCP Client Configuration (`mcp.json` or similar):**
391
+ ```json
392
+ {
393
+ "mcpServers": {
394
+ "serverlessTextgenHub": {
395
+ "url": "http://127.0.0.1:7860/gradio_api/mcp/sse"
396
+ }
397
+ }
398
+ }
399
+ ```
400
+
401
+ **Tool Parameters:**
402
+ The exposed MCP tool will likely have parameters corresponding to the inputs of the `bot` function (e.g., `history`, `system_msg`, `max_tokens`, `temperature`, `model`, etc.).
403
+
404
+ * **Important for `history` parameter:** For image inputs, the MCP client might need to format the `history` to include image references in a way the `bot` function can parse (e.g., markdown links `![Image](URL_or_base64_data_uri)` within the history's message part).
405
+ * It's highly recommended to inspect the MCP schema for this server to understand the exact tool names, descriptions, and input/output schemas. You can usually find this at: `http://127.0.0.1:7860/gradio_api/mcp/schema` (or the equivalent URL for your deployed Space).
406
+
407
+ This allows for powerful integrations where an AI agent can programmatically request text or multimodal generations from this Serverless-TextGen-Hub.
408
+ """)
409
+
410
  # Chat history state
411
  chat_history = gr.State([])
412
 
 
424
 
425
  # Function for the chat interface
426
  def user(user_message, history):
 
427
  print(f"User message received: {user_message}")
428
 
 
429
  if not user_message or (not user_message.get("text") and not user_message.get("files")):
430
  print("Empty message, skipping")
431
+ return history # Return immediately if message is empty
432
 
 
433
  text_content = user_message.get("text", "").strip()
434
  files = user_message.get("files", [])
435
 
436
  print(f"Text content: {text_content}")
437
  print(f"Files: {files}")
438
 
439
+ if not text_content and not files: # Check again after stripping text
 
440
  print("No content to display")
441
  return history
442
+
443
+ # Append text message first if it exists and is not empty
444
+ if text_content:
445
+ print(f"Adding text message: {text_content}")
446
+ history.append([text_content, None])
447
 
448
+ # Then append each image file as a separate message
449
+ if files:
 
 
 
 
 
 
 
450
  for file_path in files:
451
+ if file_path and isinstance(file_path, str): # Ensure file_path is valid
452
  print(f"Adding image: {file_path}")
453
+ history.append([f"![Image]({file_path})", None]) # Image as a new message
454
+
455
+ return history
 
 
 
 
 
 
456
 
457
  # Define bot response function
458
  def bot(history, system_msg, max_tokens, temperature, top_p, freq_penalty, seed, provider, api_key, custom_model, search_term, selected_model):
459
+ if not history or not history[-1][0]: # Check if history or last message is empty
460
+ print("No history or empty last message to process for bot")
461
+ # Yield an empty update or the history itself to avoid errors
462
+ # depending on how Gradio handles empty yields.
463
+ # For safety, just return the history if it's in a bad state.
464
+ yield history
465
+ return
466
+
467
+ user_message_content = history[-1][0] # This is the user's latest message (text or image markdown)
468
+ print(f"Bot processing user message content: {user_message_content}")
469
+
470
+ # Determine if the current turn is primarily about an image or text
471
+ # This logic assumes images are added as separate history entries like "![Image](path)"
472
+ # and text prompts might precede them or be separate.
473
+
474
+ current_message_text_for_api = ""
475
+ current_image_files_for_api = []
476
 
477
+ # Check if the last entry is an image
478
+ if isinstance(user_message_content, str) and user_message_content.startswith("![Image]("):
479
+ image_path = user_message_content.replace("![Image](", "").replace(")", "")
480
+ current_image_files_for_api.append(image_path)
481
+ print(f"Bot identified image in last history entry: {image_path}")
482
+ # If it's an image, check the second to last entry for a text prompt
483
+ if len(history) > 1:
484
+ prev_content = history[-2][0]
485
+ if isinstance(prev_content, str) and not prev_content.startswith("![Image]("):
486
+ current_message_text_for_api = prev_content
487
+ print(f"Bot identified preceding text for image: {current_message_text_for_api}")
488
+ else: # Last entry is text
489
+ current_message_text_for_api = user_message_content
490
+ print(f"Bot identified text in last history entry: {current_message_text_for_api}")
491
+
492
+ # The history sent to `respond` should not include the current turn's input,
493
+ # as `respond` will add `message` (current_message_text_for_api) to its internal `messages` list.
494
+ # If an image is present, it's passed via `image_files`.
495
+ history_for_respond_func = history[:-1] # Pass history *before* the current turn
496
+
497
+ history[-1][1] = "" # Initialize assistant's response for the current turn
498
+
499
+ for response_chunk in respond(
500
+ message=current_message_text_for_api,
501
+ image_files=current_image_files_for_api,
502
+ history=history_for_respond_func, # Pass prior history
503
+ system_message=system_msg,
504
+ max_tokens=max_tokens,
505
+ temperature=temperature,
506
+ top_p=top_p,
507
+ frequency_penalty=freq_penalty,
508
+ seed=seed,
509
+ provider=provider,
510
+ custom_api_key=api_key,
511
+ custom_model=custom_model,
512
+ model_search_term=search_term, # Though these two might not be directly used by respond if model is fixed
513
+ selected_model=selected_model
514
+ ):
515
+ history[-1][1] = response_chunk
516
+ yield history
517
+
518
+
519
+ # Event handlers
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
520
  msg.submit(
521
  user,
522
  [msg, chatbot],
 
534
  [msg]
535
  )
536
 
 
537
  model_search_box.change(
538
  fn=filter_models,
539
  inputs=model_search_box,
 
541
  )
542
  print("Model search box change event linked.")
543
 
 
544
  featured_model_radio.change(
545
  fn=set_custom_model_from_radio,
546
  inputs=featured_model_radio,
 
548
  )
549
  print("Featured model radio button change event linked.")
550
 
 
551
  byok_textbox.change(
552
  fn=validate_provider,
553
  inputs=[byok_textbox, provider_radio],
 
555
  )
556
  print("BYOK textbox change event linked.")
557
 
 
558
  provider_radio.change(
559
  fn=validate_provider,
560
  inputs=[byok_textbox, provider_radio],
 
566
 
567
  if __name__ == "__main__":
568
  print("Launching the demo application.")
569
+ demo.launch(show_api=True, mcp_server=True) # MCP SERVER ENABLED HERE