Spaces:
Runtime error
Runtime error
| """ | |
| AI Assistant - Gradio Application | |
| Powered by HuggingFace Hub | |
| """ | |
| from random import random | |
| import gradio as gr | |
| import os | |
| import json | |
| from datetime import datetime | |
| from hf_api import HuggingFaceAPI | |
| from utils import load_settings, save_settings | |
| import warnings | |
| import warnings | |
| import torch | |
| # Suppress the specific deprecation warning | |
| warnings.filterwarnings("ignore", | |
| message=".*torch.distributed.reduce_op.*", | |
| category=FutureWarning) | |
| # Optional import for Google Translate Gemma | |
| try: | |
| from google_translate import GoogleTranslateGemma | |
| GOOGLE_TRANSLATE_AVAILABLE = True | |
| except ImportError as e: | |
| print(f"Warning: Google Translate Gemma not available: {str(e)}") | |
| GOOGLE_TRANSLATE_AVAILABLE = False | |
| GoogleTranslateGemma = None | |
| # Translation testing functions | |
| def test_translategemma(text, source_lang, target_lang): | |
| """Test Google Translate Gemma model directly""" | |
| if not GOOGLE_TRANSLATE_AVAILABLE: | |
| print("❌ Google Translate Gemma not available. Using chat completion fallback.") | |
| return test_chat_completion_translation(text, source_lang, target_lang) | |
| print(f"🧪 Testing Google Translate Gemma") | |
| print(f" Text: {text}") | |
| print(f" Source: {source_lang}") | |
| print(f" Target: {target_lang}") | |
| print("-" * 50) | |
| try: | |
| # Initialize the model | |
| translator = GoogleTranslateGemma() | |
| # Perform translation | |
| translation = translator.translate(text, source_lang, target_lang) | |
| if translation: | |
| print(f"✅ Translation: {translation}") | |
| print(" ✅ Google Translate Gemma working correctly!") | |
| return translation | |
| else: | |
| print("❌ No translation returned") | |
| return None | |
| except Exception as e: | |
| print(f"❌ Error: {str(e)}") | |
| print(" ⚠️ Falling back to chat completion translation...") | |
| return test_chat_completion_translation(text, source_lang, target_lang) | |
| def test_chat_completion_translation(text, source_lang, target_lang): | |
| """Test translation using chat completion fallback""" | |
| if not hf_api: | |
| print("❌ No HuggingFace API available. Please set your token first.") | |
| return None | |
| # Test models in order of preference | |
| models_to_test = [ | |
| "google/translategemma-12b-it", | |
| "meta-llama/Llama-3.2-3B-Instruct", | |
| "microsoft/Phi-3-mini-4k-instruct", | |
| "google/gemma-2-2b-it" | |
| ] | |
| print(f"🧪 Testing translation with chat completion") | |
| print(f" Text: {text}") | |
| print(f" Source: {source_lang}") | |
| print(f" Target: {target_lang}") | |
| print("-" * 50) | |
| for model_id in models_to_test: | |
| print(f"\n📝 Testing with model: {model_id}") | |
| try: | |
| # Use the same translation logic as in the main translation function | |
| if "translategemma" in model_id.lower() and not GOOGLE_TRANSLATE_AVAILABLE: | |
| print(" ⚠️ Google Translate Gemma not available, skipping...") | |
| continue | |
| # Dynamic system prompt based on target and source language | |
| source_info = f" from {source_lang}" if source_lang != "Auto-detect" else "" | |
| system_prompt = f"You are a professional translator specializing in translating{source_info} to {target_lang}. Translate the given text accurately while preserving the original meaning and tone. Only provide the translation without any additional explanations." | |
| prompt = f"Translate the following text{source_info} to {target_lang}: {text}" | |
| messages = [ | |
| { | |
| "role": "system", | |
| "content": system_prompt | |
| }, | |
| { | |
| "role": "user", | |
| "content": prompt | |
| } | |
| ] | |
| response = hf_api.chat_completion( | |
| model=model_id, | |
| messages=messages, | |
| max_tokens=1024, | |
| temperature=0.3 | |
| ) | |
| translation = response["choices"][0]["message"]["content"].strip() | |
| print(f"✅ Translation: {translation}") | |
| print(f" ✅ Success with {model_id}!") | |
| return translation # Return first successful translation | |
| except Exception as e: | |
| print(f" ❌ Error with {model_id}: {str(e)}") | |
| continue | |
| print("\n❌ All models failed. Please check your token and model availability.") | |
| return None | |
| def run_multiple_translation_tests(): | |
| """Run multiple translation test scenarios""" | |
| test_cases = [ | |
| { | |
| "text": "Hello, how are you today?", | |
| "source": "English", | |
| "target": "Spanish", | |
| "description": "English to Spanish" | |
| }, | |
| { | |
| "text": "V nejhorším případě i k prasknutí čočky.", | |
| "source": "Czech", | |
| "target": "German", | |
| "description": "Czech to German" | |
| }, | |
| { | |
| "text": "Bonjour, comment allez-vous?", | |
| "source": "French", | |
| "target": "English", | |
| "description": "French to English" | |
| }, | |
| { | |
| "text": "这是一个测试。", | |
| "source": "Chinese (Simplified)", | |
| "target": "English", | |
| "description": "Chinese to English" | |
| }, | |
| { | |
| "text": "¡Hola! ¿Cómo estás?", | |
| "source": "Spanish", | |
| "target": "Japanese", | |
| "description": "Spanish to Japanese" | |
| } | |
| ] | |
| results = [] | |
| for i, case in enumerate(test_cases, 1): | |
| print(f"\n📝 Test {i}: {case['description']}") | |
| print(f" Source ({case['source']}): {case['text']}") | |
| # Map language names to codes | |
| lang_code_map = { | |
| "English": "en", | |
| "Spanish": "es", | |
| "French": "fr", | |
| "German": "de-DE", | |
| "Chinese (Simplified)": "zh-CN", | |
| "Chinese (Traditional)": "zh-TW", | |
| "Japanese": "ja", | |
| "Korean": "ko", | |
| "Italian": "it", | |
| "Portuguese": "pt", | |
| "Russian": "ru", | |
| "Arabic": "ar", | |
| "Hindi": "hi", | |
| "Dutch": "nl", | |
| "Turkish": "tr", | |
| "Polish": "pl", | |
| "Vietnamese": "vi", | |
| "Thai": "th", | |
| "Indonesian": "id", | |
| "Greek": "el", | |
| "Hebrew": "he", | |
| "Czech": "cs", | |
| "Swedish": "sv", | |
| "Danish": "da", | |
| "Norwegian": "no", | |
| "Finnish": "fi" | |
| } | |
| source_code = lang_code_map.get(case['source'], 'en') | |
| target_code = lang_code_map.get(case['target'], 'en') | |
| translation = test_translategemma( | |
| text=case['text'], | |
| source_lang=source_code, | |
| target_lang=target_code | |
| ) | |
| if translation: | |
| print(f" Target ({case['target']}): {translation}") | |
| results.append({ | |
| 'case': case['description'], | |
| 'original': case['text'], | |
| 'translation': translation, | |
| 'success': True | |
| }) | |
| else: | |
| results.append({ | |
| 'case': case['description'], | |
| 'original': case['text'], | |
| 'translation': None, | |
| 'success': False | |
| }) | |
| # Summary | |
| successful = sum(1 for r in results if r['success']) | |
| total = len(results) | |
| summary = f""" | |
| 📊 Test Summary | |
| {"=" * 60} | |
| Total tests: {total} | |
| Successful: {successful} | |
| Failed: {total - successful} | |
| Success rate: {successful/total*100:.1f}% | |
| """ | |
| if successful < total: | |
| summary += "❌ Some tests failed. Check your HuggingFace token and model availability." | |
| else: | |
| summary += "✅ All tests passed successfully!" | |
| return results, summary | |
| # Settings paths | |
| SETTINGS_DIR = os.path.join(os.path.dirname(__file__), 'settings') | |
| MODELS_SETTINGS_FILE = os.path.join(SETTINGS_DIR, 'models.json') | |
| FIREBASE_SETTINGS_FILE = os.path.join(SETTINGS_DIR, 'firebase.json') | |
| APP_SETTINGS_FILE = os.path.join(SETTINGS_DIR, 'app.json') | |
| # Load initial settings | |
| model_settings = load_settings(MODELS_SETTINGS_FILE) | |
| HF_TOKEN = model_settings.get('huggingfaceToken', '') | |
| hf_api = HuggingFaceAPI(token=HF_TOKEN) if HF_TOKEN else None | |
| def reinit_api(token: str): | |
| """Reinitialize HuggingFace API with new token""" | |
| global hf_api | |
| hf_api = HuggingFaceAPI(token=token) | |
| def get_saved_models(): | |
| """Get list of saved models""" | |
| settings = load_settings(MODELS_SETTINGS_FILE) | |
| models = settings.get('models', []) | |
| return [(m.get('name', m.get('modelId', 'Unknown')), m.get('modelId', '')) for m in models if m.get('enabled', True)] | |
| def get_model_choices(): | |
| """Get model choices for dropdown""" | |
| models = get_saved_models() | |
| if not models: | |
| return ["meta-llama/Llama-3.2-3B-Instruct"] | |
| return [m[1] for m in models] | |
| # ============ Chat Functions ============ | |
| def chat_response(message: str, history: list, model_id: str, temperature: float, max_tokens: int, system_prompt: str): | |
| """Generate chat response""" | |
| if not hf_api: | |
| return "Please set your HuggingFace token in Settings first." | |
| if not message.strip(): | |
| return "" | |
| try: | |
| # Build messages with system prompt | |
| messages = [] | |
| if system_prompt.strip(): | |
| messages.append({"role": "system", "content": system_prompt}) | |
| # Add history from message format | |
| for msg in history: | |
| if isinstance(msg, dict): | |
| messages.append(msg) | |
| elif isinstance(msg, tuple) and len(msg) == 2: | |
| # Handle legacy tuple format | |
| user_msg, assistant_msg = msg | |
| if user_msg: | |
| messages.append({"role": "user", "content": user_msg}) | |
| if assistant_msg: | |
| messages.append({"role": "assistant", "content": assistant_msg}) | |
| # Add current message | |
| messages.append({"role": "user", "content": message}) | |
| response = hf_api.chat_completion( | |
| model=model_id, | |
| messages=messages, | |
| max_tokens=max_tokens, | |
| temperature=temperature | |
| ) | |
| return response["choices"][0]["message"]["content"] | |
| except Exception as e: | |
| error_str = str(e) | |
| # Check if it's a model not supported error | |
| if "model_not_supported" in error_str or "not supported by any provider" in error_str: | |
| # Try to get fallback models | |
| try: | |
| fallback_models = hf_api._find_fallback_models(model_id) | |
| if fallback_models: | |
| fallback_list = "\n".join([f"- {m['id']}" for m in fallback_models[:3]]) | |
| return f"Error: Model {model_id} is not supported. Try one of these models instead:\n{fallback_list}\n\nOriginal error: {error_str}" | |
| else: | |
| return f"Error: Model {model_id} is not supported and no fallback models are available.\n\nOriginal error: {error_str}" | |
| except: | |
| return f"Error: Model {model_id} is not supported. Please try a different model.\n\nOriginal error: {error_str}" | |
| else: | |
| return f"Error: {error_str}" | |
| def text_generation(prompt: str, model_id: str, temperature: float, max_tokens: int, top_p: float): | |
| """Generate text from prompt""" | |
| if not hf_api: | |
| return "Please set your HuggingFace token in Settings first." | |
| if not prompt.strip(): | |
| return "" | |
| try: | |
| # Check model settings for task support | |
| model_settings = load_settings(MODELS_SETTINGS_FILE) | |
| models = model_settings.get('models', []) | |
| # Find the model in settings | |
| model_info = None | |
| for m in models: | |
| if m.get('modelId') == model_id: | |
| model_info = m | |
| break | |
| # Check if model recommends chat_completion | |
| if model_info and model_info.get('recommendedMethod') == 'chat_completion': | |
| # Use chat completion for conversational models | |
| messages = [{"role": "user", "content": prompt}] | |
| response = hf_api.chat_completion( | |
| model=model_id, | |
| messages=messages, | |
| max_tokens=max_tokens, | |
| temperature=temperature | |
| ) | |
| return response["choices"][0]["message"]["content"] | |
| else: | |
| # Use text generation for other models | |
| response = hf_api.text_generation( | |
| model=model_id, | |
| prompt=prompt, | |
| max_new_tokens=max_tokens, | |
| temperature=temperature, | |
| top_p=top_p | |
| ) | |
| return response.get("generated_text", "") | |
| except Exception as e: | |
| error_str = str(e) | |
| # Check if it's a model not supported error | |
| if "model_not_supported" in error_str or "not supported by any provider" in error_str: | |
| # Try to get fallback models | |
| try: | |
| fallback_models = hf_api._find_fallback_models(model_id) | |
| if fallback_models: | |
| fallback_list = "\n".join([f"- {m['id']}" for m in fallback_models[:3]]) | |
| return f"Error: Model {model_id} is not supported. Try one of these models instead:\n{fallback_list}\n\nOriginal error: {error_str}" | |
| else: | |
| return f"Error: Model {model_id} is not supported and no fallback models are available.\n\nOriginal error: {error_str}" | |
| except: | |
| return f"Error: Model {model_id} is not supported. Please try a different model.\n\nOriginal error: {error_str}" | |
| else: | |
| return f"Error: {error_str}" | |
| def summarize_text(text: str, model_id: str, max_length: int, min_length: int): | |
| """Summarize text""" | |
| if not hf_api: | |
| return "Please set your HuggingFace token in Settings first." | |
| if not text.strip(): | |
| return "" | |
| try: | |
| response = hf_api.summarization( | |
| model=model_id, | |
| text=text, | |
| max_length=max_length, | |
| min_length=min_length | |
| ) | |
| if isinstance(response, list) and len(response) > 0: | |
| return response[0].get('summary_text', '') | |
| elif isinstance(response, dict): | |
| return response.get('summary_text', str(response)) | |
| return str(response) | |
| except Exception as e: | |
| return f"Error: {str(e)}" | |
| def translate_text(text: str, model_id: str, target_language: str = "", source_language: str = "Auto-detect"): | |
| """Translate text""" | |
| if not hf_api: | |
| return "Please set your HuggingFace token in Settings first." | |
| if not text.strip(): | |
| return "" | |
| try: | |
| # Use Google Translate Gemma module for Google TranslateGemma model | |
| if "translategemma" in model_id.lower(): | |
| if not GOOGLE_TRANSLATE_AVAILABLE: | |
| # If Google Translate is not available, fall back to chat completion | |
| print("Google Translate Gemma not available, falling back to chat completion") | |
| source_info = f" from {source_language}" if source_language != "Auto-detect" else "" | |
| system_prompt = f"You are a professional translator specializing in translating{source_info} to {target_language}. Translate the given text accurately while preserving the original meaning and tone. Only provide the translation without any additional explanations." | |
| prompt = f"Translate the following text{source_info} to {target_language}: {text}" | |
| messages = [ | |
| {"role": "system", "content": system_prompt}, | |
| {"role": "user", "content": prompt} | |
| ] | |
| response = hf_api.chat_completion( | |
| model=model_id, | |
| messages=messages, | |
| max_tokens=1024, | |
| temperature=0.3 | |
| ) | |
| return response["choices"][0]["message"]["content"].strip() | |
| try: | |
| # Initialize the translator | |
| translator = GoogleTranslateGemma() | |
| # Map language names to language codes | |
| lang_code_map = { | |
| "English": "en", | |
| "Spanish": "es", | |
| "French": "fr", | |
| "German": "de-DE", | |
| "Chinese (Simplified)": "zh-CN", | |
| "Chinese (Traditional)": "zh-TW", | |
| "Japanese": "ja", | |
| "Korean": "ko", | |
| "Italian": "it", | |
| "Portuguese": "pt", | |
| "Russian": "ru", | |
| "Arabic": "ar", | |
| "Hindi": "hi", | |
| "Dutch": "nl", | |
| "Turkish": "tr", | |
| "Polish": "pl", | |
| "Vietnamese": "vi", | |
| "Thai": "th", | |
| "Indonesian": "id", | |
| "Greek": "el", | |
| "Hebrew": "he", | |
| "Czech": "cs", | |
| "Swedish": "sv", | |
| "Danish": "da", | |
| "Norwegian": "no", | |
| "Finnish": "fi" | |
| } | |
| # Get source language code | |
| source_lang = "en" # Default to English | |
| if source_language != "Auto-detect" and source_language in lang_code_map: | |
| source_lang = lang_code_map[source_language] | |
| # Get target language code | |
| target_lang = "en" # Default to English | |
| if target_language in lang_code_map: | |
| target_lang = lang_code_map[target_language] | |
| # Perform translation | |
| translated = translator.translate_text( | |
| text=text, | |
| source_lang=source_lang, | |
| target_lang=target_lang | |
| ) | |
| return translated | |
| except Exception as gemma_e: | |
| # If Google Translate Gemma fails, fall back to chat completion | |
| print(f"Google Translate Gemma failed, falling back to chat completion: {str(gemma_e)}") | |
| # Use chat completion as fallback | |
| source_info = f" from {source_language}" if source_language != "Auto-detect" else "" | |
| system_prompt = f"You are a professional translator specializing in translating{source_info} to {target_language}. Translate the given text accurately while preserving the original meaning and tone. Only provide the translation without any additional explanations." | |
| prompt = f"Translate the following text{source_info} to {target_language}: {text}" | |
| messages = [ | |
| {"role": "system", "content": system_prompt}, | |
| {"role": "user", "content": prompt} | |
| ] | |
| response = hf_api.chat_completion( | |
| model=model_id, | |
| messages=messages, | |
| max_tokens=1024, | |
| temperature=0.3 | |
| ) | |
| return response["choices"][0]["message"]["content"].strip() | |
| # For models that support chat completion (like Llama and Mistral) | |
| elif "llama" in model_id.lower() or "mistral" in model_id.lower(): | |
| # Use chat completion for translation | |
| # Dynamic system prompt based on target and source language | |
| if target_language: | |
| source_info = f" from {source_language}" if source_language != "Auto-detect" else "" | |
| system_prompt = f"You are a professional translator specializing in translating{source_info} to {target_language}. Translate the given text accurately while preserving the original meaning and tone. Only provide the translation without any additional explanations." | |
| prompt = f"Translate the following text{source_info} to {target_language}: {text}" | |
| else: | |
| system_prompt = "You are a professional translator. Translate the given text accurately while preserving the original meaning and tone. Only provide the translation without any additional explanations." | |
| prompt = f"Translate this text: {text}" | |
| messages = [ | |
| { | |
| "role": "system", | |
| "content": system_prompt | |
| }, | |
| { | |
| "role": "user", | |
| "content": prompt | |
| } | |
| ] | |
| response = hf_api.chat_completion( | |
| model=model_id, | |
| messages=messages, | |
| max_tokens=1024, | |
| temperature=0.3 | |
| ) | |
| return response["choices"][0]["message"]["content"].strip() | |
| else: | |
| # Use the standard translation endpoint for other models | |
| response = hf_api.translation( | |
| model=model_id, | |
| text=text | |
| ) | |
| if isinstance(response, list) and len(response) > 0: | |
| return response[0].get('translation_text', '') | |
| elif isinstance(response, dict): | |
| return response.get('translation_text', str(response)) | |
| return str(response) | |
| except Exception as e: | |
| # Handle specific model errors with better fallback options | |
| error_str = str(e).lower() | |
| if "model_not_supported" in error_str or "not supported by any provider" in error_str or "inference api enabled" in error_str: | |
| # Try fallback models for translation | |
| fallback_models = [ | |
| "Helsinki-NLP/opus-mt-en-es", # English to Spanish | |
| "Helsinki-NLP/opus-mt-en-fr", # English to French | |
| "Helsinki-NLP/opus-mt-en-de", # English to German | |
| "Helsinki-NLP/opus-mt-en-zh", # English to Chinese | |
| "Helsinki-NLP/opus-mt-en-ja", # English to Japanese | |
| "meta-llama/Llama-3.2-3B-Instruct" # Llama as general fallback | |
| ] | |
| # Try each fallback model | |
| for fallback_model in fallback_models: | |
| try: | |
| # For Llama models, use chat completion | |
| if "llama" in fallback_model.lower(): | |
| system_prompt = "You are a professional translator. Translate the given text accurately while preserving the original meaning and tone. Only provide the translation without any additional explanations." | |
| # If target language is specified, include it in the instruction | |
| if target_language: | |
| prompt = f"Translate the following text to {target_language}: {text}" | |
| else: | |
| prompt = f"Translate this text: {text}" | |
| messages = [ | |
| { | |
| "role": "system", | |
| "content": system_prompt | |
| }, | |
| { | |
| "role": "user", | |
| "content": prompt | |
| } | |
| ] | |
| response = hf_api.chat_completion( | |
| model=fallback_model, | |
| messages=messages, | |
| max_tokens=1024, | |
| temperature=0.3 | |
| ) | |
| return f"{response['choices'][0]['message']['content'].strip()}" | |
| # For Helsinki models, use standard translation | |
| else: | |
| response = hf_api.translation( | |
| model=fallback_model, | |
| text=text | |
| ) | |
| if isinstance(response, list) and len(response) > 0: | |
| return f"{response[0].get('translation_text', '')}" | |
| elif isinstance(response, dict): | |
| return f"{response.get('translation_text', str(response))}" | |
| except Exception as fallback_e: | |
| continue # Try next fallback model | |
| # If all fallbacks fail, return original error with suggestions | |
| return f"Error: {str(e)}. Tried fallback models but none worked. Please try a different model or check your HuggingFace token." | |
| else: | |
| return f"Error: {str(e)}" | |
| def translate_image(image_path: str, model_id: str, target_language: str = "", source_language: str = "Auto-detect"): | |
| """Translate text from image""" | |
| if not image_path: | |
| return "Please upload an image first." | |
| # Only Google TranslateGemma supports image translation | |
| if "translategemma" not in model_id.lower(): | |
| return "Image translation is only supported with Google TranslateGemma model. Please select 'google/translategemma-12b-it' from the model dropdown." | |
| if not GOOGLE_TRANSLATE_AVAILABLE: | |
| return "Google Translate Gemma is not available. Please check your installation of transformers, torch, and torchvision." | |
| try: | |
| # Initialize the translator | |
| translator = GoogleTranslateGemma() | |
| # Map language names to language codes | |
| lang_code_map = { | |
| "English": "en", | |
| "Spanish": "es", | |
| "French": "fr", | |
| "German": "de-DE", | |
| "Chinese (Simplified)": "zh-CN", | |
| "Chinese (Traditional)": "zh-TW", | |
| "Japanese": "ja", | |
| "Korean": "ko", | |
| "Italian": "it", | |
| "Portuguese": "pt", | |
| "Russian": "ru", | |
| "Arabic": "ar", | |
| "Hindi": "hi", | |
| "Dutch": "nl", | |
| "Turkish": "tr", | |
| "Polish": "pl", | |
| "Vietnamese": "vi", | |
| "Thai": "th", | |
| "Indonesian": "id", | |
| "Greek": "el", | |
| "Hebrew": "he", | |
| "Czech": "cs", | |
| "Swedish": "sv", | |
| "Danish": "da", | |
| "Norwegian": "no", | |
| "Finnish": "fi" | |
| } | |
| # Get source language code | |
| source_lang = "en" # Default to English | |
| if source_language != "Auto-detect" and source_language in lang_code_map: | |
| source_lang = lang_code_map[source_language] | |
| # Get target language code | |
| target_lang = "en" # Default to English | |
| if target_language in lang_code_map: | |
| target_lang = lang_code_map[target_language] | |
| # Translate from image (now supports local files) | |
| translated = translator.translate_image( | |
| image_input=image_path, | |
| source_lang=source_lang, | |
| target_lang=target_lang | |
| ) | |
| return translated | |
| except Exception as e: | |
| return f"Error: {str(e)}" | |
| def answer_question(question: str, context: str, model_id: str): | |
| """Answer question based on context""" | |
| if not hf_api: | |
| return "Please set your HuggingFace token in Settings first.", 0.0 | |
| if not question.strip() or not context.strip(): | |
| return "", 0.0 | |
| try: | |
| response = hf_api.question_answering( | |
| model=model_id, | |
| question=question, | |
| context=context | |
| ) | |
| answer = response.get('answer', '') | |
| score = response.get('score', 0.0) | |
| return answer, round(score, 4) | |
| except Exception as e: | |
| return f"Error: {str(e)}", 0.0 | |
| def generate_image(prompt: str, model_id: str, negative_prompt: str, num_steps: int): | |
| """Generate image from prompt""" | |
| if not hf_api: | |
| return None | |
| if not prompt.strip(): | |
| return None | |
| try: | |
| image_bytes = hf_api.image_generation( | |
| model=model_id, | |
| prompt=prompt, | |
| negative_prompt=negative_prompt if negative_prompt.strip() else None, | |
| num_inference_steps=num_steps | |
| ) | |
| # Save to temp file and return path | |
| import tempfile | |
| temp_path = os.path.join(tempfile.gettempdir(), "generated_image.png") | |
| with open(temp_path, "wb") as f: | |
| f.write(image_bytes) | |
| return temp_path | |
| except Exception as e: | |
| gr.Warning(f"Image generation error: {str(e)}") | |
| return None | |
| # ============ Model Management Functions ============ | |
| def search_hf_models(query: str, task: str, limit: int): | |
| """Search HuggingFace models""" | |
| if not hf_api: | |
| return [] | |
| if not query.strip(): | |
| return [] | |
| try: | |
| models = list(hf_api.list_models( | |
| search=query, | |
| pipeline_tag=task, | |
| sort="downloads", | |
| direction=-1, | |
| limit=limit | |
| )) | |
| results = [] | |
| for model in models: | |
| downloads = model.downloads or 0 | |
| likes = model.likes or 0 | |
| downloads_str = f"{downloads/1000000:.1f}M" if downloads >= 1000000 else f"{downloads/1000:.1f}K" if downloads >= 1000 else str(downloads) | |
| results.append([ | |
| model.id, | |
| model.author or '', | |
| model.pipeline_tag or '', | |
| downloads_str, | |
| likes | |
| ]) | |
| return results | |
| except Exception as e: | |
| gr.Warning(f"Search error: {str(e)}") | |
| return [] | |
| def get_model_info(model_id: str): | |
| """Get detailed model information""" | |
| if not hf_api or not model_id.strip(): | |
| return "No model ID provided" | |
| try: | |
| info = hf_api.model_info(model_id) | |
| downloads = info.downloads or 0 | |
| likes = info.likes or 0 | |
| result = f"""### {model_id} | |
| **Author:** {info.author or 'Unknown'} | |
| **Pipeline:** {info.pipeline_tag or 'N/A'} | |
| **Library:** {info.library_name or 'N/A'} | |
| **Downloads:** {downloads:,} | |
| **Likes:** {likes:,} | |
| **Tags:** {', '.join(info.tags[:15]) if info.tags else 'None'} | |
| **Created:** {str(info.created_at)[:10] if info.created_at else 'Unknown'} | |
| """ | |
| return result | |
| except Exception as e: | |
| return f"Error fetching model info: {str(e)}" | |
| def add_model_to_settings(model_id: str, name: str, role: str, temperature: float, max_tokens: int, system_prompt: str): | |
| """Add a model to settings""" | |
| if not model_id.strip(): | |
| return "Model ID is required", get_models_table() | |
| settings = load_settings(MODELS_SETTINGS_FILE) | |
| if 'models' not in settings: | |
| settings['models'] = [] | |
| # Generate unique ID | |
| unique_id = f"model-{int(datetime.now().timestamp() * 1000)}" | |
| model_data = { | |
| "id": unique_id, | |
| "name": name or model_id.split('/')[-1], | |
| "modelId": model_id, | |
| "role": role, | |
| "temperature": temperature, | |
| "maxTokens": max_tokens, | |
| "systemPrompt": system_prompt, | |
| "keywords": [], | |
| "enabled": True, | |
| "createdAt": int(datetime.now().timestamp() * 1000), | |
| "updatedAt": int(datetime.now().timestamp() * 1000) | |
| } | |
| settings['models'].append(model_data) | |
| save_settings(MODELS_SETTINGS_FILE, settings) | |
| return f"Model '{name or model_id}' added successfully!", get_models_table() | |
| def get_models_table(): | |
| """Get models as table data""" | |
| settings = load_settings(MODELS_SETTINGS_FILE) | |
| models = settings.get('models', []) | |
| table_data = [] | |
| for m in models: | |
| table_data.append([ | |
| m.get('id', ''), | |
| m.get('name', ''), | |
| m.get('modelId', ''), | |
| m.get('role', ''), | |
| m.get('temperature', 0.3), | |
| m.get('maxTokens', 500), | |
| "✓" if m.get('enabled', True) else "✗" | |
| ]) | |
| return table_data | |
| def delete_model(model_id: str): | |
| """Delete a model from settings""" | |
| if not model_id.strip(): | |
| return "No model selected", get_models_table() | |
| settings = load_settings(MODELS_SETTINGS_FILE) | |
| if 'models' in settings: | |
| settings['models'] = [m for m in settings['models'] if m['id'] != model_id] | |
| save_settings(MODELS_SETTINGS_FILE, settings) | |
| return f"Model deleted", get_models_table() | |
| return "Model not found", get_models_table() | |
| def toggle_model(model_id: str, enabled: bool): | |
| """Toggle model enabled state""" | |
| settings = load_settings(MODELS_SETTINGS_FILE) | |
| if 'models' in settings: | |
| for m in settings['models']: | |
| if m['id'] == model_id: | |
| m['enabled'] = enabled | |
| m['updatedAt'] = int(datetime.now().timestamp() * 1000) | |
| break | |
| save_settings(MODELS_SETTINGS_FILE, settings) | |
| return get_models_table() | |
| # ============ Settings Functions ============ | |
| def save_hf_token(token: str): | |
| """Save HuggingFace token""" | |
| settings = load_settings(MODELS_SETTINGS_FILE) | |
| settings['huggingfaceToken'] = token | |
| save_settings(MODELS_SETTINGS_FILE, settings) | |
| reinit_api(token) | |
| return "Token saved successfully!" | |
| def get_hf_token(): | |
| """Get current HuggingFace token""" | |
| settings = load_settings(MODELS_SETTINGS_FILE) | |
| return settings.get('huggingfaceToken', '') | |
| def get_account_info(): | |
| """Get HuggingFace account info""" | |
| if not hf_api: | |
| return "No API token configured" | |
| try: | |
| info = hf_api.hf_api.whoami() | |
| return f"""### Account Info | |
| **Username:** {info.get('name', 'Unknown')} | |
| **Email:** {info.get('email', 'Not available')} | |
| **Organizations:** {len(info.get('orgs', []))} | |
| """ | |
| except Exception as e: | |
| return f"Error: {str(e)}" | |
| # ============ Gradio Interface ============ | |
| # Custom CSS for professional dark theme | |
| custom_css = """ | |
| /* Dark theme with modern design */ | |
| .gradio-container { | |
| background: linear-gradient(135deg, #0f0f23 0%, #1a1a2e 100%) !important; | |
| color: #e0e0e0 !important; | |
| font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif !important; | |
| } | |
| /* Header styling */ | |
| .main-header { | |
| background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important; | |
| padding: 2rem !important; | |
| border-radius: 16px !important; | |
| margin-bottom: 2rem !important; | |
| box-shadow: 0 10px 30px rgba(102, 126, 234, 0.3) !important; | |
| border: 1px solid rgba(255, 255, 255, 0.1) !important; | |
| } | |
| .main-header h1 { | |
| color: white !important; | |
| margin: 0 !important; | |
| font-size: 2.5rem !important; | |
| font-weight: 700 !important; | |
| text-shadow: 0 2px 4px rgba(0,0,0,0.3) !important; | |
| } | |
| .main-header p { | |
| color: rgba(255,255,255,0.9) !important; | |
| margin: 0.5rem 0 0 0 !important; | |
| font-size: 1.1rem !important; | |
| } | |
| /* Tab styling */ | |
| .tabs { | |
| background: transparent !important; | |
| border-radius: 12px !important; | |
| overflow: hidden !important; | |
| } | |
| .tab-nav { | |
| background: rgba(255, 255, 255, 0.05) !important; | |
| border: 1px solid rgba(255, 255, 255, 0.1) !important; | |
| border-radius: 12px !important; | |
| padding: 0.5rem !important; | |
| margin-bottom: 1.5rem !important; | |
| } | |
| .tab-nav button { | |
| background: transparent !important; | |
| color: #a0a0a0 !important; | |
| border: none !important; | |
| padding: 0.75rem 1.5rem !important; | |
| margin: 0 0.25rem !important; | |
| border-radius: 8px !important; | |
| transition: all 0.3s ease !important; | |
| font-weight: 500 !important; | |
| } | |
| .tab-nav button:hover { | |
| background: rgba(255, 255, 255, 0.1) !important; | |
| color: #ffffff !important; | |
| } | |
| .tab-nav button.selected { | |
| background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important; | |
| color: white !important; | |
| box-shadow: 0 4px 12px rgba(102, 126, 234, 0.3) !important; | |
| } | |
| /* Card styling */ | |
| .gradio-box { | |
| background: rgba(255, 255, 255, 0.05) !important; | |
| border: 1px solid rgba(255, 255, 255, 0.1) !important; | |
| border-radius: 12px !important; | |
| padding: 1.5rem !important; | |
| backdrop-filter: blur(10px) !important; | |
| transition: all 0.3s ease !important; | |
| } | |
| .gradio-box:hover { | |
| background: rgba(255, 255, 255, 0.08) !important; | |
| border-color: rgba(255, 255, 255, 0.15) !important; | |
| transform: translateY(-2px) !important; | |
| box-shadow: 0 8px 24px rgba(0, 0, 0, 0.2) !important; | |
| } | |
| /* Button styling */ | |
| .gradio-button { | |
| background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important; | |
| color: white !important; | |
| border: none !important; | |
| padding: 0.75rem 1.5rem !important; | |
| border-radius: 8px !important; | |
| font-weight: 600 !important; | |
| transition: all 0.3s ease !important; | |
| box-shadow: 0 4px 12px rgba(102, 126, 234, 0.3) !important; | |
| } | |
| .gradio-button:hover { | |
| transform: translateY(-2px) !important; | |
| box-shadow: 0 6px 20px rgba(102, 126, 234, 0.4) !important; | |
| } | |
| .gradio-button.secondary { | |
| background: rgba(255, 255, 255, 0.1) !important; | |
| color: #e0e0e0 !important; | |
| border: 1px solid rgba(255, 255, 255, 0.2) !important; | |
| } | |
| /* Input styling */ | |
| .gradio-textbox, .gradio-dropdown { | |
| background: rgba(255, 255, 255, 0.05) !important; | |
| border: 1px solid rgba(255, 255, 255, 0.2) !important; | |
| border-radius: 8px !important; | |
| color: #e0e0e0 !important; | |
| transition: all 0.3s ease !important; | |
| } | |
| .gradio-textbox:focus, .gradio-dropdown:focus { | |
| border-color: #667eea !important; | |
| box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.2) !important; | |
| background: rgba(255, 255, 255, 0.08) !important; | |
| } | |
| .gradio-textbox::placeholder { | |
| color: rgba(255, 255, 255, 0.5) !important; | |
| } | |
| /* Slider styling */ | |
| .gradio-slider { | |
| background: rgba(255, 255, 255, 0.1) !important; | |
| } | |
| .gradio-slider .slider-track { | |
| background: linear-gradient(90deg, #667eea 0%, #764ba2 100%) !important; | |
| } | |
| /* Chatbot styling */ | |
| .gradio-chatbot { | |
| background: rgba(255, 255, 255, 0.03) !important; | |
| border: 1px solid rgba(255, 255, 255, 0.1) !important; | |
| border-radius: 12px !important; | |
| } | |
| .gradio-chatbot .message { | |
| background: rgba(255, 255, 255, 0.05) !important; | |
| border-radius: 8px !important; | |
| margin: 0.5rem !important; | |
| padding: 1rem !important; | |
| } | |
| .gradio-chatbot .message.user { | |
| background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important; | |
| color: white !important; | |
| } | |
| /* Dataframe styling */ | |
| .gradio-dataframe { | |
| background: rgba(255, 255, 255, 0.05) !important; | |
| border: 1px solid rgba(255, 255, 255, 0.1) !important; | |
| border-radius: 8px !important; | |
| } | |
| .gradio-dataframe table { | |
| color: #e0e0e0 !important; | |
| } | |
| .gradio-dataframe th { | |
| background: rgba(255, 255, 255, 0.1) !important; | |
| border-bottom: 1px solid rgba(255, 255, 255, 0.2) !important; | |
| } | |
| .gradio-dataframe td { | |
| border-bottom: 1px solid rgba(255, 255, 255, 0.05) !important; | |
| } | |
| /* Markdown styling */ | |
| .gradio-markdown { | |
| color: #e0e0e0 !important; | |
| } | |
| .gradio-markdown h1, .gradio-markdown h2, .gradio-markdown h3 { | |
| color: #ffffff !important; | |
| margin-top: 1.5rem !important; | |
| } | |
| .gradio-markdown a { | |
| color: #667eea !important; | |
| } | |
| /* Footer styling */ | |
| footer { | |
| background: rgba(255, 255, 255, 0.03) !important; | |
| border-top: 1px solid rgba(255, 255, 255, 0.1) !important; | |
| padding: 1.5rem !important; | |
| text-align: center !important; | |
| color: rgba(255, 255, 255, 0.6) !important; | |
| } | |
| /* Loading animation */ | |
| .loading { | |
| display: inline-block; | |
| width: 20px; | |
| height: 20px; | |
| border: 3px solid rgba(255, 255, 255, 0.3); | |
| border-radius: 50%; | |
| border-top-color: #667eea; | |
| animation: spin 1s ease-in-out infinite; | |
| } | |
| @keyframes spin { | |
| to { transform: rotate(360deg); } | |
| } | |
| /* Responsive design */ | |
| @media (max-width: 768px) { | |
| .main-header h1 { | |
| font-size: 2rem !important; | |
| } | |
| .gradio-box { | |
| padding: 1rem !important; | |
| } | |
| .tab-nav button { | |
| padding: 0.5rem 1rem !important; | |
| font-size: 0.9rem !important; | |
| } | |
| } | |
| /* Custom component styles */ | |
| .chatbot-container { | |
| background: rgba(255, 255, 255, 0.03) !important; | |
| border: 1px solid rgba(255, 255, 255, 0.1) !important; | |
| border-radius: 12px !important; | |
| overflow: hidden !important; | |
| } | |
| .chat-input textarea { | |
| background: rgba(255, 255, 255, 0.05) !important; | |
| border: 1px solid rgba(255, 255, 255, 0.2) !important; | |
| border-radius: 8px !important; | |
| resize: none !important; | |
| transition: all 0.3s ease !important; | |
| } | |
| .chat-input textarea:focus { | |
| border-color: #667eea !important; | |
| box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.2) !important; | |
| } | |
| .send-button { | |
| background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important; | |
| border: none !important; | |
| height: 100% !important; | |
| min-height: 40px !important; | |
| } | |
| .settings-group { | |
| background: rgba(255, 255, 255, 0.05) !important; | |
| border: 1px solid rgba(255, 255, 255, 0.1) !important; | |
| border-radius: 12px !important; | |
| padding: 1.5rem !important; | |
| } | |
| .input-group, .output-group { | |
| background: rgba(255, 255, 255, 0.05) !important; | |
| border: 1px solid rgba(255, 255, 255, 0.1) !important; | |
| border-radius: 12px !important; | |
| padding: 1.5rem !important; | |
| height: 100% !important; | |
| } | |
| .output-textarea { | |
| background: rgba(255, 255, 255, 0.03) !important; | |
| border: 1px solid rgba(255, 255, 255, 0.1) !important; | |
| border-radius: 8px !important; | |
| font-family: 'Inter', monospace !important; | |
| line-height: 1.6 !important; | |
| } | |
| .translation-input, .translation-output { | |
| background: rgba(255, 255, 255, 0.05) !important; | |
| border: 1px solid rgba(255, 255, 255, 0.1) !important; | |
| border-radius: 12px !important; | |
| padding: 1.5rem !important; | |
| } | |
| .translation-result { | |
| background: rgba(102, 126, 234, 0.1) !important; | |
| border: 1px solid rgba(102, 126, 234, 0.3) !important; | |
| border-radius: 8px !important; | |
| font-weight: 500 !important; | |
| } | |
| .image-controls, .image-output { | |
| background: rgba(255, 255, 255, 0.05) !important; | |
| border: 1px solid rgba(255, 255, 255, 0.1) !important; | |
| border-radius: 12px !important; | |
| padding: 1.5rem !important; | |
| } | |
| .generated-image { | |
| border-radius: 8px !important; | |
| overflow: hidden !important; | |
| box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2) !important; | |
| } | |
| /* Animation classes */ | |
| .fade-in { | |
| animation: fadeIn 0.5s ease-in-out; | |
| } | |
| @keyframes fadeIn { | |
| from { opacity: 0; transform: translateY(10px); } | |
| to { opacity: 1; transform: translateY(0); } | |
| } | |
| .slide-up { | |
| animation: slideUp 0.3s ease-out; | |
| } | |
| @keyframes slideUp { | |
| from { transform: translateY(20px); opacity: 0; } | |
| to { transform: translateY(0); opacity: 1; } | |
| } | |
| /* Custom scrollbar */ | |
| ::-webkit-scrollbar { | |
| width: 8px; | |
| height: 8px; | |
| } | |
| ::-webkit-scrollbar-track { | |
| background: rgba(255, 255, 255, 0.05); | |
| border-radius: 4px; | |
| } | |
| ::-webkit-scrollbar-thumb { | |
| background: rgba(102, 126, 234, 0.5); | |
| border-radius: 4px; | |
| } | |
| ::-webkit-scrollbar-thumb:hover { | |
| background: rgba(102, 126, 234, 0.7); | |
| } | |
| """ | |
| # Build the Gradio app | |
| with gr.Blocks( | |
| title="AI Assistant - HuggingFace", | |
| theme=gr.themes.Soft( | |
| primary_hue="purple", | |
| secondary_hue="blue", | |
| neutral_hue="slate", | |
| font=["Inter", "system-ui", "sans-serif"] | |
| ), | |
| css=custom_css | |
| ) as app: | |
| # Header with modern design | |
| gr.HTML(""" | |
| <div class="main-header"> | |
| <div style="display: flex; align-items: center; justify-content: space-between;"> | |
| <div> | |
| <h1 style="display: flex; align-items: center; gap: 1rem;"> | |
| <span style="font-size: 2.5rem;">🤖</span> | |
| AI Assistant | |
| </h1> | |
| <p style="margin-top: 0.5rem; opacity: 0.9;">Powered by HuggingFace Hub • Advanced AI Models</p> | |
| </div> | |
| <div style="display: flex; gap: 1rem; align-items: center;"> | |
| <div style="background: rgba(255,255,255,0.1); padding: 0.5rem 1rem; border-radius: 20px; font-size: 0.9rem;"> | |
| <span style="color: #4ade80;">●</span> System Online | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| """) | |
| with gr.Tabs() as tabs: | |
| # ============ Chat Tab ============ | |
| with gr.Tab("💬 Chat", id="chat"): | |
| with gr.Row(equal_height=True): | |
| with gr.Column(scale=3): | |
| # Chat header | |
| gr.HTML(""" | |
| <div style="background: linear-gradient(135deg, rgba(102, 126, 234, 0.1), rgba(118, 75, 162, 0.1)); | |
| padding: 1rem; border-radius: 12px; margin-bottom: 1rem; | |
| border: 1px solid rgba(255, 255, 255, 0.1);"> | |
| <h3 style="margin: 0; color: white; display: flex; align-items: center; gap: 0.5rem;"> | |
| <span>💬</span> Conversation | |
| </h3> | |
| <p style="margin: 0.5rem 0 0 0; opacity: 0.8; font-size: 0.9rem;"> | |
| Interactive chat with AI models | |
| </p> | |
| </div> | |
| """) | |
| chatbot = gr.Chatbot( | |
| label="", | |
| height=500, | |
| placeholder="Start a conversation... Type your message below", | |
| value=[], | |
| show_label=False, | |
| container=True, | |
| elem_classes=["chatbot-container"] | |
| ) | |
| with gr.Row(): | |
| with gr.Column(scale=4): | |
| chat_input = gr.Textbox( | |
| placeholder="Type your message here... (Press Enter to send)", | |
| label="", | |
| show_label=False, | |
| lines=1, | |
| max_lines=5, | |
| elem_classes=["chat-input"] | |
| ) | |
| with gr.Column(scale=1, min_width=100): | |
| send_btn = gr.Button( | |
| "Send", | |
| variant="primary", | |
| size="lg", | |
| elem_classes=["send-button"] | |
| ) | |
| with gr.Row(): | |
| clear_btn = gr.Button( | |
| "🗑️ Clear Chat", | |
| size="sm", | |
| variant="secondary", | |
| elem_classes=["action-button"] | |
| ) | |
| with gr.Column(scale=1): | |
| gr.HTML(""" | |
| <div style="text-align: right; opacity: 0.6; font-size: 0.8rem; margin-top: 0.5rem;"> | |
| Tip: Press Shift+Enter for new line | |
| </div> | |
| """) | |
| with gr.Column(scale=1, min_width=300): | |
| # Settings panel | |
| gr.HTML(""" | |
| <div style="background: rgba(255, 255, 255, 0.05); | |
| padding: 1rem; border-radius: 12px; margin-bottom: 1rem; | |
| border: 1px solid rgba(255, 255, 255, 0.1);"> | |
| <h3 style="margin: 0; color: white; display: flex; align-items: center; gap: 0.5rem;"> | |
| <span>⚙️</span> Chat Settings | |
| </h3> | |
| </div> | |
| """) | |
| with gr.Group(elem_classes=["settings-group"]): | |
| chat_model = gr.Dropdown( | |
| choices=get_model_choices(), | |
| value=get_model_choices()[0] if get_model_choices() else None, | |
| label="🤖 Model", | |
| interactive=True, | |
| info="Select AI model for conversation" | |
| ) | |
| chat_temp = gr.Slider( | |
| minimum=0.1, | |
| maximum=1.0, | |
| value=0.7, | |
| step=0.1, | |
| label="🌡️ Temperature", | |
| info="Controls randomness (0.1 = focused, 1.0 = creative)" | |
| ) | |
| chat_max_tokens = gr.Slider( | |
| minimum=50, | |
| maximum=4096, | |
| value=500, | |
| step=50, | |
| label="📝 Max Tokens", | |
| info="Maximum response length" | |
| ) | |
| chat_system = gr.Textbox( | |
| label="🎯 System Prompt", | |
| placeholder="You are a helpful assistant...", | |
| lines=3, | |
| info="Define AI behavior and personality" | |
| ) | |
| refresh_models_btn = gr.Button( | |
| "🔄 Refresh Models", | |
| size="sm", | |
| variant="secondary", | |
| elem_classes=["refresh-button"] | |
| ) | |
| def respond(message, history, model, temp, max_tok, system): | |
| if not message.strip(): | |
| return history, "" | |
| response = chat_response(message, history, model, temp, max_tok, system) | |
| # Use tuple format for default chatbot | |
| history.append((message, response)) | |
| return history, "" | |
| send_btn.click( | |
| respond, | |
| inputs=[chat_input, chatbot, chat_model, chat_temp, chat_max_tokens, chat_system], | |
| outputs=[chatbot, chat_input] | |
| ) | |
| chat_input.submit( | |
| respond, | |
| inputs=[chat_input, chatbot, chat_model, chat_temp, chat_max_tokens, chat_system], | |
| outputs=[chatbot, chat_input] | |
| ) | |
| clear_btn.click(lambda: [], outputs=[chatbot]) | |
| refresh_models_btn.click( | |
| lambda: gr.update(choices=get_model_choices()), | |
| outputs=[chat_model] | |
| ) | |
| # ============ Text Generation Tab ============ | |
| with gr.Tab("📝 Text Generation", id="text-gen"): | |
| gr.HTML(""" | |
| <div style="background: linear-gradient(135deg, rgba(102, 126, 234, 0.1), rgba(118, 75, 162, 0.1)); | |
| padding: 1.5rem; border-radius: 12px; margin-bottom: 1.5rem; | |
| border: 1px solid rgba(255, 255, 255, 0.1);"> | |
| <h3 style="margin: 0; color: white; display: flex; align-items: center; gap: 0.5rem;"> | |
| <span>📝</span> Text Generation | |
| </h3> | |
| <p style="margin: 0.5rem 0 0 0; opacity: 0.8;"> | |
| Generate creative text, stories, articles, and more with AI | |
| </p> | |
| </div> | |
| """) | |
| with gr.Row(equal_height=True): | |
| with gr.Column(scale=1): | |
| with gr.Group(elem_classes=["input-group"]): | |
| gen_prompt = gr.Textbox( | |
| label="✍️ Prompt", | |
| placeholder="Enter your prompt for text generation... Be creative and specific!", | |
| lines=6, | |
| info="The more detailed your prompt, the better the result" | |
| ) | |
| with gr.Row(): | |
| gen_model = gr.Dropdown( | |
| choices=get_model_choices(), | |
| value=get_model_choices()[0] if get_model_choices() else None, | |
| label="🤖 Model", | |
| info="Choose model for generation" | |
| ) | |
| gen_temp = gr.Slider( | |
| 0.1, 1.0, 0.7, step=0.1, | |
| label="🌡️ Temperature", | |
| info="Creativity level" | |
| ) | |
| with gr.Row(): | |
| gen_max_tokens = gr.Slider( | |
| 50, 2048, 250, step=50, | |
| label="📊 Max Tokens", | |
| info="Response length" | |
| ) | |
| gen_top_p = gr.Slider( | |
| 0.1, 1.0, 0.95, step=0.05, | |
| label="🎯 Top P", | |
| info="Nucleus sampling" | |
| ) | |
| gen_btn = gr.Button( | |
| "✨ Generate", | |
| variant="primary", | |
| size="lg", | |
| elem_classes=["generate-button"] | |
| ) | |
| with gr.Column(scale=1): | |
| with gr.Group(elem_classes=["output-group"]): | |
| gen_output = gr.Textbox( | |
| label="📄 Generated Text", | |
| lines=18, | |
| show_label=True, | |
| info="AI-generated content will appear here", | |
| elem_classes=["output-textarea"] | |
| ) | |
| with gr.Row(): | |
| copy_gen_btn = gr.Button( | |
| "📋 Copy", | |
| size="sm", | |
| variant="secondary", | |
| elem_classes=["copy-button"] | |
| ) | |
| regenerate_btn = gr.Button( | |
| "🔄 Regenerate", | |
| size="sm", | |
| variant="secondary" | |
| ) | |
| gen_btn.click( | |
| text_generation, | |
| inputs=[gen_prompt, gen_model, gen_temp, gen_max_tokens, gen_top_p], | |
| outputs=[gen_output] | |
| ) | |
| copy_gen_btn.click( | |
| fn=None, | |
| js="(text) => { navigator.clipboard.writeText(text); alert('Text copied to clipboard!'); }", | |
| inputs=[gen_output] | |
| ) | |
| # ============ Summarization Tab ============ | |
| with gr.Tab("📋 Summarization", id="summarize"): | |
| with gr.Row(): | |
| with gr.Column(): | |
| sum_text = gr.Textbox( | |
| label="Text to Summarize", | |
| placeholder="Paste the text you want to summarize...", | |
| lines=10 | |
| ) | |
| sum_model = gr.Dropdown( | |
| choices=["facebook/bart-large-cnn", "sshleifer/distilbart-cnn-12-6", "google/pegasus-xsum"], | |
| value="facebook/bart-large-cnn", | |
| label="Model" | |
| ) | |
| with gr.Row(): | |
| sum_max_len = gr.Slider(50, 500, 150, step=10, label="Max Length") | |
| sum_min_len = gr.Slider(10, 100, 30, step=5, label="Min Length") | |
| sum_btn = gr.Button("Summarize", variant="primary") | |
| with gr.Column(): | |
| sum_output = gr.Textbox( | |
| label="Summary", | |
| lines=8 | |
| ) | |
| sum_btn.click( | |
| summarize_text, | |
| inputs=[sum_text, sum_model, sum_max_len, sum_min_len], | |
| outputs=[sum_output] | |
| ) | |
| # ============ Translation Tab ============ | |
| with gr.Tab("🌐 Translation", id="translate"): | |
| gr.HTML(""" | |
| <div style="background: linear-gradient(135deg, rgba(102, 126, 234, 0.1), rgba(118, 75, 162, 0.1)); | |
| padding: 1.5rem; border-radius: 12px; margin-bottom: 1.5rem; | |
| border: 1px solid rgba(255, 255, 255, 0.1);"> | |
| <h3 style="margin: 0; color: white; display: flex; align-items: center; gap: 0.5rem;"> | |
| <span>🌐</span> Translation | |
| </h3> | |
| <p style="margin: 0.5rem 0 0 0; opacity: 0.8;"> | |
| Translate text between multiple languages with advanced AI models | |
| </p> | |
| </div> | |
| """) | |
| with gr.Row(equal_height=True): | |
| with gr.Column(scale=1): | |
| with gr.Group(elem_classes=["translation-input"]): | |
| gr.HTML(""" | |
| <div style="background: rgba(255, 255, 255, 0.05); padding: 1rem; border-radius: 8px; margin-bottom: 1rem;"> | |
| <h4 style="margin: 0; color: white; font-size: 0.9rem;"> | |
| 🌟 Translation Models | |
| </h4> | |
| <ul style="margin: 0.5rem 0 0 0; padding-left: 1.5rem; color: rgba(255,255,255,0.8); font-size: 0.85rem;"> | |
| <li><strong>Google TranslateGemma</strong> - Advanced multilingual translation</li> | |
| <li><strong>Llama 3.2</strong> - Multilingual with dynamic prompts</li> | |
| <li><strong>MADLAD-400</strong> - 400+ languages support</li> | |
| <li><strong>Helsinki-NLP</strong> - Specialized language pairs</li> | |
| </ul> | |
| <p style="margin: 0.5rem 0 0 0; padding: 0.5rem; background: rgba(102, 126, 234, 0.1); border-radius: 4px; font-size: 0.8rem;"> | |
| <strong>✨ New:</strong> Target language selection now works with all models! | |
| </p> | |
| <p style="margin: 0.5rem 0 0 0; padding: 0.5rem; background: rgba(255, 255, 0, 0.1); border-radius: 4px; font-size: 0.8rem;"> | |
| <strong>Note:</strong> If a model is not available, the system will automatically try fallback models. | |
| </p> | |
| </div> | |
| """) | |
| trans_text = gr.Textbox( | |
| label="📝 Text to Translate", | |
| placeholder="Enter text to translate...", | |
| lines=6, | |
| info="Supports multiple languages and formats" | |
| ) | |
| trans_model = gr.Dropdown( | |
| choices=[ | |
| "google/translategemma-12b-it", | |
| "meta-llama/Llama-3.2-3B-Instruct", | |
| "google/madlad400-3b-mt", | |
| "Helsinki-NLP/opus-mt-en-de", | |
| "Helsinki-NLP/opus-mt-en-fr", | |
| "Helsinki-NLP/opus-mt-en-es", | |
| "Helsinki-NLP/opus-mt-en-zh", | |
| "Helsinki-NLP/opus-mt-en-ja", | |
| "Helsinki-NLP/opus-mt-de-en", | |
| "Helsinki-NLP/opus-mt-fr-en", | |
| "Helsinki-NLP/opus-mt-es-en" | |
| ], | |
| value="google/translategemma-12b-it", | |
| label="🤖 Translation Model", | |
| info="Choose model based on your language needs" | |
| ) | |
| # Target language selection (works with all models) | |
| target_language = gr.Dropdown( | |
| choices=[ | |
| "English", "Spanish", "French", "German", "Chinese (Simplified)", "Chinese (Traditional)", | |
| "Japanese", "Korean", "Italian", "Portuguese", "Russian", "Arabic", "Hindi", | |
| "Dutch", "Turkish", "Polish", "Vietnamese", "Thai", "Indonesian", | |
| "Greek", "Hebrew", "Czech", "Swedish", "Danish", "Norwegian", "Finnish" | |
| ], | |
| value="English", | |
| label="🎯 Target Language", | |
| info="Select target language for translation (works with all models)", | |
| visible=True | |
| ) | |
| # Source language selection (optional) | |
| source_language = gr.Dropdown( | |
| choices=[ | |
| "Auto-detect", "English", "Spanish", "French", "German", "Chinese (Simplified)", "Chinese (Traditional)", | |
| "Japanese", "Korean", "Italian", "Portuguese", "Russian", "Arabic", "Hindi", | |
| "Dutch", "Turkish", "Polish", "Vietnamese", "Thai", "Indonesian" | |
| ], | |
| value="Auto-detect", | |
| label="🔤 Source Language", | |
| info="Select source language or leave as Auto-detect", | |
| visible=True | |
| ) | |
| trans_btn = gr.Button( | |
| "🌐 Translate", | |
| variant="primary", | |
| size="lg", | |
| elem_classes=["translate-button"] | |
| ) | |
| # Image translation section | |
| gr.HTML(""" | |
| <div style="background: rgba(255, 255, 255, 0.05); padding: 1rem; border-radius: 8px; margin: 1rem 0;"> | |
| <h4 style="margin: 0; color: white; font-size: 0.9rem;"> | |
| 🖼️ Image Translation (Google TranslateGemma only) | |
| </h4> | |
| <p style="margin: 0.5rem 0 0 0; color: rgba(255,255,255,0.8); font-size: 0.85rem;"> | |
| Upload an image containing text to extract and translate it | |
| </p> | |
| </div> | |
| """) | |
| trans_image = gr.Image( | |
| label="🖼️ Upload Image", | |
| type="filepath", | |
| visible=True # Initially visible since default model is TranslateGemma | |
| ) | |
| trans_image_btn = gr.Button( | |
| "🖼️ Translate Image", | |
| variant="secondary", | |
| size="lg", | |
| elem_classes=["translate-button"], | |
| visible=True # Initially visible since default model is TranslateGemma | |
| ) | |
| with gr.Column(scale=1): | |
| with gr.Group(elem_classes=["translation-output"]): | |
| trans_output = gr.Textbox( | |
| label="✅ Translation Result", | |
| lines=6, | |
| info="Translated text will appear here", | |
| elem_classes=["translation-result"] | |
| ) | |
| with gr.Row(): | |
| copy_trans_btn = gr.Button( | |
| "📋 Copy", | |
| size="sm", | |
| variant="secondary" | |
| ) | |
| swap_lang_btn = gr.Button( | |
| "🔄 Swap Languages", | |
| size="sm", | |
| variant="secondary" | |
| ) | |
| # Function to show/hide target language dropdown based on model selection | |
| def update_target_language_visibility(model): | |
| show = "translategemma" in model.lower() | |
| return gr.update(visible=show) | |
| # Function to show/hide image elements based on model selection | |
| def update_image_visibility(model): | |
| show = "translategemma" in model.lower() | |
| return gr.update(visible=show) | |
| # Update visibility when model changes | |
| trans_model.change( | |
| update_target_language_visibility, | |
| inputs=[trans_model], | |
| outputs=[target_language] | |
| ) | |
| trans_model.change( | |
| update_image_visibility, | |
| inputs=[trans_model], | |
| outputs=[trans_image] | |
| ) | |
| trans_model.change( | |
| update_image_visibility, | |
| inputs=[trans_model], | |
| outputs=[trans_image_btn] | |
| ) | |
| trans_btn.click( | |
| translate_text, | |
| inputs=[trans_text, trans_model, target_language, source_language], | |
| outputs=[trans_output] | |
| ) | |
| trans_image_btn.click( | |
| translate_image, | |
| inputs=[trans_image, trans_model, target_language, source_language], | |
| outputs=[trans_output] | |
| ) | |
| # Swap languages functionality | |
| def swap_languages(src_lang, tgt_lang, text): | |
| # Swap source and target languages | |
| # For simplicity, we'll just swap the dropdown values | |
| # In a more complex implementation, you might want to translate the text as well | |
| return tgt_lang, src_lang, "" | |
| swap_lang_btn.click( | |
| swap_languages, | |
| inputs=[source_language, target_language, trans_text], | |
| outputs=[source_language, target_language, trans_text] | |
| ) | |
| # Copy translation functionality | |
| def copy_translation(text): | |
| # This will be handled by JavaScript in the frontend | |
| return text | |
| copy_trans_btn.click( | |
| copy_translation, | |
| inputs=[trans_output], | |
| js="(text) => { navigator.clipboard.writeText(text); alert('Translation copied to clipboard!'); }" | |
| ) | |
| # ============ Translation Testing Tab ============ | |
| with gr.Tab("🧪 Translation Testing", id="translation-testing"): | |
| gr.HTML(""" | |
| <div style="background: linear-gradient(135deg, rgba(102, 126, 234, 0.1), rgba(118, 75, 162, 0.1)); | |
| padding: 1.5rem; border-radius: 12px; margin-bottom: 1.5rem; | |
| border: 1px solid rgba(255, 255, 255, 0.1);"> | |
| <h3 style="margin: 0; color: white; display: flex; align-items: center; gap: 0.5rem;"> | |
| <span>🧪</span> Translation Testing | |
| </h3> | |
| <p style="margin: 0.5rem 0 0 0; opacity: 0.8;"> | |
| Test and validate translation functionality with comprehensive test scenarios | |
| </p> | |
| </div> | |
| """) | |
| with gr.Row(equal_height=True): | |
| with gr.Column(scale=1): | |
| with gr.Group(elem_classes=["test-input"]): | |
| gr.HTML(""" | |
| <div style="background: rgba(255, 255, 255, 0.05); padding: 1rem; border-radius: 8px; margin-bottom: 1rem;"> | |
| <h4 style="margin: 0; color: white; font-size: 0.9rem;"> | |
| 🧪 Test Options | |
| </h4> | |
| <p style="margin: 0.5rem 0 0 0; color: rgba(255,255,255,0.8); font-size: 0.85rem;"> | |
| Choose between single translation test or comprehensive test suite | |
| </p> | |
| </div> | |
| """) | |
| # Single test inputs | |
| test_text = gr.Textbox( | |
| label="📝 Test Text", | |
| placeholder="Enter text to test translation...", | |
| lines=3, | |
| info="Text to translate for testing" | |
| ) | |
| test_source_lang = gr.Dropdown( | |
| choices=[ | |
| "en", "es", "fr", "de-DE", "zh-CN", "zh-TW", "ja", "ko", "it", "pt", "ru", "ar", "hi", | |
| "nl", "tr", "pl", "vi", "th", "id", "el", "he", "cs", "sv", "da", "no", "fi" | |
| ], | |
| value="en", | |
| label="🔤 Source Language Code", | |
| info="ISO language code (e.g., 'en' for English)" | |
| ) | |
| test_target_lang = gr.Dropdown( | |
| choices=[ | |
| "en", "es", "fr", "de-DE", "zh-CN", "zh-TW", "ja", "ko", "it", "pt", "ru", "ar", "hi", | |
| "nl", "tr", "pl", "vi", "th", "id", "el", "he", "cs", "sv", "da", "no", "fi" | |
| ], | |
| value="es", | |
| label="🎯 Target Language Code", | |
| info="ISO language code (e.g., 'es' for Spanish)" | |
| ) | |
| with gr.Row(): | |
| single_test_btn = gr.Button( | |
| "🧪 Run Single Test", | |
| variant="primary", | |
| size="lg" | |
| ) | |
| comprehensive_test_btn = gr.Button( | |
| "📊 Run Comprehensive Tests", | |
| variant="secondary", | |
| size="lg" | |
| ) | |
| with gr.Column(scale=1): | |
| with gr.Group(elem_classes=["test-output"]): | |
| test_output = gr.Textbox( | |
| label="📋 Test Results", | |
| lines=15, | |
| info="Test results and diagnostics will appear here", | |
| elem_classes=["test-result"] | |
| ) | |
| test_summary = gr.Textbox( | |
| label="📊 Test Summary", | |
| lines=5, | |
| info="Summary of test results", | |
| elem_classes=["test-summary"] | |
| ) | |
| copy_test_btn = gr.Button( | |
| "📋 Copy Results", | |
| size="sm", | |
| variant="secondary" | |
| ) | |
| # Test functions | |
| def run_single_test(text, source_lang, target_lang): | |
| """Run a single translation test""" | |
| if not text.strip(): | |
| return "❌ Please enter text to test", "" | |
| result = test_translategemma(text, source_lang, target_lang) | |
| if result: | |
| return f""" | |
| 🧪 Single Translation Test Results | |
| {"=" * 50} | |
| ✅ Test completed successfully! | |
| 📝 Input: | |
| Text: {text} | |
| Source: {source_lang} | |
| Target: {target_lang} | |
| 🌐 Translation: | |
| {result} | |
| ✅ Status: PASSED | |
| 🔧 Method: {'Google Translate Gemma' if GOOGLE_TRANSLATE_AVAILABLE else 'Chat Completion Fallback'} | |
| """, "✅ Single test completed successfully" | |
| else: | |
| return f""" | |
| 🧪 Single Translation Test Results | |
| {"=" * 50} | |
| ❌ Test failed! | |
| 📝 Input: | |
| Text: {text} | |
| Source: {source_lang} | |
| Target: {target_lang} | |
| ❌ Status: FAILED | |
| 🔧 Method: {'Google Translate Gemma' if GOOGLE_TRANSLATE_AVAILABLE else 'Chat Completion Fallback'} | |
| ⚠️ Please check your HuggingFace token and model availability. | |
| """, "❌ Single test failed" | |
| def run_comprehensive_tests(): | |
| """Run comprehensive translation tests""" | |
| results, summary = run_multiple_translation_tests() | |
| # Format results for display | |
| output = "🧪 Comprehensive Translation Test Results\n" + "=" * 60 + "\n\n" | |
| for i, result in enumerate(results, 1): | |
| status = "✅ PASSED" if result['success'] else "❌ FAILED" | |
| output += f"📝 Test {i}: {result['case']}\n" | |
| output += f" Status: {status}\n" | |
| output += f" Original: {result['original']}\n" | |
| if result['translation']: | |
| output += f" Translation: {result['translation']}\n" | |
| output += "\n" | |
| return output, summary | |
| # Button click handlers | |
| single_test_btn.click( | |
| run_single_test, | |
| inputs=[test_text, test_source_lang, test_target_lang], | |
| outputs=[test_output, test_summary] | |
| ) | |
| comprehensive_test_btn.click( | |
| run_comprehensive_tests, | |
| inputs=[], | |
| outputs=[test_output, test_summary] | |
| ) | |
| # Copy test results functionality | |
| def copy_test_results(results, summary): | |
| # This will be handled by JavaScript in the frontend | |
| return results + "\n\n" + summary | |
| copy_test_btn.click( | |
| copy_test_results, | |
| inputs=[test_output, test_summary], | |
| js="(results, summary) => { const text = results + '\\n\\n' + summary; navigator.clipboard.writeText(text); alert('Test results copied to clipboard!'); }" | |
| ) | |
| # ============ Question Answering Tab ============ | |
| with gr.Tab("❓ Q&A", id="qa"): | |
| with gr.Row(): | |
| with gr.Column(): | |
| qa_context = gr.Textbox( | |
| label="Context", | |
| placeholder="Paste the context/document here...", | |
| lines=8 | |
| ) | |
| qa_question = gr.Textbox( | |
| label="Question", | |
| placeholder="What would you like to know?" | |
| ) | |
| qa_model = gr.Dropdown( | |
| choices=[ | |
| "deepset/roberta-base-squad2", | |
| "distilbert-base-cased-distilled-squad", | |
| "bert-large-uncased-whole-word-masking-finetuned-squad" | |
| ], | |
| value="deepset/roberta-base-squad2", | |
| label="Model" | |
| ) | |
| qa_btn = gr.Button("Get Answer", variant="primary") | |
| with gr.Column(): | |
| qa_answer = gr.Textbox(label="Answer", lines=3) | |
| qa_score = gr.Number(label="Confidence Score") | |
| qa_btn.click( | |
| answer_question, | |
| inputs=[qa_question, qa_context, qa_model], | |
| outputs=[qa_answer, qa_score] | |
| ) | |
| # ============ Image Generation Tab ============ | |
| with gr.Tab("🎨 Image Generation", id="image-gen"): | |
| gr.HTML(""" | |
| <div style="background: linear-gradient(135deg, rgba(102, 126, 234, 0.1), rgba(118, 75, 162, 0.1)); | |
| padding: 1.5rem; border-radius: 12px; margin-bottom: 1.5rem; | |
| border: 1px solid rgba(255, 255, 255, 0.1);"> | |
| <h3 style="margin: 0; color: white; display: flex; align-items: center; gap: 0.5rem;"> | |
| <span>🎨</span> Image Generation | |
| </h3> | |
| <p style="margin: 0.5rem 0 0 0; opacity: 0.8;"> | |
| Create stunning images from text descriptions using advanced AI models | |
| </p> | |
| </div> | |
| """) | |
| with gr.Row(equal_height=True): | |
| with gr.Column(scale=1): | |
| with gr.Group(elem_classes=["image-controls"]): | |
| img_prompt = gr.Textbox( | |
| label="✨ Prompt", | |
| placeholder="Describe the image you want to generate... Be detailed and creative!", | |
| lines=4, | |
| info="Example: 'A beautiful sunset over mountains, digital art, highly detailed'" | |
| ) | |
| img_negative = gr.Textbox( | |
| label="🚫 Negative Prompt", | |
| placeholder="What to avoid in the image... (optional)", | |
| lines=2, | |
| info="Example: 'blurry, low quality, distorted'" | |
| ) | |
| with gr.Row(): | |
| img_model = gr.Dropdown( | |
| choices=[ | |
| "stabilityai/stable-diffusion-xl-base-1.0", | |
| "runwayml/stable-diffusion-v1-5", | |
| "CompVis/stable-diffusion-v1-4" | |
| ], | |
| value="stabilityai/stable-diffusion-xl-base-1.0", | |
| label="🎭 Model", | |
| info="Choose image generation model" | |
| ) | |
| img_steps = gr.Slider( | |
| 10, 100, 30, step=5, | |
| label="⚙️ Steps", | |
| info="More steps = better quality (slower)" | |
| ) | |
| img_btn = gr.Button( | |
| "🎨 Generate Image", | |
| variant="primary", | |
| size="lg", | |
| elem_classes=["generate-image-button"] | |
| ) | |
| gr.HTML(""" | |
| <div style="background: rgba(255, 255, 255, 0.05); padding: 1rem; border-radius: 8px; margin-top: 1rem;"> | |
| <h4 style="margin: 0; color: white; font-size: 0.9rem;"> | |
| 💡 Pro Tips | |
| </h4> | |
| <ul style="margin: 0.5rem 0 0 0; padding-left: 1.5rem; color: rgba(255,255,255,0.8); font-size: 0.85rem;"> | |
| <li>Be specific in your descriptions</li> | |
| <li>Use art styles: 'digital art', 'oil painting', 'photo'</li> | |
| <li>Add quality terms: 'highly detailed', '4K', 'sharp focus'</li> | |
| </ul> | |
| </div> | |
| """) | |
| with gr.Column(scale=1): | |
| with gr.Group(elem_classes=["image-output"]): | |
| img_output = gr.Image( | |
| label="🖼️ Generated Image", | |
| type="filepath", | |
| elem_classes=["generated-image"] | |
| ) | |
| with gr.Row(): | |
| download_btn = gr.Button( | |
| "💾 Download", | |
| size="sm", | |
| variant="secondary" | |
| ) | |
| share_img_btn = gr.Button( | |
| "🔗 Share", | |
| size="sm", | |
| variant="secondary" | |
| ) | |
| img_btn.click( | |
| generate_image, | |
| inputs=[img_prompt, img_model, img_negative, img_steps], | |
| outputs=[img_output] | |
| ) | |
| # ============ Models Tab ============ | |
| with gr.Tab("🧠 Models", id="models"): | |
| with gr.Row(): | |
| # Left: Search & Browse | |
| with gr.Column(scale=2): | |
| gr.Markdown("### 🔍 Search HuggingFace Models") | |
| with gr.Row(): | |
| search_query = gr.Textbox( | |
| placeholder="Search for models...", | |
| show_label=False, | |
| scale=3 | |
| ) | |
| search_task = gr.Dropdown( | |
| choices=[ | |
| "text-generation", | |
| "text-classification", | |
| "summarization", | |
| "translation", | |
| "question-answering", | |
| "image-classification", | |
| "text-to-image" | |
| ], | |
| value="text-generation", | |
| show_label=False, | |
| scale=2 | |
| ) | |
| search_limit = gr.Slider(5, 50, 10, step=5, label="Limit", scale=1) | |
| search_btn = gr.Button("Search", variant="primary") | |
| search_results = gr.Dataframe( | |
| headers=["Model ID", "Author", "Task", "Downloads", "Likes"], | |
| label="Search Results", | |
| interactive=False, | |
| wrap=True | |
| ) | |
| gr.Markdown("### 📄 Model Info") | |
| model_info_input = gr.Textbox( | |
| placeholder="Enter model ID to get info...", | |
| label="Model ID" | |
| ) | |
| get_info_btn = gr.Button("Get Info") | |
| model_info_output = gr.Markdown(label="Model Information") | |
| # Right: My Models | |
| with gr.Column(scale=2): | |
| gr.Markdown("### 📦 My Models") | |
| my_models_table = gr.Dataframe( | |
| headers=["ID", "Name", "Model ID", "Role", "Temp", "Max Tokens", "Enabled"], | |
| value=get_models_table(), | |
| label="Saved Models", | |
| interactive=False | |
| ) | |
| refresh_table_btn = gr.Button("🔄 Refresh") | |
| gr.Markdown("### ➕ Add New Model") | |
| add_model_id = gr.Textbox(label="Model ID", placeholder="e.g., meta-llama/Llama-3.2-3B-Instruct") | |
| add_model_name = gr.Textbox(label="Display Name", placeholder="My Model") | |
| add_model_role = gr.Dropdown( | |
| choices=["assistant", "creative", "coder", "analyst", "custom"], | |
| value="assistant", | |
| label="Role" | |
| ) | |
| with gr.Row(): | |
| add_model_temp = gr.Slider(0.1, 1.0, 0.3, step=0.1, label="Temperature") | |
| add_model_tokens = gr.Slider(50, 4096, 500, step=50, label="Max Tokens") | |
| add_model_system = gr.Textbox( | |
| label="System Prompt", | |
| placeholder="Optional system prompt...", | |
| lines=2 | |
| ) | |
| add_model_btn = gr.Button("Add Model", variant="primary") | |
| add_model_status = gr.Textbox(label="Status", interactive=False) | |
| gr.Markdown("### 🗑️ Delete Model") | |
| delete_model_id = gr.Textbox(label="Model ID to Delete", placeholder="Enter model ID") | |
| delete_model_btn = gr.Button("Delete", variant="stop") | |
| search_btn.click( | |
| search_hf_models, | |
| inputs=[search_query, search_task, search_limit], | |
| outputs=[search_results] | |
| ) | |
| get_info_btn.click( | |
| get_model_info, | |
| inputs=[model_info_input], | |
| outputs=[model_info_output] | |
| ) | |
| add_model_btn.click( | |
| add_model_to_settings, | |
| inputs=[add_model_id, add_model_name, add_model_role, add_model_temp, add_model_tokens, add_model_system], | |
| outputs=[add_model_status, my_models_table] | |
| ) | |
| refresh_table_btn.click( | |
| get_models_table, | |
| outputs=[my_models_table] | |
| ) | |
| delete_model_btn.click( | |
| delete_model, | |
| inputs=[delete_model_id], | |
| outputs=[add_model_status, my_models_table] | |
| ) | |
| # ============ Settings Tab ============ | |
| with gr.Tab("⚙️ Settings", id="settings"): | |
| with gr.Row(): | |
| with gr.Column(): | |
| gr.Markdown("### 🔑 HuggingFace API Token") | |
| token_input = gr.Textbox( | |
| label="API Token", | |
| value=get_hf_token(), | |
| type="password", | |
| placeholder="hf_..." | |
| ) | |
| save_token_btn = gr.Button("Save Token", variant="primary") | |
| token_status = gr.Textbox(label="Status", interactive=False) | |
| gr.Markdown(""" | |
| --- | |
| **How to get your token:** | |
| 1. Go to [HuggingFace Settings](https://huggingface.co/settings/tokens) | |
| 2. Create a new token with read access | |
| 3. Paste it above and save | |
| """) | |
| with gr.Column(): | |
| gr.Markdown("### 👤 Account Info") | |
| account_info = gr.Markdown(value="Click refresh to load account info") | |
| refresh_account_btn = gr.Button("🔄 Refresh Account Info") | |
| save_token_btn.click( | |
| save_hf_token, | |
| inputs=[token_input], | |
| outputs=[token_status] | |
| ) | |
| refresh_account_btn.click( | |
| get_account_info, | |
| outputs=[account_info] | |
| ) | |
| # Footer with modern design | |
| gr.HTML(""" | |
| <footer style="background: rgba(255, 255, 255, 0.03); | |
| border-top: 1px solid rgba(255, 255, 255, 0.1); | |
| padding: 2rem; | |
| text-align: center; | |
| color: rgba(255, 255, 255, 0.6); | |
| margin-top: 3rem;"> | |
| <div style="display: flex; justify-content: center; align-items: center; gap: 2rem; flex-wrap: wrap;"> | |
| <div style="display: flex; align-items: center; gap: 0.5rem;"> | |
| <span style="font-size: 1.5rem;">🤖</span> | |
| <span>AI Assistant © 2026</span> | |
| </div> | |
| <div style="display: flex; align-items: center; gap: 0.5rem;"> | |
| <span style="font-size: 1.2rem;">⚡</span> | |
| <span>Powered by HuggingFace Hub</span> | |
| </div> | |
| <div style="display: flex; align-items: center; gap: 0.5rem;"> | |
| <span style="font-size: 1.2rem;">🎨</span> | |
| <span>Built with Gradio</span> | |
| </div> | |
| </div> | |
| <div style="margin-top: 1rem; font-size: 0.85rem; opacity: 0.7;"> | |
| Advanced AI Models • Professional Interface • Seamless Experience | |
| </div> | |
| </footer> | |
| """) | |
| # Add custom JavaScript for enhanced interactions | |
| app.load( | |
| fn=None, | |
| js=""" | |
| function() { | |
| // Add smooth scrolling | |
| document.querySelectorAll('a[href^="#"]').forEach(anchor => { | |
| anchor.addEventListener('click', function (e) { | |
| e.preventDefault(); | |
| document.querySelector(this.getAttribute('href')).scrollIntoView({ | |
| behavior: 'smooth' | |
| }); | |
| }); | |
| }); | |
| // Add auto-resizing for chat input | |
| const chatInput = document.querySelector('.chat-input textarea'); | |
| if (chatInput) { | |
| chatInput.addEventListener('input', function() { | |
| this.style.height = 'auto'; | |
| this.style.height = Math.min(this.scrollHeight, 200) + 'px'; | |
| }); | |
| // Add shift+enter for new line | |
| chatInput.addEventListener('keydown', function(e) { | |
| if (e.key === 'Enter' && !e.shiftKey) { | |
| e.preventDefault(); | |
| const sendBtn = document.querySelector('.send-button'); | |
| if (sendBtn) sendBtn.click(); | |
| } | |
| }); | |
| } | |
| // Add hover effects to all interactive elements | |
| document.querySelectorAll('.gradio-button, .gradio-dropdown, .gradio-slider').forEach(element => { | |
| element.addEventListener('mouseenter', function() { | |
| this.style.transform = 'translateY(-2px)'; | |
| this.style.transition = 'all 0.3s ease'; | |
| }); | |
| element.addEventListener('mouseleave', function() { | |
| this.style.transform = 'translateY(0)'; | |
| }); | |
| }); | |
| // Add card hover effects | |
| document.querySelectorAll('.gradio-box, .input-group, .output-group').forEach(card => { | |
| card.addEventListener('mouseenter', function() { | |
| this.style.boxShadow = '0 8px 24px rgba(102, 126, 234, 0.3)'; | |
| this.style.transform = 'translateY(-4px)'; | |
| }); | |
| card.addEventListener('mouseleave', function() { | |
| this.style.boxShadow = ''; | |
| this.style.transform = 'translateY(0)'; | |
| }); | |
| }); | |
| // Add loading animations | |
| window.addEventListener('load', function() { | |
| document.body.style.opacity = '0'; | |
| setTimeout(() => { | |
| document.body.style.transition = 'opacity 0.5s ease-in-out'; | |
| document.body.style.opacity = '1'; | |
| }, 100); | |
| }); | |
| // Add visual feedback for button clicks | |
| document.querySelectorAll('.gradio-button').forEach(button => { | |
| button.addEventListener('click', function() { | |
| this.style.transform = 'scale(0.95)'; | |
| setTimeout(() => { | |
| this.style.transform = ''; | |
| }, 100); | |
| }); | |
| }); | |
| // Add copy functionality with visual feedback | |
| document.querySelectorAll('[id*=\"copy\"], [id*=\"Copy\"]').forEach(button => { | |
| button.addEventListener('click', function() { | |
| const originalText = this.textContent; | |
| this.textContent = '✓ Copied!'; | |
| this.style.background = 'linear-gradient(135deg, #4ade80, #22c55e)'; | |
| this.style.color = 'white'; | |
| setTimeout(() => { | |
| this.textContent = originalText; | |
| this.style.background = ''; | |
| this.style.color = ''; | |
| }, 2000); | |
| }); | |
| }); | |
| // Add keyboard shortcuts | |
| document.addEventListener('keydown', function(e) { | |
| // Ctrl/Cmd + K to focus chat input | |
| if ((e.ctrlKey || e.metaKey) && e.key === 'k') { | |
| e.preventDefault(); | |
| const chatInput = document.querySelector('.chat-input textarea'); | |
| if (chatInput) { | |
| chatInput.focus(); | |
| chatInput.scrollIntoView({ behavior: 'smooth', block: 'center' }); | |
| } | |
| } | |
| // Ctrl/Cmd + / to show shortcuts help | |
| if ((e.ctrlKey || e.metaKey) && e.key === '/') { | |
| e.preventDefault(); | |
| alert('Keyboard Shortcuts:\\n• Ctrl+K: Focus chat input\\n• Escape: Clear chat\\n• Shift+Enter: New line in chat'); | |
| } | |
| // Escape to clear chat | |
| if (e.key === 'Escape') { | |
| const clearButton = document.querySelector('[id*=\"clear\"], [id*=\"Clear\"]'); | |
| if (clearButton) clearButton.click(); | |
| } | |
| }); | |
| // Add focus effects to inputs | |
| document.querySelectorAll('input, textarea').forEach(input => { | |
| input.addEventListener('focus', function() { | |
| this.style.boxShadow = '0 0 0 3px rgba(102, 126, 234, 0.3)'; | |
| }); | |
| input.addEventListener('blur', function() { | |
| this.style.boxShadow = ''; | |
| }); | |
| }); | |
| console.log('✨ AI Assistant UI Enhanced with Interactive Features'); | |
| } | |
| """ | |
| ) | |
| # Launch the app | |
| if __name__ == "__main__": | |
| import random | |
| app.launch( | |
| server_name="0.0.0.0", | |
| server_port=random.randint(7860, 7890), | |
| share=False, | |
| show_error=True, | |
| inbrowser=True | |
| ) | |