Spaces:
Running
Running
import gradio as gr | |
import time | |
import requests | |
import io | |
from PIL import Image | |
from huggingface_hub import InferenceClient, HfApi | |
from deep_translator import GoogleTranslator | |
from indic_transliteration import sanscript | |
from indic_transliteration.detect import detect as detect_script | |
from indic_transliteration.sanscript import transliterate | |
import langdetect | |
import re | |
import os | |
# Get secrets from Hugging Face Space | |
HF_TOKEN = os.environ.get('HF_TOKEN') | |
if not HF_TOKEN: | |
raise ValueError("Please set the HF_TOKEN secret in your HuggingFace Space") | |
# Initialize clients | |
text_client = InferenceClient( | |
"HuggingFaceH4/zephyr-7b-beta", | |
token=HF_TOKEN | |
) | |
# Image generation setup | |
API_URL = "https://api-inference.huggingface.co/models/SG161222/RealVisXL_V4.0" | |
headers = {"Authorization": f"Bearer {HF_TOKEN}"} | |
def detect_language_script(text: str) -> tuple[str, str]: | |
"""Detect language and script of the input text. | |
Returns (language_code, script_type)""" | |
try: | |
# Use confidence threshold to avoid false detections | |
lang_detect = langdetect.detect_langs(text) | |
if lang_detect[0].prob > 0.8: | |
# Only accept high confidence detections | |
lang = lang_detect[0].lang | |
else: | |
lang = 'en' # Default to English if unsure | |
script = None | |
try: | |
script = detect_script(text) | |
except: | |
pass | |
return lang, script | |
except: | |
return 'en', None | |
def is_romanized_indic(text: str) -> bool: | |
"""Check if text appears to be romanized Indic language. | |
More strict pattern matching.""" | |
# Common Bengali romanized patterns with word boundaries | |
bengali_patterns = [ | |
r'\b(ami|tumi|apni)\b', # Common pronouns | |
r'\b(ache|achen|thako|thaken)\b', # Common verbs | |
r'\b(kemon|bhalo|kharap)\b', # Common adjectives | |
r'\b(ki|kothay|keno)\b' # Common question words | |
] | |
# Require multiple matches to confirm it's actually Bengali | |
text_lower = text.lower() | |
matches = sum(1 for pattern in bengali_patterns if re.search(pattern, text_lower)) | |
return matches >= 2 # Require at least 2 matches to consider it Bengali | |
def translate_text(text: str, target_lang='en') -> tuple[str, str, bool]: | |
"""Translate text to target language, with more conservative translation logic.""" | |
# Skip translation for very short inputs or basic greetings | |
if len(text.split()) <= 2 or text.lower() in ['hello', 'hi', 'hey']: | |
return text, 'en', False | |
original_lang, script = detect_language_script(text) | |
is_transliterated = False | |
# Only process if confident it's non-English | |
if original_lang != 'en' and len(text.split()) > 2: | |
try: | |
translator = GoogleTranslator(source='auto', target=target_lang) | |
translated = translator.translate(text) | |
return translated, original_lang, is_transliterated | |
except Exception as e: | |
print(f"Translation error: {e}") | |
return text, 'en', False | |
# Check for romanized Indic text only if it's a longer input | |
if original_lang == 'en' and len(text.split()) > 2 and is_romanized_indic(text): | |
text = romanized_to_bengali(text) | |
return translate_text(text, target_lang) # Recursive call with Bengali script | |
return text, 'en', False | |
def check_custom_responses(message: str) -> str: | |
"""Check for specific patterns and return custom responses.""" | |
message_lower = message.lower() | |
custom_responses = { | |
"what is ur name?": "xylaria", | |
"what is your name?": "xylaria", | |
"what's your name?": "xylaria", | |
"whats your name": "xylaria", | |
"how many 'r' is in strawberry?": "3", | |
"who is your developer?": "sk md saad amin", | |
"how many r is in strawberry": "3", | |
"who is ur dev": "sk md saad amin", | |
"who is ur developer": "sk md saad amin", | |
} | |
for pattern, response in custom_responses.items(): | |
if pattern in message_lower: | |
return response | |
return None | |
def is_image_request(message: str) -> bool: | |
"""Detect if the message is requesting image generation.""" | |
image_triggers = [ | |
"generate an image", | |
"create an image", | |
"draw", | |
"make a picture", | |
"generate a picture", | |
"create a picture", | |
"generate art", | |
"create art", | |
"make art", | |
"visualize", | |
"show me", | |
] | |
message_lower = message.lower() | |
return any(trigger in message_lower for trigger in image_triggers) | |
def generate_image(prompt): | |
"""Generate image using HuggingFace inference API""" | |
try: | |
response = requests.post(API_URL, headers=headers, json={"inputs": prompt}) | |
image = Image.open(io.BytesIO(response.content)) | |
return image | |
except Exception as e: | |
print(f"Image generation error: {e}") | |
return None | |
def romanized_to_bengali(text: str) -> str: | |
"""Convert romanized Bengali text to Bengali script.""" | |
bengali_mappings = { | |
'ami': 'আমি', | |
'tumi': 'তুমি', | |
'apni': 'আপনি', | |
'kemon': 'কেমন', | |
'achen': 'আছেন', | |
'acchen': 'আছেন', | |
'bhalo': 'ভালো', | |
'achi': 'আছি', | |
'ki': 'কি', | |
'kothay': 'কোথায়', | |
'keno': 'কেন', | |
} | |
text_lower = text.lower() | |
for roman, bengali in bengali_mappings.items(): | |
text_lower = re.sub(r'\b' + roman + r'\b', bengali, text_lower) | |
if text_lower == text.lower(): | |
try: | |
return transliterate(text, sanscript.ITRANS, sanscript.BENGALI) | |
except: | |
return text | |
return text_lower | |
def create_chat_interface(): | |
# Custom CSS for better styling with Inter font and animations | |
custom_css = """ | |
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap'); | |
* { | |
font-family: 'Inter', sans-serif !important; | |
} | |
.container { | |
max-width: 850px !important; | |
margin: auto; | |
} | |
.chat-window { | |
height: 600px !important; | |
overflow-y: auto; | |
border-radius: 15px !important; | |
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.1) !important; | |
transition: all 0.3s ease !important; | |
} | |
.chat-window:hover { | |
box-shadow: 0 12px 20px rgba(0, 0, 0, 0.15) !important; | |
} | |
.chat-message { | |
padding: 1rem !important; | |
margin: 0.5rem !important; | |
border-radius: 12px !important; | |
transition: all 0.2s ease !important; | |
opacity: 0; | |
animation: messageSlide 0.3s ease forwards; | |
} | |
@keyframes messageSlide { | |
from { | |
opacity: 0; | |
transform: translateY(10px); | |
} | |
to { | |
opacity: 1; | |
transform: translateY(0); | |
} | |
} | |
.user-message { | |
background: linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%) !important; | |
color: white !important; | |
margin-left: 2rem !important; | |
} | |
.bot-message { | |
background: linear-gradient(135deg, #f3f4f6 0%, #e5e7eb 100%) !important; | |
margin-right: 2rem !important; | |
} | |
/* Button Styles */ | |
button.primary { | |
background: linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%) !important; | |
border: none !important; | |
color: white !important; | |
padding: 0.75rem 1.5rem !important; | |
border-radius: 12px !important; | |
font-weight: 600 !important; | |
transition: all 0.3s ease !important; | |
transform: translateY(0); | |
box-shadow: 0 4px 6px rgba(99, 102, 241, 0.2) !important; | |
} | |
button.primary:hover { | |
transform: translateY(-2px); | |
box-shadow: 0 8px 12px rgba(99, 102, 241, 0.3) !important; | |
} | |
button.primary:active { | |
transform: translateY(0); | |
} | |
button.secondary { | |
background: #f3f4f6 !important; | |
border: 2px solid #e5e7eb !important; | |
color: #4b5563 !important; | |
padding: 0.75rem 1.5rem !important; | |
border-radius: 12px !important; | |
font-weight: 600 !important; | |
transition: all 0.3s ease !important; | |
} | |
button.secondary:hover { | |
background: #e5e7eb !important; | |
border-color: #d1d5db !important; | |
} | |
/* Input Styles */ | |
.input-container { | |
position: relative; | |
margin-bottom: 1rem; | |
} | |
textarea { | |
border: 2px solid #e5e7eb !important; | |
border-radius: 12px !important; | |
padding: 1rem !important; | |
transition: all 0.3s ease !important; | |
font-size: 1rem !important; | |
line-height: 1.5 !important; | |
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05) !important; | |
} | |
textarea:focus { | |
border-color: #6366f1 !important; | |
box-shadow: 0 4px 6px rgba(99, 102, 241, 0.1) !important; | |
} | |
/* Settings Panel */ | |
.settings-block { | |
background: white !important; | |
border-radius: 15px !important; | |
padding: 1.5rem !important; | |
margin-top: 1rem !important; | |
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05) !important; | |
transition: all 0.3s ease !important; | |
} | |
.settings-block:hover { | |
box-shadow: 0 6px 8px rgba(0, 0, 0, 0.08) !important; | |
} | |
/* Slider Styles */ | |
.gr-slider { | |
height: 4px !important; | |
background: #e5e7eb !important; | |
border-radius: 2px !important; | |
} | |
.gr-slider .handle { | |
width: 16px !important; | |
height: 16px !important; | |
border: 2px solid #6366f1 !important; | |
background: white !important; | |
border-radius: 50% !important; | |
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1) !important; | |
transition: all 0.2s ease !important; | |
} | |
.gr-slider .handle:hover { | |
transform: scale(1.1); | |
} | |
/* Loading Animation */ | |
@keyframes pulse { | |
0% { opacity: 1; } | |
50% { opacity: 0.5; } | |
100% { opacity: 1; } | |
} | |
.loading { | |
animation: pulse 1.5s ease-in-out infinite; | |
} | |
""" | |
# Create the interface with custom theme | |
with gr.Blocks(css=custom_css, theme=gr.themes.Soft()) as demo: | |
# Header | |
with gr.Row(): | |
gr.HTML(""" | |
<div style="text-align: center; margin-bottom: 2rem; padding: 2rem;"> | |
<h1 style="font-size: 3rem; font-weight: 700; color: #4f46e5; margin-bottom: 0.5rem;"> | |
✨ Xylaria Chat | |
</h1> | |
<p style="color: #6b7280; font-size: 1.2rem; font-weight: 500;"> | |
Your Intelligent Multilingual Assistant | |
</p> | |
</div> | |
""") | |
# Main chat interface | |
with gr.Row(): | |
with gr.Column(scale=4): | |
chatbot = gr.Chatbot( | |
height=500, | |
show_label=False, | |
container=True, | |
elem_classes=["chat-window"], | |
type='messages' | |
) | |
image_output = gr.Image( | |
type="pil", | |
label="Generated Image", | |
visible=False, | |
elem_classes=["generated-image"] | |
) | |
with gr.Row(): | |
with gr.Column(scale=8): | |
txt = gr.Textbox( | |
show_label=False, | |
placeholder="Type your message here...", | |
container=False, | |
elem_classes=["input-textbox"] | |
) | |
with gr.Column(scale=1): | |
send_btn = gr.Button( | |
"Send", | |
variant="primary", | |
elem_classes=["primary"] | |
) | |
with gr.Column(scale=1): | |
clear_btn = gr.Button( | |
"Clear", | |
variant="secondary", | |
elem_classes=["secondary"] | |
) | |
# Settings panel | |
with gr.Accordion( | |
"⚙️ Advanced Settings", | |
open=False, | |
elem_classes=["settings-accordion"] | |
): | |
with gr.Row(): | |
with gr.Column(): | |
system_msg = gr.Textbox( | |
value="You are a friendly Chatbot who always responds in English unless the user specifically uses another language.", | |
label="System Message", | |
lines=2 | |
) | |
max_tokens = gr.Slider( | |
minimum=1, | |
maximum=2048, | |
value=512, | |
step=1, | |
label="Max Tokens" | |
) | |
with gr.Column(): | |
temperature = gr.Slider( | |
minimum=0.1, | |
maximum=4.0, | |
value=0.7, | |
step=0.1, | |
label="Temperature" | |
) | |
top_p = gr.Slider( | |
minimum=0.1, | |
maximum=1.0, | |
value=0.95, | |
step=0.05, | |
label="Top-p (nucleus sampling)" | |
) | |
# Rest of your existing functions (user_message, bot_response, etc.) | |
# ... (keep the same function implementations) | |
# Update the event handlers to use the new classes | |
send_event = txt.submit( | |
user_message, | |
[txt, chatbot], | |
[txt, chatbot], | |
queue=False | |
).then( | |
bot_response, | |
[chatbot, system_msg, max_tokens, temperature, top_p], | |
[chatbot, image_output] | |
) | |
send_btn.click( | |
user_message, | |
[txt, chatbot], | |
[txt, chatbot], | |
queue=False | |
).then( | |
bot_response, | |
[chatbot, system_msg, max_tokens, temperature, top_p], | |
[chatbot, image_output] | |
) | |
clear_btn.click( | |
lambda: (None, None), | |
None, | |
[chatbot, image_output], | |
queue=False | |
) | |
# Update image visibility | |
send_event.then( | |
lambda img: gr.update(visible=img is not None), | |
image_output, | |
image_output | |
) | |
return demo | |
# Create and launch the interface | |
demo = create_chat_interface() | |
if __name__ == "__main__": | |
demo.launch(share=True) | |