File size: 6,883 Bytes
8ba5d9d 734767d 64235d1 8eaf76a 8ba5d9d feac34e 6b3e14e 0391cfb 6c01c87 734767d 6c01c87 b35944e 8eaf76a b35944e 8eaf76a 6c01c87 8ba5d9d ef0ee7c 8ba5d9d b35944e 8ba5d9d 566ad88 8eaf76a 8ba5d9d 734767d 6c01c87 566ad88 eabb1c0 566ad88 49805d5 566ad88 49805d5 566ad88 49805d5 f1718bf 49805d5 566ad88 938a3f9 566ad88 6b3e14e 269cb51 566ad88 b0cfb8c 566ad88 6b3e14e 566ad88 b0cfb8c 566ad88 b0cfb8c 7a0c5f8 566ad88 0391cfb 9431561 7a0c5f8 0391cfb 7a0c5f8 c2bc69f 3c086f7 b0cfb8c 566ad88 b0cfb8c 566ad88 e087162 566ad88 7a0c5f8 6b3e14e e087162 566ad88 734767d 6c01c87 09e2bc4 a0d91e2 6c01c87 734767d 566ad88 0391cfb f1718bf 566ad88 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 |
from asyncio.log import logger
import yaml
from pathlib import Path
import gradio as gr
import asyncio
from langchain_mcp_client import lc_mcp_exec
from dotenv import load_dotenv
import os
import base64
from memory_store import MemoryStore
import logging
# Load environment variables
load_dotenv()
VERSION = "0.0.1"
# ======================================= Load DB configs
def load_db_configs():
"""Load database configurations from configs.yaml"""
configs_path = Path("configs.yaml")
if not configs_path.exists():
raise FileNotFoundError("configs.yaml not found")
with open(configs_path) as f:
configs = yaml.safe_load(f)
return configs["db_configs"]
def image_to_base64_html(image_path, alt_text="Customer Status", width=600):
with open(image_path, "rb") as f:
encoded = base64.b64encode(f.read()).decode("utf-8")
return f'<img src="data:image/png;base64,{encoded}" alt="{alt_text}" style="width:{width}px; max-width:100%; display:block; margin:0 auto 24px auto; border-radius:12px; box-shadow:0 2px 12px rgba(0,0,0,0.08);" />'
# ====================================== Async-compatible wrapper
async def run_agent(request, history=None):
try:
# Process request using existing memory
response, messages = await lc_mcp_exec(request)
# Handle image processing
image_path = ""
load_dotenv()
PANDAS_EXPORTS_PATH = os.environ["PANDAS_EXPORTS_PATH"]
# Ensure the exports directory exists
os.makedirs(PANDAS_EXPORTS_PATH, exist_ok=True)
# Check for generated charts
generated_files = [f for f in os.listdir(PANDAS_EXPORTS_PATH)
if f.startswith("temp_chart_") and f.endswith(".png")]
if generated_files:
image_path = os.path.join(PANDAS_EXPORTS_PATH, generated_files[0])
try:
image_markdown = image_to_base64_html(image_path)
output = f"{image_markdown}\n\n{response}"
os.remove(image_path) # Clean up the image file
except Exception as e:
logger.error(f"Error processing image: {e}")
output = response
else:
output = response
return output
except Exception as e:
logger.error(f"Error in run_agent: {str(e)}", exc_info=True)
return f"Error: {str(e)}"
# ====================================== Gradio UI with history
LOGO_PATH = "resources/pialogo.png"
# CSS customizations
custom_css = """
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;600&display=swap');
body, .markdown-content, .container, .chat-container, .message-container {
font-family: 'Inter', Arial, sans-serif !important;
}
.container {
max-width: 2200px !important;
margin: auto;
padding: 20px;
}
.chat-container {
height: 1000px !important;
min-height: 1000px !important;
overflow-y: auto;
}
.message-container {
padding: 15px;
border-radius: 10px;
margin: 10px 0;
}
.markdown-content {
font-size: 18px;
line-height: 1.7;
}
button.svelte-lixn6qd {
display: none !important;
}
#component-16 {
display: none !important;
}
"""
xtheme = gr.themes.Soft()
with gr.Blocks(css=custom_css, theme=xtheme) as demo:
with gr.Row(elem_classes="container"):
# with gr.Column(scale=0.5):
# gr.Image(value=LOGO_PATH, height=100, show_label=False, show_download_button=False, show_fullscreen_button=False)
with gr.Column(scale=5):
gr.Markdown(
"""
<h1 style='text-align: center; margin-bottom: 1rem'>Talk to Your Data</h1>
<p style='text-align: center'>Ask questions about your database, analyze and visualize data.</p>
<p style='text-align: center; color: #666; font-size: 0.9em; margin-top: -0.5rem'>Version: {}</p>
""".format(VERSION)
)
with gr.Row(elem_classes="container"):
with gr.Column(scale=3):
chat = gr.ChatInterface(
fn=run_agent,
chatbot=gr.Chatbot(
height=1000,
show_label=False,
elem_classes="chat-container",
render_markdown=True,
type="messages"
),
textbox=gr.Textbox(
placeholder="Type your questions here...",
container=False,
scale=4
),
examples=[
"Describe the database",
"List all tables in the database",
"Show me the top 10 customers by ticket count and visualize it bar chart with different colors",
"Show distribution of tickets by status, use pie chart",
"List all tables with columns and data types",
"How many comments are there per ticket channel (email, chat, portal)? Also Visualize it as a pie chart",
"Delete ticket with id BR-90001",
"Show top 5 products with highest number of tickets in a bar chart, add legand, print product names vertically",
"How many customers are in each industry?",
"List the 5 most active agents by ticket count in 2025.",
"How many tickets were reopened at least once?",
"In which of the records, have the customer provided positive feedback as a result of the service they got?",
"Show the number of comments per ticket, grouped by channel (email, chat, portal), for tickets with comments. Visualize using box plot.",
"Display the number of tickets and average reopen count per customer, colored by industry, for customers with resolved tickets. Visualize using Scatter Plot."
],
save_history=True,
type="messages"
)
with gr.Column(scale=1):
with gr.Accordion("Example Questions", open=True):
gr.Markdown("""
- π Describe the database
- π List all tables in database
- π₯ Total number of customers
- π Top 10 customers by ticket count
- π Ticket count by status and visualize
- π Average ticket reopened
- π§Ή Clear memory : `/clear-cache`
""")
# TODO: maybe we can add a mcp tool to validate the results (those converted to DataFrame) to make sure the valid type is passed to the visualization tool by ReAct agent
if __name__ == "__main__":
demo.launch(
server_name="0.0.0.0",
# server_port=7860,
#share=True,
debug=True
)
|