import gradio as gr from supervisor_agent import supervisor_agent from database import engine from sqlalchemy import text import uuid import pandas as pd import os from supabase import create_client from dotenv import load_dotenv import base64 from langchain_groq import ChatGroq load_dotenv() supabase = create_client(os.getenv("SUPABASE_URL"), os.getenv("SUPABASE_KEY")) # Initialize LLM for evidence detection GROQ_API_KEY = os.getenv("GROQ_API_KEY") if not GROQ_API_KEY: raise ValueError( "GROQ_API_KEY environment variable is not set. " "Please set it in your environment variables." ) evidence_detector_llm = ChatGroq( model="moonshotai/kimi-k2-instruct-0905", api_key=GROQ_API_KEY, temperature=0.3 ) # Handle logo loading safely try: logo_path = os.path.join(os.getcwd(), "ic_logo.png") def embed_image_base64(path): with open(path, "rb") as f: return "data:image/png;base64," + base64.b64encode(f.read()).decode() logo_b64 = embed_image_base64(logo_path) except: # Fallback if logo file doesn't exist logo_b64 = "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTIwIiBoZWlnaHQ9IjEyMCIgdmlld0JveD0iMCAwIDEyMCAxMjAiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHdpZHRoPSIxMjAiIGhlaWdodD0iMTIwIiByeD0iMTAiIGZpbGw9IiM2NjdlZWEiLz4KPHRleHQgeD0iNjAiIHk9IjcwIiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmaWxsPSJ3aGl0ZSIgZm9udC1zaXplPSIyNCIgZm9udC13ZWlnaHQ9ImJvbGQiPkU8L3RleHQ+Cjwvc3ZnPgo=" def upload_file_to_supabase_gradio(file_path: str, order_id: str): """ Uploads a file to Supabase storage from a file path (Gradio format) Returns the public URL or None if upload fails """ if not file_path: return None try: with open(file_path, 'rb') as f: file_bytes = f.read() file_ext = os.path.splitext(file_path)[1].lstrip('.') if not file_ext: file_ext = 'bin' timestamp = str(uuid.uuid4().hex) storage_path = f"{order_id}_{timestamp}.{file_ext}" res = supabase.storage.from_("complaints").upload(storage_path, file_bytes) if isinstance(res, dict) and res.get("error"): print(f"Supabase upload error: {res.get('error')}") return None public_url = supabase.storage.from_("complaints").get_public_url(storage_path) print(f"File uploaded successfully: {public_url}") return public_url except Exception as e: print(f"Error uploading file to Supabase: {e}") import traceback traceback.print_exc() return None def check_if_evidence_requested(bot_response: str) -> bool: """ Use LLM to intelligently detect if the bot is asking for file/evidence upload. Returns True if evidence is requested, False otherwise. """ try: prompt = f"""You are an assistant that determines if a customer service response is asking the user to upload files, photos, videos, or any evidence. Analyze the following bot response and determine if it is EXPLICITLY asking the user to upload or attach files/photos/videos/evidence. Bot Response: "{bot_response}" Rules: - Only respond with "true" if the bot is CLEARLY asking for file uploads, photos, videos, or evidence - Respond with "false" if the bot is just asking questions, providing information, or not requesting any files - Keywords to look for: upload, attach, file, photo, video, evidence, picture, image, send, provide (when related to files) - Do NOT say "true" for general questions about the issue Respond with ONLY "true" or "false" - nothing else.""" response = evidence_detector_llm.invoke(prompt) result = response.content.strip().lower() print(f"šŸ¤– LLM Evidence Detection - Bot said: '{bot_response[:100]}...'") print(f"šŸ¤– LLM Response: '{result}'") # Return True only if LLM explicitly says "true" return result == "true" except Exception as e: print(f"āŒ Error in LLM evidence detection: {e}") # Fallback to False (don't show upload button if LLM fails) return False def chat_with_agent(message, file, session_id): """Main chat function""" try: if not session_id: session_id = int(uuid.uuid4().int % 1000000) session_id = int(session_id) file_url = None if file is not None: print(f"šŸ“Ž File received: {file}") print(f"šŸ“Ž File type: {type(file)}") try: file_url = upload_file_to_supabase_gradio( file, order_id=str(session_id) ) if file_url: print(f"āœ… File uploaded: {file_url}") else: print("āš ļø File upload failed, continuing without file") except Exception as e: # File upload should not crash the conversation print(f"āŒ File upload error: {e}") return ( "The file could not be uploaded due to a processing error. " "Your request was received, but the file was not saved.", session_id, gr.update(visible=False) ) supervisor_input = f"Session ID: {session_id} | User Query: {message}" if file_url: supervisor_input += f" | FileURL: {file_url}" print(f"\nšŸ“Ø Sending to supervisor: {supervisor_input}\n") try: result = supervisor_agent.invoke( {"messages": [{"role": "user", "content": supervisor_input}]}, {"configurable": {"thread_id": str(session_id)}}, ) bot_response = result["messages"][-1].content # USE LLM TO INTELLIGENTLY DETECT IF EVIDENCE/FILE UPLOAD IS REQUESTED should_show_upload = check_if_evidence_requested(bot_response) print(f"šŸ‘ Upload button visibility: {should_show_upload}") return ( bot_response, session_id, gr.update(visible=should_show_upload) ) except Exception as e: # Dedicated LLM failure response print(f"āŒ LLM processing error: {e}") import traceback traceback.print_exc() return ( "The current model is not responding reliably at the moment. " "Please try again shortly or contact support if the issue continues.", session_id, gr.update(visible=False) ) except Exception as e: # Catch-all to avoid silent crashes print(f"āŒ Unexpected server error: {e}") import traceback traceback.print_exc() return ( "An unexpected internal error occurred while processing your request.", session_id, gr.update(visible=False) ) def check_complaints(order_id): """View all complaints for a user""" try: if not order_id: return pd.DataFrame({"Message": ["Please enter a valid Order ID"]}) with engine.connect() as conn: result = conn.execute( text(""" SELECT order_id, product_name, complaint_text, complaint_file_url, created_at FROM orders WHERE order_id = :order_id AND is_complaint = 1 ORDER BY created_at DESC """), {"order_id": order_id} ) rows = result.fetchall() if not rows: return pd.DataFrame({"Message": ["No complaints found for this Order ID"]}) df = pd.DataFrame(rows, columns=["Order ID", "Product", "Complaint", "Evidence", "Date"]) return df except Exception as e: return pd.DataFrame({"Error": [str(e)]}) def upload_catalog(file, table_name): """Upload CSV/Excel to database with validation""" if not file: return "āŒ Please select a file to upload" try: file_path = file if isinstance(file, str) else file.name file_name = os.path.basename(file_path) # Check file extension if not (file_path.lower().endswith('.csv') or file_path.lower().endswith(('.xlsx', '.xls'))): return f"āŒ Invalid file type: {file_name}\n\nāš ļø Please select a CSV or Excel file (.csv, .xlsx, .xls)" # Read file based on extension if file_path.lower().endswith('.csv'): df = pd.read_csv(file_path) elif file_path.lower().endswith(('.xlsx', '.xls')): df = pd.read_excel(file_path) # Upload to database df.to_sql(table_name, con=engine, if_exists="replace", index=False) return f"āœ… Successfully uploaded {len(df)} rows from {file_name} to table '{table_name}'" except Exception as e: return f"āŒ Upload failed: {str(e)}" custom_css = """ body { position: relative; background-image: linear-gradient(rgba(59, 130, 246, 0.03) 1px, transparent 1px), linear-gradient(90deg, rgba(59, 130, 246, 0.03) 1px, transparent 1px); background-size: 60px 60px; z-index: 1; transform: unset !important; background: radial-gradient(circle at 20% 15%, rgba(59, 130, 246, 0.12), transparent 40%), radial-gradient(circle at 80% 25%, rgba(139, 92, 246, 0.1), transparent 45%), radial-gradient(circle at 50% 70%, rgba(16, 185, 129, 0.08), transparent 50%), radial-gradient(circle at 10% 85%, rgba(236, 72, 153, 0.08), transparent 45%), linear-gradient(135deg, #0a0e27 0%, #1a1f3a 25%, #0f1629 50%, #1e2139 75%, #0d1425 100%) !important; } gradio-app { background-color: transparent !important; } /* Hexagon pattern overlay */ body::after { content: ''; position: absolute; z-index: -1; top: 0; left: 0; right: 0; bottom: 0; background-image: repeating-linear-gradient(45deg, transparent, transparent 25px, rgba(174, 246, 59, 0.015) 35px, rgba(93, 205, 18, 0.015) 70px), repeating-linear-gradient(-45deg, transparent, transparent 25px, rgba(139, 92, 246, 0.015) 35px, rgba(139, 92, 246, 0.015) 70px); pointer-events: none; opacity: 0.6; } .header { padding: 40px 30px; background: radial-gradient(circle at 20% 10%, rgba(255, 255, 255, 0.25), transparent 55%), radial-gradient(circle at 85% 20%, rgba(45, 135, 255, 0.22), transparent 70%), linear-gradient(135deg, #0e1a2b, #0f1032, #03011a); color: white; border-bottom-left-radius: 40px; border-bottom-right-radius: 40px; box-shadow: 0 50px 90px -20px rgba(0, 0, 0, 0.6); display: flex; align-items: center; width: 100%; } .header-logo { flex: unset; display: flex; justify-content: flex-start; width: 100px; } .header h1 { margin: 0; flex: 1; text-align: center; font-size: 2.5em; font-weight: 800; letter-spacing: 1px; background: linear-gradient(to right, #fff 20%, #dce4ff 40%, #cedcff 60%); -webkit-background-clip: text; color: transparent; } /* Tab headers with gradient */ .tabs button { background: radial-gradient(circle at 20% 10%, rgba(255, 255, 255, 0.25), transparent 55%), radial-gradient(circle at 85% 20%, rgba(45, 135, 255, 0.22), transparent 70%), linear-gradient(35deg, #0e1a2b, #0f1032, #03011a); color: white !important; font-weight: 600 !important; font-size: 1.1em !important; padding: 8px 24px !important; border-radius: 8px 8px 0 0 !important; border: none !important; transition: all 0.3s ease !important; } .tabs button:hover { background: linear-gradient(135deg, #764ba2 0%, #667eea 100%) !important; transform: translateY(-2px) !important; box-shadow: 0 4px 15px rgba(102, 126, 234, 0.4) !important; } .tabs button[aria-selected="true"] { background: linear-gradient(135deg, #764ba2 0%, #667eea 100%) !important; box-shadow: 0 4px 15px rgba(102, 126, 234, 0.5) !important; } .tabs button .label-text { width: 100%; max-width: 200px; } /* Target all buttons with variant="primary" */ button[variant="primary"] { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important; border: none !important; color: white !important; font-weight: 600 !important; border-radius: 8px !important; transition: all 0.3s ease !important; } button[variant="primary"]:hover { background: linear-gradient(135deg, #764ba2 0%, #667eea 100%) !important; transform: translateY(-2px) !important; box-shadow: 0 5px 20px rgba(102, 126, 234, 0.5) !important; } /* Backup selectors for buttons */ .primary { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important; border: none !important; color: white !important; } .primary:hover { background: linear-gradient(135deg, #764ba2 0%, #667eea 100%) !important; } /* ========== NEW: CENTERED FILE UPLOAD BUTTON ========== */ /* Container for centered file button */ .centered-file-container { display: flex !important; justify-content: center !important; align-items: center !important; width: 100% !important; margin: 15px 0 !important; padding: 0 !important; } /* Style the file upload button itself */ #evidence-upload { max-width: 200px !important; min-width: 180px !important; margin: 0 auto !important; display: flex !important; justify-content: center !important; } #evidence-upload button { font-size: 0.9em !important; padding: 10px 20px !important; white-space: nowrap !important; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important; color: white !important; border: none !important; border-radius: 8px !important; font-weight: 600 !important; transition: all 0.3s ease !important; cursor: pointer !important; } #evidence-upload button:hover { background: linear-gradient(135deg, #764ba2 0%, #667eea 100%) !important; transform: translateY(-2px) !important; box-shadow: 0 5px 20px rgba(102, 126, 234, 0.5) !important; } #evidence-upload .wrap { display: flex !important; justify-content: center !important; } /* ========== END CENTERED FILE UPLOAD BUTTON ========== */ .compact-action-row { display: flex; justify-content: center !important; gap: 12px !important; margin-top: 10px; } .compact-action-row button { min-width: 120px !important; height: 42px !important; font-size: 0.9rem !important; border-radius: 10px !important; font-weight: 600 !important; } /* Upload button center alignment */ .upload-section { display: flex; flex-direction: column; align-items: center !important; } .upload-btn { min-width: 165px !important; height: 45px !important; color: white; font-size: 0.9rem !important; border-radius: 12px !important; } /* Hover effect */ .compact-action-row button:hover { transform: translateY(-2px) !important; box-shadow: 0 4px 15px rgba(102, 126, 234, 0.5) !important; } /* Clear button override */ button[variant="secondary"] { background: rgba(255, 255, 255, 0.14) !important; backdrop-filter: blur(8px); border: 1px solid rgba(255, 255, 255, 0.25) !important; } .upload-section button { background: linear-gradient(135deg, #56ccf2 0%, #2f80ed 100%) !important; min-width: 165px !important; height: 45px !important; font-size: 0.9rem !important; border-radius: 12px !important; color: #ffffff !important; margin:auto; margin-bottom: 6px; } /* Align Upload button center horizontally */ .upload-section { display: flex; flex-direction: column; justify-content: center; align-items: center; } .compact-btn button { padding: 4px 0px !important; font-size: 0.85em !important; min-height: 4px !important; max-height: 14px !important; } .how-to-use .gr-markdown, .how-to-use .gr-markdown *, .how-to-use .prose, .how-to-use .prose *, .how-to-use p, .how-to-use li, .how-to-use h3{ color: white !important; } .how-to-use span{ color:black; } .how-to-use label{ color:black; } .how-to-use { background: rgba(255, 255, 255, 0.08); backdrop-filter: blur(14px) brightness(1.3); -webkit-backdrop-filter: blur(14px) brightness(1.3); border: 1px solid rgba(255, 255, 255, 0.18); border-radius: 20px; padding: 25px; margin: 20px 10px; box-shadow: 0px 8px 25px rgba(0, 0, 0, 0.3); } .how-to-use .gr-markdown h3 { margin-top: 0; font-size: 1.4rem; font-weight: 700; text-shadow: 0px 0px 8px rgba(255,255,255,0.3); } .how-to-use .gr-markdown li { padding: 4px 0; } .how-to-use li::marker { color: #ffffff !important; } .upload-btn{ padding: 0px ; background-color: #1E1C4B; } .single-btn { width: 190px; margin: auto; } .label-text label { color: white !important; } /* Dataframe styling */ .label-text .gr-dataframe, .label-text [data-testid="dataframe"], .label-text .dataframe, div[data-testid="dataframe"], .gr-dataframe { min-height: 600px !important; height: 600px !important; max-height: 600px !important; overflow-y: auto !important; border: 2px solid rgba(255, 255, 255, 0.3) !important; border-radius: 8px !important; background: rgba(0, 0, 0, 0.2) !important; width: 100% !important; display: block !important; margin: 10px 0 !important; } .label-text .gr-dataframe .wrap, .label-text [data-testid="dataframe"] .wrap, div[data-testid="dataframe"] .wrap, .gr-dataframe .wrap { min-height: 600px !important; height: 600px !important; max-height: 600px !important; overflow-y: auto !important; width: 100% !important; display: block !important; } .label-text table, .gr-dataframe table, [data-testid="dataframe"] table { width: 100% !important; background: rgba(255, 255, 255, 0.05) !important; color: white !important; font-size: 14px !important; } .label-text th, .gr-dataframe th, [data-testid="dataframe"] th { background: rgba(255, 255, 255, 0.15) !important; color: white !important; padding: 12px !important; font-weight: bold !important; } .label-text td, .gr-dataframe td, [data-testid="dataframe"] td { color: white !important; padding: 10px !important; border-bottom: 1px solid rgba(255, 255, 255, 0.1) !important; } .table-wrap.svelte-1bvc1.p0.no-wrap, .table-wrap.svelte-1bvc1p0.no-wrap, .table-wrap { display: block !important; visibility: visible !important; opacity: 1 !important; min-height: 300px !important; max-height: 400px !important; overflow-y: auto !important; overflow-x: auto !important; background: rgba(255, 255, 255, 0.05) !important; border: 1px solid rgba(255, 255, 255, 0.2) !important; border-radius: 8px !important; padding: 10px !important; } .table-wrap table { width: 100% !important; display: table !important; color: white !important; } .table-wrap::-webkit-scrollbar { width: 8px !important; height: 8px !important; } .table-wrap::-webkit-scrollbar-track { background: rgba(255, 255, 255, 0.1) !important; border-radius: 4px !important; } .table-wrap::-webkit-scrollbar-thumb { background: rgba(255, 255, 255, 0.3) !important; border-radius: 4px !important; } .table-wrap::-webkit-scrollbar-thumb:hover { background: rgba(255, 255, 255, 0.5) !important; } .wrap.svelte-1vmd51o{ color: #ffffff !important; } button.svelte-jqnyug { display: none !important; visibility: hidden !important; } * [data-testid="dataframe"], * .gr-dataframe, * .dataframe { display: block !important; visibility: visible !important; opacity: 1 !important; background: rgba(0, 0, 0, 0.6) !important; border: 3px solid #ffffff !important; min-height: 200px !important; } * [data-testid="dataframe"] *, * .gr-dataframe *, * .dataframe * { display: inherit !important; visibility: visible !important; opacity: 1 !important; color: white !important; } div#component-70.block.svelte-90oupt.hide-container { display: block !important; visibility: visible !important; opacity: 1 !important; background: rgba(255, 255, 255, 0.05) !important; border: 1px solid rgba(255, 255, 255, 0.3) !important; border-radius: 8px !important; padding: 10px !important; min-height: 300px !important; max-height: 400px !important; overflow-y: auto !important; } div#component-70 .gr-dataframe, div#component-70 [data-testid="dataframe"] { display: block !important; visibility: visible !important; background: transparent !important; } div#component-70 table { width: 100% !important; color: white !important; display: table !important; } div#component-70 td, div#component-70 th { color: white !important; padding: 8px !important; border-bottom: 1px solid rgba(255, 255, 255, 0.2) !important; } .orders-table, .complaints-table { width: 100% !important; color: white !important; border-collapse: collapse !important; } .orders-table th, .complaints-table th { background: rgba(255, 255, 255, 0.1) !important; color: white !important; padding: 10px !important; border: 1px solid rgba(255, 255, 255, 0.2) !important; } .orders-table td, .complaints-table td { color: white !important; padding: 8px !important; border: 1px solid rgba(255, 255, 255, 0.1) !important; } [data-testid="dataframe"] { display: block !important; visibility: visible !important; opacity: 1 !important; background: rgba(0, 0, 0, 0.6) !important; border: 3px solid #00ff00 !important; min-height: 250px !important; padding: 15px !important; margin: 15px 0 !important; } [data-testid="dataframe"] table { display: table !important; visibility: visible !important; width: 100% !important; color: white !important; background: transparent !important; } [data-testid="dataframe"] th { background: rgba(255, 255, 255, 0.2) !important; color: white !important; padding: 10px !important; border: 1px solid white !important; } [data-testid="dataframe"] td { color: white !important; padding: 8px !important; border: 1px solid rgba(255, 255, 255, 0.3) !important; background: rgba(0, 0, 0, 0.2) !important; } .gradio-container table tbody tr td[colspan="5"] { color: white !important; } .gradio-container .prose span.md.prose > h3 { color: white !important; } .gradio-container table thead tr th { color: white !important; } .gradio-container table tbody tr td { color: white !important; } """ # with gr.Blocks(css=custom_css) as demo: # gr.HTML( # f""" #
# #

