"""Gradio web application for Francis Botcon.""" import gradio as gr from typing import List, Tuple import sys from pathlib import Path # Add project root to path sys.path.insert(0, str(Path(__file__).parent.parent)) from src.model import FrancisModel from src.rag_system import RAGSystem from src.logger import LoggerSetup from src.config_loader import config logger = LoggerSetup.setup("INFO", "./logs/app.log") class FrancisBotconApp: """Gradio application for Francis Botcon chatbot.""" def __init__(self): """Initialize the application.""" logger.info("Initializing Francis Botcon Application...") try: # Check if LoRA adapter exists adapter_path = None if Path("./models/francis_botcon_lora").exists(): adapter_path = "./models/francis_botcon_lora" logger.info("✓ LoRA adapter found, will use fine-tuned model") # Initialize model logger.info("Loading language model...") self.model = FrancisModel(adapter_path=adapter_path) logger.info("✓ Model loaded") # Initialize RAG system logger.info("Initializing RAG system...") self.rag_system = RAGSystem() logger.info("✓ RAG system initialized") self.conversation_history: List[Tuple[str, str]] = [] except Exception as e: logger.error(f"Failed to initialize application: {str(e)}") raise def respond(self, message: str, history: List) -> Tuple[str, List]: """Generate a response to a user message. Args: message: User's input message history: Conversation history Returns: Tuple of (response, updated_history) """ if not message.strip(): return "", history logger.info(f"User: {message[:100]}...") try: # Retrieve context using RAG context_docs = self.rag_system.retrieve_context(message, top_k=5) # Build prompt with context prompt = self.rag_system.build_prompt(message, context_docs) # Generate response response = self.model.generate( prompt, max_length=512, temperature=0.7, top_p=0.9 ) logger.info(f"Generated response ({len(response)} chars)") # Format response with citations formatted_response = self._format_response(response, context_docs) return formatted_response except Exception as e: logger.error(f"Error generating response: {str(e)}") return f"I apologize, but I encountered an error: {str(e)}", history def _format_response(self, response: str, context_docs: List) -> str: """Format response with citations. Args: response: Generated response text context_docs: Retrieved context documents Returns: Formatted response with citations """ formatted = f"{response}\n\n" if context_docs: formatted += "---\n**Sources from My Works:**\n" for i, doc in enumerate(context_docs[:3], 1): # Show top 3 sources metadata = doc["metadata"] title = metadata.get("title", "Unknown Work") source = metadata.get("source", "") similarity = doc["similarity"] formatted += f"\n{i}. *{title}*" if source: formatted += f" ({source})" formatted += f" - Relevance: {similarity:.1%}" return formatted def create_interface(self) -> gr.Interface: """Create Gradio interface. Returns: Gradio Interface """ with gr.Blocks(title="Francis Botcon", theme=gr.themes.Soft()) as interface: gr.Markdown("# Francis Botcon") gr.Markdown( "A conversational AI that emulates the philosophical style of Francis Bacon (1561-1626). " "Ask questions and receive responses in his distinctive voice, with citations from his works." ) chatbot = gr.Chatbot( label="Conversation with Francis Bacon", height=500 ) with gr.Row(): message_input = gr.Textbox( label="Your Question", placeholder="Ask Francis Bacon anything...", lines=2 ) submit_button = gr.Button("Send", variant="primary") with gr.Accordion("Settings", open=False): with gr.Row(): temperature = gr.Slider( label="Temperature", minimum=0.1, maximum=1.0, value=0.7, step=0.1 ) top_p = gr.Slider( label="Top P", minimum=0.0, maximum=1.0, value=0.9, step=0.1 ) def chat(message, chat_history): """Chat function.""" response = self.respond(message, chat_history) chat_history.append((message, response)) return "", chat_history # Connect events submit_button.click( chat, inputs=[message_input, chatbot], outputs=[message_input, chatbot] ) message_input.submit( chat, inputs=[message_input, chatbot], outputs=[message_input, chatbot] ) gr.Markdown(""" --- **About Francis Botcon:** - Based on Llama 3.2:3b fine-tuned on Francis Bacon's works - Uses Retrieval-Augmented Generation (RAG) for contextual responses - Citations refer to passages from Bacon's authentic works **Note:** This is an AI model trained to emulate Bacon's style. For academic purposes, always verify citations with original sources. """) return interface def launch(self, share: bool = False, server_name: str = "0.0.0.0", server_port: int = 7860): """Launch the Gradio app. Args: share: Whether to create a shareable link server_name: Server address server_port: Server port """ logger.info(f"Launching Francis Botcon on {server_name}:{server_port}") interface = self.create_interface() interface.launch( share=share, server_name=server_name, server_port=server_port, ) def main(): """Main entry point.""" try: app = FrancisBotconApp() app.launch( share=config.get("app.share", False), server_port=config.get("app.port", 7860) ) except Exception as e: logger.error(f"Application failed: {str(e)}", exc_info=True) sys.exit(1) if __name__ == "__main__": main()