Spaces:
Sleeping
Sleeping
| 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() | |