import gradio as gr import time import sys from llama_index.llms.google_genai import GoogleGenAI from llama_index.core import Settings from config import * from document_processor import * from llama_index.core.chat_engine import CondensePlusContextChatEngine import faiss #new thing query_engine = None chunks_df = None chat_engine = None chat_history = [] def answer_question(question, history): global query_engine, chat_engine if query_engine is None: return "
❌ System not initialized or document database is empty
", "", history try: start_time = time.time() if chat_engine is None: chat_engine = CondensePlusContextChatEngine.from_defaults( retriever=query_engine.retriever, response_synthesizer=query_engine.response_synthesizer ) response = chat_engine.chat(question) retrieved_nodes = query_engine.retriever.retrieve(question) end_time = time.time() processing_time = end_time - start_time sources_html = generate_sources_html(retrieved_nodes) answer_with_time = f"""

📋 Answer:

{response.response}
⏱️ Processing time: {processing_time:.2f} sec
""" new_history = history + [{"role": "user", "content": question}, {"role": "assistant", "content": response.response}] if len(new_history) > 6: new_history = new_history[-6:] return answer_with_time, sources_html, new_history except Exception as e: error_msg = f"
❌ Error processing question: {str(e)}
" return error_msg, "", history def generate_sources_html(nodes): html = "
" html += "

📚 Sources:

" unique_docs = {} for node in nodes: metadata = node.metadata if hasattr(node, 'metadata') else {} doc_name = metadata.get('document_name', 'unknown') doc_link = metadata.get('document_link', '') doc_key = f"{doc_name}||{doc_link}" if doc_key not in unique_docs: unique_docs[doc_key] = [] unique_docs[doc_key].append(node) for doc_key, doc_nodes in unique_docs.items(): doc_name, doc_link = doc_key.split('||', 1) html += f"
" if doc_link: html += f"

📄 {doc_name}

" else: html += f"

📄 {doc_name}

" html += f"

Found relevant fragments: {len(doc_nodes)}

" html += "
" html += "
" return html def get_documents_display(): documents = get_existing_documents() if not documents: return "
No documents in system yet
" html = f"
" html += f"

📚 {len(documents)} documents in the system:

" html += "
" for i, doc_name in enumerate(documents, 1): html += f"
" html += f"{i}. {doc_name}" html += "
" html += "
" return html def upload_and_process_file(files, doc_names, doc_links): global query_engine, chunks_df, chat_engine if not files: return "No files selected", get_documents_display() if len(files) != len(doc_names) or len(files) != len(doc_links): return "Error: Number of files must match number of document names and links", get_documents_display() existing_docs = get_existing_documents() results = [] for i, file in enumerate(files): doc_name = doc_names[i].strip() if i < len(doc_names) else "" doc_link = doc_links[i].strip() if i < len(doc_links) else "" if not doc_name: doc_name = file.name.split('/')[-1].replace('.txt', '').replace('.pdf', '') # Check if document already exists if doc_name in existing_docs: results.append(f"⚠️ {doc_name}: Document already exists in the system") continue log_message(f"🔄 Starting processing of file {i+1}/{len(files)}: {file.name}") file_info, error = process_uploaded_file(file.name, file.name.split('/')[-1], doc_name, doc_link) if error: results.append(f"❌ {file.name.split('/')[-1]}: {error}") continue query_engine, chunks_df, error = add_to_vector_index(file_info['chunks'], file_info, chunks_df) if error: results.append(f"❌ {file_info['file_name']}: Error adding to database - {error}") else: results.append(f"✅ {file_info['document']}: Successfully processed and added to database") log_message(f"✅ Completed processing: {file_info['document']}") # Reset chat engine to include new documents chat_engine = None return "\n".join(results), get_documents_display() def create_interface(): with gr.Blocks(title="AIEXP - AI Expert for Regulatory Documentation", theme=gr.themes.Soft()) as demo: gr.Markdown(""" # AIEXP - Artificial Intelligence Expert ## Tool for working with regulatory documentation """) with gr.Tab("🔍 Document Search"): gr.Markdown("### Ask a question about the uploaded documentation") with gr.Row(): with gr.Column(scale=3): chatbot = gr.Chatbot( label="Chat History", height=400, show_label=True, type="messages" ) question_input = gr.Textbox( label="Your question to the knowledge base", placeholder="Enter your question about the documents...", lines=3 ) ask_btn = gr.Button("🔍 Find Answer", variant="primary", size="lg") clear_btn = gr.Button("🗑️ Clear History", variant="secondary") gr.Examples( examples=[ "Какой стандарт устанавливает порядок признания протоколов испытаний продукции в области использования атомной энергии?", "Кто несет ответственность за организацию и проведение признания протоколов испытаний продукции?", "В каких случаях могут быть признаны протоколы испытаний, проведенные лабораториями, не включенными в перечисления?", ], inputs=question_input ) with gr.Column(scale=1): answer_output = gr.HTML( label="", value="
The answer to your question will appear here...
", ) sources_output = gr.HTML( label="", value="
Sources will appear here...
", ) ask_btn.click( fn=answer_question, inputs=[question_input, chatbot], outputs=[answer_output, sources_output, chatbot] ).then( lambda: "", inputs=None, outputs=question_input ) question_input.submit( fn=answer_question, inputs=[question_input, chatbot], outputs=[answer_output, sources_output, chatbot] ).then( lambda: "", inputs=None, outputs=question_input ) clear_btn.click( lambda: [], inputs=None, outputs=chatbot ) with gr.Tab("📚 Document Management"): gr.Markdown("### Document database and adding new files") with gr.Row(): with gr.Column(scale=2): documents_display = gr.HTML( label="Document list", value=get_documents_display() ) refresh_btn = gr.Button("🔄 Refresh List", variant="secondary") with gr.Column(scale=1): gr.Markdown("#### Upload new documents") gr.Markdown("Supported formats: PDF, TXT") file_upload = gr.File( file_count="multiple", file_types=[".pdf", ".txt"], label="Select files to upload" ) doc_names_input = gr.Textbox( label="Document names (one per line)", placeholder="Enter document names, one per line...", lines=5 ) doc_links_input = gr.Textbox( label="Document links (one per line)", placeholder="Enter document links, one per line...", lines=5 ) upload_btn = gr.Button("📤 Upload and Process", variant="primary") upload_status = gr.Textbox( label="Upload status", lines=8, max_lines=10, interactive=False ) def process_names_and_links(names_text, links_text): names = [name.strip() for name in names_text.split('\n') if name.strip()] links = [link.strip() for link in links_text.split('\n') if link.strip()] return names, links upload_btn.click( fn=lambda files, names, links: upload_and_process_file( files, *process_names_and_links(names, links) ), inputs=[file_upload, doc_names_input, doc_links_input], outputs=[upload_status, documents_display] ) refresh_btn.click( fn=lambda: get_documents_display(), outputs=[documents_display] ) return demo if __name__ == "__main__": try: log_message("🚀 Starting AIEXP - AI Expert for Regulatory Documentation") # Initialize LLM llm = GoogleGenAI(model="gemini-2.0-flash", api_key=GOOGLE_API_KEY) Settings.llm = llm # Initialize system query_engine, chunks_df, success = initialize_system() log_message("🌟 Starting web interface...") demo = create_interface() # Launch regardless of initialization success demo.launch( server_name="0.0.0.0", server_port=7860, share=True, debug=False, show_error=True ) except Exception as e: log_message(f"❌ Startup error: {str(e)}") # Create minimal interface even if there's an error import gradio as gr def error_interface(): with gr.Blocks() as demo: gr.Markdown(f"# Error: {str(e)}") gr.Markdown("Please check your configuration and try again.") return demo error_demo = error_interface() error_demo.launch( server_name="0.0.0.0", server_port=7860, share=True )