Spaces:
Running
Running
Update app.py
Browse files
app.py
CHANGED
|
@@ -12,24 +12,24 @@ from typing import List, Dict, Optional, Any, Tuple
|
|
| 12 |
|
| 13 |
|
| 14 |
# Configuration
|
| 15 |
-
SPACE_NAME = '
|
| 16 |
-
SPACE_DESCRIPTION = 'AI
|
| 17 |
|
| 18 |
# Default configuration values
|
| 19 |
DEFAULT_CONFIG = {
|
| 20 |
'name': SPACE_NAME,
|
| 21 |
'description': SPACE_DESCRIPTION,
|
| 22 |
-
'system_prompt':
|
| 23 |
-
'temperature': 0.
|
| 24 |
-
'max_tokens':
|
| 25 |
'model': 'google/gemma-3-27b-it',
|
| 26 |
'api_key_var': 'API_KEY',
|
| 27 |
-
'theme': '
|
| 28 |
-
'grounding_urls': [
|
| 29 |
'enable_dynamic_urls': True,
|
| 30 |
'enable_file_upload': True,
|
| 31 |
-
'examples': ['
|
| 32 |
-
'language': '
|
| 33 |
'locked': False
|
| 34 |
}
|
| 35 |
|
|
@@ -183,13 +183,7 @@ def fetch_url_content(url: str, max_length: int = 3000) -> str:
|
|
| 183 |
return f"❌ Invalid URL format: {url}"
|
| 184 |
|
| 185 |
headers = {
|
| 186 |
-
'User-Agent': 'Mozilla/5.0 (compatible; HuggingFace-Space/1.0)'
|
| 187 |
-
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
|
| 188 |
-
'Accept-Language': 'en-US,en;q=0.5',
|
| 189 |
-
'Accept-Encoding': 'gzip, deflate',
|
| 190 |
-
'DNT': '1',
|
| 191 |
-
'Connection': 'keep-alive',
|
| 192 |
-
'Upgrade-Insecure-Requests': '1'
|
| 193 |
}
|
| 194 |
|
| 195 |
response = requests.get(url, headers=headers, timeout=5)
|
|
@@ -235,7 +229,7 @@ def fetch_url_content(url: str, max_length: int = 3000) -> str:
|
|
| 235 |
|
| 236 |
def extract_urls_from_text(text: str) -> List[str]:
|
| 237 |
"""Extract URLs from message text"""
|
| 238 |
-
url_pattern = r'https?://[^\s<>"{}
|
| 239 |
urls = re.findall(url_pattern, text)
|
| 240 |
return [url.rstrip('.,;:)?!') for url in urls]
|
| 241 |
|
|
@@ -335,17 +329,18 @@ def get_grounding_context() -> str:
|
|
| 335 |
return ""
|
| 336 |
|
| 337 |
|
| 338 |
-
def
|
| 339 |
-
"""Export conversation history to
|
| 340 |
if not history:
|
| 341 |
return "No conversation to export."
|
| 342 |
|
| 343 |
-
|
|
|
|
| 344 |
Generated on: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}
|
| 345 |
Space: {SPACE_NAME}
|
| 346 |
Model: {MODEL}
|
| 347 |
|
| 348 |
-
|
| 349 |
|
| 350 |
"""
|
| 351 |
|
|
@@ -355,15 +350,28 @@ Model: {MODEL}
|
|
| 355 |
role = message.get('role', 'unknown')
|
| 356 |
content = message.get('content', '')
|
| 357 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 358 |
if role == 'user':
|
| 359 |
message_count += 1
|
| 360 |
-
|
| 361 |
elif role == 'assistant':
|
| 362 |
-
|
| 363 |
|
| 364 |
-
return
|
| 365 |
-
|
| 366 |
-
|
| 367 |
|
| 368 |
|
| 369 |
def generate_response(message: str, history: List[Dict[str, str]], files: Optional[List] = None) -> str:
|
|
@@ -594,7 +602,7 @@ def create_interface():
|
|
| 594 |
submit_btn = gr.Button("Send", variant="primary")
|
| 595 |
clear_btn = gr.Button("Clear")
|
| 596 |
|
| 597 |
-
# Export functionality
|
| 598 |
with gr.Row():
|
| 599 |
# Use a regular Button for triggering export
|
| 600 |
export_trigger_btn = gr.Button(
|
|
@@ -615,18 +623,18 @@ def create_interface():
|
|
| 615 |
return None
|
| 616 |
|
| 617 |
try:
|
| 618 |
-
content =
|
| 619 |
|
| 620 |
# Create filename
|
| 621 |
space_name_safe = re.sub(r'[^a-zA-Z0-9]+', '_', SPACE_NAME).lower()
|
| 622 |
timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
|
| 623 |
-
filename = f"{space_name_safe}_conversation_{timestamp}.
|
| 624 |
|
| 625 |
# Save to temp file
|
| 626 |
temp_path = Path(tempfile.gettempdir()) / filename
|
| 627 |
temp_path.write_text(content, encoding='utf-8')
|
| 628 |
|
| 629 |
-
# Return the file
|
| 630 |
return gr.File(visible=True, value=str(temp_path))
|
| 631 |
except Exception as e:
|
| 632 |
gr.Error(f"Failed to export conversation: {str(e)}")
|
|
@@ -659,10 +667,13 @@ def create_interface():
|
|
| 659 |
# Get response
|
| 660 |
response = generate_response(message, formatted_history, files_state)
|
| 661 |
|
| 662 |
-
#
|
|
|
|
|
|
|
|
|
|
| 663 |
chat_history = chat_history + [
|
| 664 |
-
{"role": "user", "content": message},
|
| 665 |
-
{"role": "assistant", "content": response}
|
| 666 |
]
|
| 667 |
|
| 668 |
# Update stored history for export
|
|
@@ -1092,12 +1103,4 @@ def create_interface():
|
|
| 1092 |
# Create and launch the interface
|
| 1093 |
if __name__ == "__main__":
|
| 1094 |
demo = create_interface()
|
| 1095 |
-
|
| 1096 |
-
demo.launch(
|
| 1097 |
-
server_name="0.0.0.0",
|
| 1098 |
-
server_port=7860,
|
| 1099 |
-
share=False,
|
| 1100 |
-
# Add allowed CORS origins if needed
|
| 1101 |
-
# Since this is a Gradio app, CORS is handled by Gradio itself
|
| 1102 |
-
# For custom API endpoints, you would need to add CORS middleware
|
| 1103 |
-
)
|
|
|
|
| 12 |
|
| 13 |
|
| 14 |
# Configuration
|
| 15 |
+
SPACE_NAME = 'AmigAI Demo (v1.2a)'
|
| 16 |
+
SPACE_DESCRIPTION = 'Practice conversational Spanish with AmigAI, an AI conversation partner from Peru who will role-play various scenarios while keeping vocabulary and grammar at a Spanish 101 level.'
|
| 17 |
|
| 18 |
# Default configuration values
|
| 19 |
DEFAULT_CONFIG = {
|
| 20 |
'name': SPACE_NAME,
|
| 21 |
'description': SPACE_DESCRIPTION,
|
| 22 |
+
'system_prompt': 'Actuarás como compañero de conversación nativo hispanohablante de Perú para estudiantes universitarios en una clase de español 101. Tu nombre es AmigAI, y estás diseñada para hacer diferentes roles (puedes ser camarero, cocinero, policía, asesor universitario, político, etc.) Nunca jamás harás la tarea de los estudiantes, y esto debes dejarlo muy claro si el estudiante intenta hacer trampas: a) Los estudiantes interactuarán contigo y mantendrán conversaciones en español, y espero que respondas EXCLUSIVAMENTE EN ESPAÑOL (no proporciones traducciones al inglés) utilizando una gama limitada de vocabulario apto para español 101 en la universidad; b) Los verbos sólo deben conjugarse en presente. Evite el subjuntivo; c) Centre sus respuestas en temas como los deportes, la vida cotidiana, las rutinas, la comida y los números; d) Si los alumnos cometen errores, no se les llamará la atención directamente, sino que se les responderá de forma que se modele la ortografía/conjugación/etc. correcta para que puedan entender el error; e) Quiero que reconozcas cuando los alumnos son más avanzados y calibres tu respuesta en función de su nivel, pero tus respuestas NUNCA deben tener errores. f) Las respuestas nunca deben tener más de 30 palabras. Asegúrate de que las oraciones estén completas antes de interrumpir. Espera terminar cada respuesta en menos de 150 tokens.',
|
| 23 |
+
'temperature': 0.7,
|
| 24 |
+
'max_tokens': 150,
|
| 25 |
'model': 'google/gemma-3-27b-it',
|
| 26 |
'api_key_var': 'API_KEY',
|
| 27 |
+
'theme': 'Base',
|
| 28 |
+
'grounding_urls': [],
|
| 29 |
'enable_dynamic_urls': True,
|
| 30 |
'enable_file_upload': True,
|
| 31 |
+
'examples': ['¡Saludos!', 'Buenas tardes', 'Buenos días'],
|
| 32 |
+
'language': 'Spanish',
|
| 33 |
'locked': False
|
| 34 |
}
|
| 35 |
|
|
|
|
| 183 |
return f"❌ Invalid URL format: {url}"
|
| 184 |
|
| 185 |
headers = {
|
| 186 |
+
'User-Agent': 'Mozilla/5.0 (compatible; HuggingFace-Space/1.0)'
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 187 |
}
|
| 188 |
|
| 189 |
response = requests.get(url, headers=headers, timeout=5)
|
|
|
|
| 229 |
|
| 230 |
def extract_urls_from_text(text: str) -> List[str]:
|
| 231 |
"""Extract URLs from message text"""
|
| 232 |
+
url_pattern = r'https?://[^\s<>"{}\|\\^`\[\]]+(?:\.[^\s<>"{}\|\\^`\[\]])*'
|
| 233 |
urls = re.findall(url_pattern, text)
|
| 234 |
return [url.rstrip('.,;:)?!') for url in urls]
|
| 235 |
|
|
|
|
| 329 |
return ""
|
| 330 |
|
| 331 |
|
| 332 |
+
def export_conversation_to_text(history: List[Dict[str, str]]) -> str:
|
| 333 |
+
"""Export conversation history to text with timestamps"""
|
| 334 |
if not history:
|
| 335 |
return "No conversation to export."
|
| 336 |
|
| 337 |
+
text_content = f"""Conversation Export
|
| 338 |
+
==================
|
| 339 |
Generated on: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}
|
| 340 |
Space: {SPACE_NAME}
|
| 341 |
Model: {MODEL}
|
| 342 |
|
| 343 |
+
==================
|
| 344 |
|
| 345 |
"""
|
| 346 |
|
|
|
|
| 350 |
role = message.get('role', 'unknown')
|
| 351 |
content = message.get('content', '')
|
| 352 |
|
| 353 |
+
# Get timestamp from message or use current time as fallback
|
| 354 |
+
timestamp_str = message.get('timestamp', '')
|
| 355 |
+
if timestamp_str:
|
| 356 |
+
try:
|
| 357 |
+
# Parse ISO format timestamp and format it nicely
|
| 358 |
+
timestamp = datetime.fromisoformat(timestamp_str)
|
| 359 |
+
formatted_timestamp = timestamp.strftime('%Y-%m-%d %H:%M:%S')
|
| 360 |
+
except:
|
| 361 |
+
formatted_timestamp = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
| 362 |
+
else:
|
| 363 |
+
formatted_timestamp = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
| 364 |
+
|
| 365 |
+
# Get message length
|
| 366 |
+
msg_length = message.get('length', len(content))
|
| 367 |
+
|
| 368 |
if role == 'user':
|
| 369 |
message_count += 1
|
| 370 |
+
text_content += f"[{formatted_timestamp}] User Message {message_count} ({msg_length} chars):\n{content}\n\n"
|
| 371 |
elif role == 'assistant':
|
| 372 |
+
text_content += f"[{formatted_timestamp}] Assistant Response {message_count} ({msg_length} chars):\n{content}\n\n------------------\n\n"
|
| 373 |
|
| 374 |
+
return text_content
|
|
|
|
|
|
|
| 375 |
|
| 376 |
|
| 377 |
def generate_response(message: str, history: List[Dict[str, str]], files: Optional[List] = None) -> str:
|
|
|
|
| 602 |
submit_btn = gr.Button("Send", variant="primary")
|
| 603 |
clear_btn = gr.Button("Clear")
|
| 604 |
|
| 605 |
+
# Export functionality
|
| 606 |
with gr.Row():
|
| 607 |
# Use a regular Button for triggering export
|
| 608 |
export_trigger_btn = gr.Button(
|
|
|
|
| 623 |
return None
|
| 624 |
|
| 625 |
try:
|
| 626 |
+
content = export_conversation_to_text(chat_history)
|
| 627 |
|
| 628 |
# Create filename
|
| 629 |
space_name_safe = re.sub(r'[^a-zA-Z0-9]+', '_', SPACE_NAME).lower()
|
| 630 |
timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
|
| 631 |
+
filename = f"{space_name_safe}_conversation_{timestamp}.txt"
|
| 632 |
|
| 633 |
# Save to temp file
|
| 634 |
temp_path = Path(tempfile.gettempdir()) / filename
|
| 635 |
temp_path.write_text(content, encoding='utf-8')
|
| 636 |
|
| 637 |
+
# Return the file component with visibility and value
|
| 638 |
return gr.File(visible=True, value=str(temp_path))
|
| 639 |
except Exception as e:
|
| 640 |
gr.Error(f"Failed to export conversation: {str(e)}")
|
|
|
|
| 667 |
# Get response
|
| 668 |
response = generate_response(message, formatted_history, files_state)
|
| 669 |
|
| 670 |
+
# Get current timestamp
|
| 671 |
+
current_time = datetime.now()
|
| 672 |
+
|
| 673 |
+
# Update chat history with timestamps and message lengths
|
| 674 |
chat_history = chat_history + [
|
| 675 |
+
{"role": "user", "content": message, "timestamp": current_time.isoformat(), "length": len(message)},
|
| 676 |
+
{"role": "assistant", "content": response, "timestamp": current_time.isoformat(), "length": len(response)}
|
| 677 |
]
|
| 678 |
|
| 679 |
# Update stored history for export
|
|
|
|
| 1103 |
# Create and launch the interface
|
| 1104 |
if __name__ == "__main__":
|
| 1105 |
demo = create_interface()
|
| 1106 |
+
demo.launch()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|