Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
@@ -9,16 +9,6 @@ from datetime import datetime
|
|
9 |
import urllib.parse
|
10 |
from pathlib import Path
|
11 |
from typing import List, Dict, Optional, Any, Tuple
|
12 |
-
import base64
|
13 |
-
import io
|
14 |
-
|
15 |
-
# Try to import gradio_client for TTS support
|
16 |
-
try:
|
17 |
-
from gradio_client import Client
|
18 |
-
GRADIO_CLIENT_AVAILABLE = True
|
19 |
-
except ImportError:
|
20 |
-
GRADIO_CLIENT_AVAILABLE = False
|
21 |
-
print("Warning: gradio_client not available. TTS features will be disabled.")
|
22 |
|
23 |
|
24 |
# Configuration
|
@@ -32,7 +22,7 @@ DEFAULT_CONFIG = {
|
|
32 |
'system_prompt': "You are Domenico from Sicily, a Juventus football fan, native Italian speaker serving as a conversational partner for university students in an Italian 101 class. Students will interact and converse with you in Italian, and you must respond EXCLUSIVELY IN ITALIAN without providing English translations, using vocabulary appropriate for beginner-level Italian 101 students. Focus your responses on topics suitable for beginners such as sports, daily life, routines, food, numbers, and hobbies. When students make errors, model the correct forms naturally in your response without explicitly pointing out mistakes, allowing them to learn through exposure to proper usage. Recognize when students demonstrate more advanced abilities and adjust your language complexity accordingly, while ensuring your Italian remains error-free. Keep all responses between 5-50 words, making sure sentences are grammatically complete. Limit all verb conjugations to the present tense only, avoiding all other verb forms and tenses. Address students using the informal second-person singular 'tu' form.",
|
33 |
'temperature': 0.5,
|
34 |
'max_tokens': 250,
|
35 |
-
'model': '
|
36 |
'api_key_var': 'API_KEY',
|
37 |
'theme': 'Default',
|
38 |
'grounding_urls': ["https://www.pnac.org/wp-content/uploads/Italian-Study-Guide.pdf"],
|
@@ -40,8 +30,6 @@ DEFAULT_CONFIG = {
|
|
40 |
'enable_file_upload': True,
|
41 |
'examples': ['Ciao! Come stai oggi?', 'Mi piace giocare a calcio. E tu?', 'Cosa mangi di solito a colazione?', 'A che ora ti svegli la mattina?', 'Qual è il tuo sport preferito?'],
|
42 |
'language': 'Italian',
|
43 |
-
'enable_tts': False,
|
44 |
-
'tts_spaces': ['facebook/mms-tts-eng', 'microsoft/speecht5-tts-demo'],
|
45 |
'locked': False
|
46 |
}
|
47 |
|
@@ -294,80 +282,6 @@ def process_file_upload(file_path: str) -> str:
|
|
294 |
_url_content_cache = {}
|
295 |
|
296 |
|
297 |
-
def generate_tts(text: str, hf_token: Optional[str] = None) -> Optional[Tuple[int, Any]]:
|
298 |
-
"""
|
299 |
-
Generate text-to-speech audio using HuggingFace Spaces via gradio_client.
|
300 |
-
Uses multiple fallback options for maximum reliability.
|
301 |
-
|
302 |
-
Returns: Tuple of (sample_rate, audio_array) or None if failed
|
303 |
-
"""
|
304 |
-
if not GRADIO_CLIENT_AVAILABLE:
|
305 |
-
return None
|
306 |
-
|
307 |
-
if not text or not text.strip():
|
308 |
-
return None
|
309 |
-
|
310 |
-
# Get HF token from environment if not provided
|
311 |
-
if not hf_token:
|
312 |
-
hf_token = os.getenv("HF_TOKEN")
|
313 |
-
|
314 |
-
# Get TTS spaces from config
|
315 |
-
tts_spaces = DEFAULT_CONFIG.get('tts_spaces', [])
|
316 |
-
if not tts_spaces:
|
317 |
-
# Default fallback spaces if none configured
|
318 |
-
tts_spaces = [
|
319 |
-
"facebook/mms-tts-eng",
|
320 |
-
"microsoft/speecht5-tts-demo",
|
321 |
-
"coqui/XTTS",
|
322 |
-
"myshell-ai/OpenVoice"
|
323 |
-
]
|
324 |
-
|
325 |
-
# Limit text length for TTS
|
326 |
-
max_text_length = 500
|
327 |
-
if len(text) > max_text_length:
|
328 |
-
text = text[:max_text_length] + "..."
|
329 |
-
|
330 |
-
# Try each TTS space in order
|
331 |
-
for space_name in tts_spaces:
|
332 |
-
try:
|
333 |
-
print(f"Trying TTS space: {space_name}")
|
334 |
-
client = Client(space_name, hf_token=hf_token)
|
335 |
-
|
336 |
-
# Different spaces have different APIs, try common patterns
|
337 |
-
try:
|
338 |
-
# Pattern 1: Simple text input
|
339 |
-
result = client.predict(text, api_name="/predict")
|
340 |
-
except:
|
341 |
-
try:
|
342 |
-
# Pattern 2: Text + language
|
343 |
-
result = client.predict(text, "en", api_name="/predict")
|
344 |
-
except:
|
345 |
-
try:
|
346 |
-
# Pattern 3: Text + voice/speaker
|
347 |
-
result = client.predict(text, "default", api_name="/predict")
|
348 |
-
except:
|
349 |
-
continue
|
350 |
-
|
351 |
-
# Handle different return types
|
352 |
-
if isinstance(result, str) and os.path.exists(result):
|
353 |
-
# Result is a file path
|
354 |
-
import soundfile as sf
|
355 |
-
audio_data, sample_rate = sf.read(result)
|
356 |
-
return (sample_rate, audio_data)
|
357 |
-
elif isinstance(result, tuple) and len(result) >= 2:
|
358 |
-
# Result is (sample_rate, audio_array)
|
359 |
-
return result
|
360 |
-
elif hasattr(result, 'get') and 'audio' in result:
|
361 |
-
# Result is a dict with audio key
|
362 |
-
return result['audio']
|
363 |
-
|
364 |
-
except Exception as e:
|
365 |
-
print(f"TTS failed with {space_name}: {str(e)}")
|
366 |
-
continue
|
367 |
-
|
368 |
-
return None
|
369 |
-
|
370 |
-
|
371 |
def get_grounding_context() -> str:
|
372 |
"""Get grounding context from configured URLs with caching"""
|
373 |
urls = GROUNDING_URLS
|
@@ -556,11 +470,8 @@ Get your API key at: https://openrouter.ai/keys"""
|
|
556 |
)
|
557 |
|
558 |
if response.status_code == 200:
|
559 |
-
|
560 |
-
|
561 |
-
ai_response = result['choices'][0]['message']['content']
|
562 |
-
except (json.JSONDecodeError, KeyError) as e:
|
563 |
-
return f"❌ Error parsing API response: {str(e)}"
|
564 |
|
565 |
# Add file notification if files were uploaded
|
566 |
if file_notification:
|
@@ -568,11 +479,8 @@ Get your API key at: https://openrouter.ai/keys"""
|
|
568 |
|
569 |
return ai_response
|
570 |
else:
|
571 |
-
|
572 |
-
|
573 |
-
error_message = error_data.get('error', {}).get('message', 'Unknown error')
|
574 |
-
except:
|
575 |
-
error_message = response.text if response.text else 'Unknown error'
|
576 |
return f"❌ API Error ({response.status_code}): {error_message}"
|
577 |
|
578 |
except requests.exceptions.Timeout:
|
@@ -610,7 +518,6 @@ def verify_hf_token_access() -> Tuple[bool, str]:
|
|
610 |
return False, f"Error verifying HF token: {str(e)}"
|
611 |
|
612 |
|
613 |
-
|
614 |
# Create main interface with clean tab structure
|
615 |
def create_interface():
|
616 |
"""Create the Gradio interface with clean tab structure"""
|
@@ -683,19 +590,6 @@ def create_interface():
|
|
683 |
size="sm"
|
684 |
)
|
685 |
|
686 |
-
# TTS functionality
|
687 |
-
if DEFAULT_CONFIG.get('enable_tts', False) and GRADIO_CLIENT_AVAILABLE:
|
688 |
-
with gr.Row():
|
689 |
-
tts_btn = gr.Button("🔊 Read Last Response", variant="secondary", size="sm")
|
690 |
-
tts_status = gr.Textbox(label="TTS Status", visible=False, interactive=False)
|
691 |
-
|
692 |
-
audio_output = gr.Audio(
|
693 |
-
label="TTS Output",
|
694 |
-
visible=False,
|
695 |
-
autoplay=True,
|
696 |
-
type="numpy"
|
697 |
-
)
|
698 |
-
|
699 |
# Export handler
|
700 |
def prepare_export():
|
701 |
if not chat_history_store:
|
@@ -719,46 +613,6 @@ def create_interface():
|
|
719 |
outputs=[export_btn]
|
720 |
)
|
721 |
|
722 |
-
# TTS handler
|
723 |
-
if DEFAULT_CONFIG.get('enable_tts', False) and GRADIO_CLIENT_AVAILABLE:
|
724 |
-
def handle_tts(chat_history):
|
725 |
-
"""Generate TTS for the last assistant message"""
|
726 |
-
if not chat_history:
|
727 |
-
return None, gr.update(value="No messages to read", visible=True)
|
728 |
-
|
729 |
-
# Find last assistant message
|
730 |
-
last_assistant_msg = None
|
731 |
-
for msg in reversed(chat_history):
|
732 |
-
if msg.get("role") == "assistant":
|
733 |
-
last_assistant_msg = msg.get("content", "")
|
734 |
-
break
|
735 |
-
|
736 |
-
if not last_assistant_msg:
|
737 |
-
return None, gr.update(value="No assistant message found", visible=True)
|
738 |
-
|
739 |
-
# Update status
|
740 |
-
status_msg = "🎯 Generating audio..."
|
741 |
-
|
742 |
-
# Generate TTS
|
743 |
-
audio_result = generate_tts(last_assistant_msg)
|
744 |
-
|
745 |
-
if audio_result:
|
746 |
-
return (
|
747 |
-
gr.update(value=audio_result, visible=True),
|
748 |
-
gr.update(value="✅ Audio generated successfully", visible=True)
|
749 |
-
)
|
750 |
-
else:
|
751 |
-
return (
|
752 |
-
None,
|
753 |
-
gr.update(value="❌ TTS generation failed", visible=True)
|
754 |
-
)
|
755 |
-
|
756 |
-
tts_btn.click(
|
757 |
-
handle_tts,
|
758 |
-
inputs=[chatbot],
|
759 |
-
outputs=[audio_output, tts_status]
|
760 |
-
)
|
761 |
-
|
762 |
# Examples section
|
763 |
if examples:
|
764 |
gr.Examples(examples=examples, inputs=msg)
|
|
|
9 |
import urllib.parse
|
10 |
from pathlib import Path
|
11 |
from typing import List, Dict, Optional, Any, Tuple
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
12 |
|
13 |
|
14 |
# Configuration
|
|
|
22 |
'system_prompt': "You are Domenico from Sicily, a Juventus football fan, native Italian speaker serving as a conversational partner for university students in an Italian 101 class. Students will interact and converse with you in Italian, and you must respond EXCLUSIVELY IN ITALIAN without providing English translations, using vocabulary appropriate for beginner-level Italian 101 students. Focus your responses on topics suitable for beginners such as sports, daily life, routines, food, numbers, and hobbies. When students make errors, model the correct forms naturally in your response without explicitly pointing out mistakes, allowing them to learn through exposure to proper usage. Recognize when students demonstrate more advanced abilities and adjust your language complexity accordingly, while ensuring your Italian remains error-free. Keep all responses between 5-50 words, making sure sentences are grammatically complete. Limit all verb conjugations to the present tense only, avoiding all other verb forms and tenses. Address students using the informal second-person singular 'tu' form.",
|
23 |
'temperature': 0.5,
|
24 |
'max_tokens': 250,
|
25 |
+
'model': 'openai/gpt-oss-120b',
|
26 |
'api_key_var': 'API_KEY',
|
27 |
'theme': 'Default',
|
28 |
'grounding_urls': ["https://www.pnac.org/wp-content/uploads/Italian-Study-Guide.pdf"],
|
|
|
30 |
'enable_file_upload': True,
|
31 |
'examples': ['Ciao! Come stai oggi?', 'Mi piace giocare a calcio. E tu?', 'Cosa mangi di solito a colazione?', 'A che ora ti svegli la mattina?', 'Qual è il tuo sport preferito?'],
|
32 |
'language': 'Italian',
|
|
|
|
|
33 |
'locked': False
|
34 |
}
|
35 |
|
|
|
282 |
_url_content_cache = {}
|
283 |
|
284 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
285 |
def get_grounding_context() -> str:
|
286 |
"""Get grounding context from configured URLs with caching"""
|
287 |
urls = GROUNDING_URLS
|
|
|
470 |
)
|
471 |
|
472 |
if response.status_code == 200:
|
473 |
+
result = response.json()
|
474 |
+
ai_response = result['choices'][0]['message']['content']
|
|
|
|
|
|
|
475 |
|
476 |
# Add file notification if files were uploaded
|
477 |
if file_notification:
|
|
|
479 |
|
480 |
return ai_response
|
481 |
else:
|
482 |
+
error_data = response.json()
|
483 |
+
error_message = error_data.get('error', {}).get('message', 'Unknown error')
|
|
|
|
|
|
|
484 |
return f"❌ API Error ({response.status_code}): {error_message}"
|
485 |
|
486 |
except requests.exceptions.Timeout:
|
|
|
518 |
return False, f"Error verifying HF token: {str(e)}"
|
519 |
|
520 |
|
|
|
521 |
# Create main interface with clean tab structure
|
522 |
def create_interface():
|
523 |
"""Create the Gradio interface with clean tab structure"""
|
|
|
590 |
size="sm"
|
591 |
)
|
592 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
593 |
# Export handler
|
594 |
def prepare_export():
|
595 |
if not chat_history_store:
|
|
|
613 |
outputs=[export_btn]
|
614 |
)
|
615 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
616 |
# Examples section
|
617 |
if examples:
|
618 |
gr.Examples(examples=examples, inputs=msg)
|