cinematch-ai / app.py
dbadeev's picture
Update app.py
3505abb verified
Raw
History Blame Contribute Delete
7.82 kB
import modal
import gradio as gr
import asyncio
import os
import logging
from agents.modal_orchestrator import ModalMovieSearchOrchestrator
# Настройка логирования
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
# Инициализация Modal App
app = modal.App("movie-plot-search")
# Глобальная инициализация оркестратора
orchestrator = ModalMovieSearchOrchestrator()
async def chat_interface(message: str, history: list) -> tuple:
""" Основной интерфейс чата с Modal агентами """
try:
logger.info(f"Processing user message: {message[:50]}...")
# Вызов оркестратора
result = await orchestrator.process_user_input(message)
logger.info(f"RESULT: {result}")
status = result.get("status")
response_parts = []
# Логика формирования ответа
if status == "insufficient_length":
response_parts += [
"**❗ Editor Feedback:**",
result.get("message", ""),
"\n---\nPlot description is too short (min 50 words). Please expand and try again."
]
elif status == "search_completed":
response_parts.append("**✅ Search completed!**")
if (result.get("improved_plot") and result.get("improved_plot") != result.get("original_plot")):
response_parts.append(f"**📝 Improved plot:** {result.get('improved_plot')}")
if result.get("movie_overview"):
response_parts.append(f"\n**🎬 Generated movie overview:**\n{result.get('movie_overview')}")
response_parts.append("\n" + "=" * 60)
response_parts.append("**🎯 EXPERT SYSTEM RECOMMENDATIONS**")
response_parts.append("=" * 60)
recommendations = result.get("recommendations", "")
if isinstance(recommendations, dict):
recommendations = recommendations.get("explanations", str(recommendations))
if isinstance(recommendations, str) and recommendations:
response_parts.append(recommendations)
else:
response_parts.append("No recommendations were generated.")
# Метрики
response_parts.append("\n" + "=" * 60)
response_parts.append("**📊 PERFORMANCE METRICS**")
response_parts.append("=" * 60)
metrics = result.get("performance_metrics", {})
if metrics:
response_parts.append(f"🚀 **GPU Used:** {'✅ Yes' if metrics.get('using_gpu', False) else '❌ No'}")
response_parts.append(f"⚡ **Search Time:** {metrics.get('search_time', 0):.3f}s")
response_parts.append(f"🎬 **Movies Analyzed:** {result.get('total_analyzed', 0)}")
response_parts.append("\n" + "=" * 60)
response_parts.append("**🔄 Ready for the next search!**")
elif status == "suggestion":
response_parts.append("**💡 AI Plot Suggestion**")
response_parts.append(result.get("message", ""))
elif status == "awaiting_custom_plot":
response_parts.append("**📝 Custom Plot Mode Activated**")
response_parts.append(result.get("message", ""))
elif status == "custom_plot_too_short":
response_parts.append(result.get("message", "Your plot is too short."))
elif status == "custom_plot_rejected":
response_parts.append(result.get("message", "Your plot doesn't meet requirements."))
elif status == "end_session":
response_parts.append(result.get("message", "Thank you for using Movie Plot Search!"))
elif status == "error":
response_parts.append("**❌ System Error occurred:**")
response_parts.append(result.get("message", "Unknown error"))
else:
response_parts.append(f"⚠️ Unhandled status: {status}")
# Сборка ответа
assistant_reply = "\n".join(response_parts)
new_history = history + [
{"role": "user", "content": message},
{"role": "assistant", "content": assistant_reply}
]
return new_history, ""
except Exception as e:
logger.error(f"Error in chat interface: {e}")
new_history = history + [
{"role": "user", "content": message},
{"role": "assistant", "content": f"**❌ Unexpected error:** {e}"}
]
return new_history, ""
def reset_chat():
logger.info("Resetting chat session")
orchestrator.reset_conversation()
return [], ""
def get_session_info():
try:
summary = orchestrator.get_conversation_summary()
return f"""**Hybrid Session Info:**
- ID: {summary.get('session_id', 'N/A')}
- Step: {summary.get('current_step', 'N/A')}
- Has Plot: {'✅' if summary.get('has_plot', False) else '❌'}
- Has Recommendations: {'✅' if summary.get('has_recommendations', False) else '❌'}
- Total Results: {summary.get('total_search_results', 0)}
"""
except Exception as e:
return f"Error getting session info: {e}"
def force_refresh_session_info():
return get_session_info()
# --- 3. Создание интерфейса (Глобально!) ---
with gr.Blocks(title="🎬 Movie Plot Search") as demo:
gr.Markdown("""
# 🎬 Movie Plot Search Engine
**🏗️ Architecture:**
🖥️ **UI**: Hugging Face Spaces (Gradio);
⚡ **Agents**: Running on Modal Cloud (Serverless GPU);
🤖 **LLM**: Nebius AI Studio API (Llama-3.3-70B-Instruct).
****How it works:**** *Describe a story, dream, or vibe (50+ words) in English. 5 AI Agents will collaborate to find 3 semantic matches.*
""")
with gr.Row():
with gr.Column(scale=4):
# Простой конструктор для максимальной совместимости
chatbot = gr.Chatbot(
value=[],
height=600,
label="🎬 Conversation with AI Agents",
# type='messages'
)
msg = gr.Textbox(
placeholder="Describe a movie plot (50-100 words in English)...",
label="Your message",
lines=3,
max_lines=5
)
with gr.Row():
submit_btn = gr.Button("🚀 Submit", variant="primary", scale=2)
clear_btn = gr.Button("🔄 Clear Chat", scale=1)
with gr.Column(scale=1):
gr.Markdown("### 🔍 How to use:\n1. Describe the plot (50+ words)\n2. Wait for agents to analyze\n3. Get expert recommendations")
session_info = gr.Textbox(
label="Session Info",
value=get_session_info(),
interactive=False,
lines=5
)
refresh_btn = gr.Button("🔄 Refresh Info", size="sm")
# Обработчики
submit_btn.click(chat_interface, [msg, chatbot], [chatbot, msg]).then(get_session_info, None, session_info)
msg.submit(chat_interface, [msg, chatbot], [chatbot, msg]).then(get_session_info, None, session_info)
clear_btn.click(reset_chat, None, [chatbot, msg])
refresh_btn.click(force_refresh_session_info, None, session_info)
# --- 4. Запуск (С ПРАВИЛЬНЫМИ ПАРАМЕТРАМИ) ---
if __name__ == "__main__":
# ВАЖНО: 0.0.0.0 делает приложение видимым для Hugging Face
# demo.launch(server_name="0.0.0.0", server_port=7860)
demo.launch()