SparkMart

#
#
#

# A conversational AI solution for e-commerce that automates product inquiries, # order management, and complaint resolution while maintaining customer context, # history, and secure evidence-based complaint handling. #

#

# Powered by SparkBrains #

#
# """ # ) # initial_session = int(uuid.uuid4().int % 1000000) # session_state = gr.State(value=initial_session) # with gr.Row(): # with gr.Column(scale=1, elem_classes="how-to-use"): # gr.Markdown(""" # ### How to Use: # 1. **Chat**: Talk to AI for product recommendations, place orders, or file complaints # 2. **My Complaints**: Track complaint status and view evidence # """) with gr.Blocks(css=custom_css) as demo: gr.HTML( f"""

SparkMart

A conversational AI solution for e-commerce that automates product inquiries, order management, and complaint resolution while maintaining customer context, history, and secure evidence-based complaint handling.

Powered by SparkBrains

""" ) # MUST BE ALIGNED WITH gr.HTML (same indentation) initial_session = int(uuid.uuid4().int % 1000000) session_state = gr.State(value=initial_session) with gr.Row(): with gr.Column(scale=1, elem_classes="how-to-use"): gr.Markdown(""" ### How to Use: 1. **Chat**: Talk to AI for product recommendations, place orders, or file complaints 2. **My Complaints**: Track complaint status and view evidence """) with gr.Column(scale=3): with gr.Tabs(): with gr.Tab("Chat with Support"): chatbot = gr.Chatbot(height=550) # CENTERED FILE UPLOAD BUTTON - placed right below chatbot with gr.Row(elem_classes="centered-file-container"): file_input = gr.File( file_types=["image", "video", ".jpg", ".jpeg", ".png", ".mp4", ".mov"], file_count="single", visible=False, elem_id="evidence-upload" ) msg_input = gr.Textbox( placeholder="Type your message here...", label="Your Message", lines=1 ) with gr.Row(elem_classes="compact-action-row"): send_btn = gr.Button("Send", variant="secondary", size="xs", scale=1, elem_classes="compact-btn") clear_btn = gr.Button("Clear", variant="secondary", size="xs", scale=1, elem_classes="compact-btn") def respond(message, chat_history, file_path, session_id): """Handle user message and bot response""" if not message or not message.strip(): return chat_history or [], session_id, None, "" try: bot_response, updated_session, file_visibility = chat_with_agent(message, file_path, session_id) # Ensure chat_history is a list if chat_history is None: chat_history = [] # Gradio 6.0 expects messages format with role and content chat_history.append({"role": "user", "content": message}) chat_history.append({"role": "assistant", "content": bot_response}) return chat_history, updated_session, file_visibility, "" except Exception as e: print(f"Error in respond function: {e}") import traceback traceback.print_exc() error_msg = f"I apologize, but I encountered an error: {str(e)}" if chat_history is None: chat_history = [] chat_history.append({"role": "user", "content": message}) chat_history.append({"role": "assistant", "content": error_msg}) return chat_history, session_id, None, "" send_btn.click( respond, inputs=[msg_input, chatbot, file_input, session_state], outputs=[chatbot, session_state, file_input, msg_input] ) msg_input.submit( respond, inputs=[msg_input, chatbot, file_input, session_state], outputs=[chatbot, session_state, file_input, msg_input] ) def clear_and_update(): new_session = int(uuid.uuid4().int % 1000000) return [], new_session, gr.update(visible=False) clear_btn.click( clear_and_update, outputs=[chatbot, session_state, file_input] ) with gr.Tab("My Complaints", elem_classes="label-text"): with gr.Column(): user_id_complaints = gr.Textbox( label="Enter Your Order ID", ) check_complaints_btn = gr.Button("View My Complaints", variant="primary", elem_classes='single-btn') gr.Markdown("### Your Complaints") complaints_output = gr.HTML( value="""
Order ID Product Complaint Evidence Files Date
Enter Order ID and click 'View My Complaints' to see your complaints
""" ) def handle_view_complaints(user_id): print(f"Complaints button clicked with user_id: {user_id}") result = check_complaints(user_id) print(f"Complaints function returned: {result}") if hasattr(result, 'empty') and result.empty: return """
Order ID Product Complaint Evidence Files Date
No complaints found for this Order ID
""" if hasattr(result, 'columns') and 'Message' in result.columns: message = result['Message'].iloc[0] return f"""
Order ID Product Complaint Evidence Files Date
{message}
""" if hasattr(result, 'columns') and 'Error' in result.columns: error_msg = result['Error'].iloc[0] return f"""
Order ID Product Complaint Evidence Files Date
āš ļø Error: {error_msg}
""" if hasattr(result, 'values') and hasattr(result, 'columns') and len(result.columns) >= 5: rows_html = "" for _, row in result.iterrows(): rows_html += f""" {row.iloc[0]} {row.iloc[1]} {row.iloc[2]} {row.iloc[3]} {row.iloc[4]} """ return f"""
{rows_html}
Order ID Product Complaint Evidence Files Date
""" return f"
{str(result)}
" check_complaints_btn.click( handle_view_complaints, inputs=[user_id_complaints], outputs=[complaints_output] ) if __name__ == "__main__": demo.launch( server_name="0.0.0.0", server_port=7860, share=False, debug=True )