Add useable demo to test functionality
#1
by nolanzandi - opened
- .gitignore +0 -4
- README.md +4 -4
- app.py +13 -192
- assets/styles.css +0 -198
- data_sources/__init__.py +1 -4
- data_sources/connect_doc_db.py +0 -36
- data_sources/connect_graphql.py +0 -148
- data_sources/connect_sql_db.py +0 -42
- data_sources/upload_file.py +17 -163
- functions/__init__.py +3 -16
- functions/chart_functions.py +0 -526
- functions/chat_functions.py +88 -161
- functions/query_functions.py +0 -229
- functions/sqlite_functions.py +35 -0
- functions/stat_functions.py +0 -285
- pipelines/__init__.py +3 -0
- pipelines/pipelines.py +91 -0
- requirements.txt +2 -16
- samples/online_retail_data.csv +0 -0
- samples/tb_illness_data.csv +0 -0
- temp/.gitignore +0 -2
- templates/data_file.py +0 -286
- templates/doc_db.py +0 -105
- templates/graphql.py +0 -110
- templates/sql_db.py +0 -102
- tools.py +54 -0
- tools/__init__.py +0 -0
- tools/chart_tools.py +0 -308
- tools/stats_tools.py +0 -130
- tools/tools.py +0 -130
- utils.py +0 -9
.gitignore
DELETED
|
@@ -1,4 +0,0 @@
|
|
| 1 |
-
__pycache__/
|
| 2 |
-
.gradio/
|
| 3 |
-
.env
|
| 4 |
-
temp/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
README.md
CHANGED
|
@@ -1,13 +1,13 @@
|
|
| 1 |
---
|
| 2 |
-
title: Virtual Data Analyst
|
| 3 |
emoji: 📈
|
| 4 |
colorFrom: pink
|
| 5 |
colorTo: blue
|
| 6 |
sdk: gradio
|
| 7 |
-
sdk_version: 5.
|
| 8 |
app_file: app.py
|
| 9 |
-
pinned:
|
| 10 |
-
short_description:
|
| 11 |
---
|
| 12 |
|
| 13 |
Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
|
|
|
|
| 1 |
---
|
| 2 |
+
title: Virtual Data Analyst Demo
|
| 3 |
emoji: 📈
|
| 4 |
colorFrom: pink
|
| 5 |
colorTo: blue
|
| 6 |
sdk: gradio
|
| 7 |
+
sdk_version: 5.14.0
|
| 8 |
app_file: app.py
|
| 9 |
+
pinned: false
|
| 10 |
+
short_description: demo of virtual data analyst project
|
| 11 |
---
|
| 12 |
|
| 13 |
Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
|
app.py
CHANGED
|
@@ -1,192 +1,13 @@
|
|
| 1 |
-
from
|
| 2 |
-
|
| 3 |
-
import
|
| 4 |
-
|
| 5 |
-
import
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
shutil.rmtree(dir_path)
|
| 15 |
-
message_dict[req.session_hash] = {}
|
| 16 |
-
api_key_store.pop(req.session_hash, None)
|
| 17 |
-
model_store.pop(req.session_hash, None)
|
| 18 |
-
|
| 19 |
-
def set_api_key(api_key, model, request: gr.Request):
|
| 20 |
-
api_key = api_key.strip()
|
| 21 |
-
if not api_key:
|
| 22 |
-
return (
|
| 23 |
-
gr.update(visible=True),
|
| 24 |
-
gr.update(visible=True, value="<p style='color:#b91c1c;text-align:center;margin:6px 0;font-size:14px;'>Please enter your API key.</p>"),
|
| 25 |
-
gr.update(visible=False),
|
| 26 |
-
)
|
| 27 |
-
api_key_store[request.session_hash] = api_key
|
| 28 |
-
model_store[request.session_hash] = model
|
| 29 |
-
provider = "Anthropic" if api_key.startswith("sk-ant-") else "OpenAI"
|
| 30 |
-
provider_icon = "fa-a" if provider == "Anthropic" else "fa-o"
|
| 31 |
-
badge_html = f"""
|
| 32 |
-
<div style="display:flex;flex-direction:column;align-items:center;gap:6px;padding:10px 0 4px;">
|
| 33 |
-
<div style="display:inline-flex;align-items:center;gap:10px;background:#f0fdf4;border:1px solid #86efac;
|
| 34 |
-
padding:8px 20px;border-radius:9999px;font-size:13px;font-weight:500;color:#15803d;
|
| 35 |
-
box-shadow:0 1px 3px rgba(0,0,0,0.06);">
|
| 36 |
-
<i class="fas fa-circle-check" style="font-size:14px;"></i>
|
| 37 |
-
<span>{provider}</span>
|
| 38 |
-
<span style="color:#86efac;">·</span>
|
| 39 |
-
<span style="font-weight:600;">{model}</span>
|
| 40 |
-
</div>
|
| 41 |
-
<p style="margin:0;font-size:11px;color:#9ca3af;letter-spacing:0.02em;">
|
| 42 |
-
Session active — use the button below to change
|
| 43 |
-
</p>
|
| 44 |
-
</div>
|
| 45 |
-
"""
|
| 46 |
-
return gr.update(visible=False), gr.update(visible=True, value=badge_html), gr.update(visible=True)
|
| 47 |
-
|
| 48 |
-
def show_api_form():
|
| 49 |
-
return gr.update(visible=True), gr.update(visible=False, value=""), gr.update(visible=False)
|
| 50 |
-
|
| 51 |
-
css = ".file_marker .large{min-height:50px !important;} .padding{padding:0;} .description_component{overflow:visible !important;}"
|
| 52 |
-
head = """<meta charset="UTF-8">
|
| 53 |
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| 54 |
-
<title>Virtual Data Analyst</title>
|
| 55 |
-
<!-- Tailwind CSS -->
|
| 56 |
-
<script src="https://cdn.tailwindcss.com"></script>
|
| 57 |
-
<!-- Google Fonts -->
|
| 58 |
-
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
|
| 59 |
-
<!-- Font Awesome -->
|
| 60 |
-
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css">
|
| 61 |
-
<!-- Custom Styles -->
|
| 62 |
-
<link rel="stylesheet" href="/gradio_api/file=assets/styles.css">
|
| 63 |
-
"""
|
| 64 |
-
|
| 65 |
-
theme = gr.themes.Base(primary_hue="sky", secondary_hue="slate", font=[gr.themes.GoogleFont("Inter"), "Inter", "sans-serif"]).set(
|
| 66 |
-
button_primary_background_fill="#3B82F6",
|
| 67 |
-
button_secondary_background_fill="#6B7280",
|
| 68 |
-
)
|
| 69 |
-
|
| 70 |
-
from pathlib import Path
|
| 71 |
-
gr.set_static_paths(paths=[Path.cwd().absolute() / "assets"])
|
| 72 |
-
|
| 73 |
-
_env_api_key = os.getenv("OPENAI_API_KEY", "")
|
| 74 |
-
|
| 75 |
-
OPENAI_MODELS = [
|
| 76 |
-
"gpt-4.1", "gpt-4.1-mini", "gpt-4.1-nano",
|
| 77 |
-
"gpt-4o", "gpt-4o-mini",
|
| 78 |
-
"o3-mini", "o4-mini",
|
| 79 |
-
"gpt-5.4-mini", "gpt-5.4", "gpt-5.5",
|
| 80 |
-
]
|
| 81 |
-
ANTHROPIC_MODELS = [
|
| 82 |
-
"claude-sonnet-4-6",
|
| 83 |
-
"claude-opus-4-8",
|
| 84 |
-
"claude-haiku-4-5-20251001",
|
| 85 |
-
]
|
| 86 |
-
|
| 87 |
-
def update_models(api_key):
|
| 88 |
-
if api_key.strip().startswith("sk-ant-"):
|
| 89 |
-
return gr.update(choices=ANTHROPIC_MODELS, value=ANTHROPIC_MODELS[0])
|
| 90 |
-
return gr.update(choices=OPENAI_MODELS, value=OPENAI_MODELS[0])
|
| 91 |
-
|
| 92 |
-
with gr.Blocks(theme=theme, css=css, head=head, delete_cache=(3600, 3600)) as demo:
|
| 93 |
-
|
| 94 |
-
with gr.Column(visible=True) as api_key_section:
|
| 95 |
-
gr.HTML("""
|
| 96 |
-
<div style="max-width:640px;margin:28px auto 12px;padding:22px 28px;
|
| 97 |
-
background:linear-gradient(135deg,#eff6ff 0%,#e0f2fe 100%);
|
| 98 |
-
border:1px solid #bfdbfe;border-radius:14px;
|
| 99 |
-
box-shadow:0 2px 8px rgba(59,130,246,0.08);">
|
| 100 |
-
<div style="display:flex;align-items:flex-start;gap:16px;">
|
| 101 |
-
<div style="width:42px;height:42px;flex-shrink:0;background:#3B82F6;
|
| 102 |
-
border-radius:10px;display:flex;align-items:center;
|
| 103 |
-
justify-content:center;box-shadow:0 2px 6px rgba(59,130,246,0.35);">
|
| 104 |
-
<i class="fas fa-key" style="color:white;font-size:16px;"></i>
|
| 105 |
-
</div>
|
| 106 |
-
<div>
|
| 107 |
-
<h3 style="color:#1e40af;margin:0 0 6px;font-size:16px;font-weight:700;letter-spacing:-0.01em;">
|
| 108 |
-
Get Started
|
| 109 |
-
</h3>
|
| 110 |
-
<p style="color:#3730a3;font-size:13.5px;margin:0;line-height:1.6;">
|
| 111 |
-
Enter your <strong>OpenAI</strong>
|
| 112 |
-
(<code style="background:rgba(255,255,255,0.7);padding:1px 6px;border-radius:4px;font-size:12px;">sk-...</code>)
|
| 113 |
-
or <strong>Anthropic</strong>
|
| 114 |
-
(<code style="background:rgba(255,255,255,0.7);padding:1px 6px;border-radius:4px;font-size:12px;">sk-ant-...</code>)
|
| 115 |
-
API key. The model list updates automatically. Your key is held in memory only
|
| 116 |
-
and cleared when you leave — never saved or shared.
|
| 117 |
-
</p>
|
| 118 |
-
</div>
|
| 119 |
-
</div>
|
| 120 |
-
</div>
|
| 121 |
-
""")
|
| 122 |
-
with gr.Row(equal_height=True):
|
| 123 |
-
api_key_input = gr.Textbox(
|
| 124 |
-
label="API Key",
|
| 125 |
-
placeholder="sk-proj-... or sk-ant-api03-...",
|
| 126 |
-
type="password",
|
| 127 |
-
value=_env_api_key,
|
| 128 |
-
scale=4,
|
| 129 |
-
)
|
| 130 |
-
model_dropdown = gr.Dropdown(
|
| 131 |
-
label="Model",
|
| 132 |
-
choices=OPENAI_MODELS,
|
| 133 |
-
value=OPENAI_MODELS[0],
|
| 134 |
-
scale=2,
|
| 135 |
-
)
|
| 136 |
-
api_key_btn = gr.Button("Set API Key", variant="primary", scale=1, min_width=120)
|
| 137 |
-
|
| 138 |
-
api_key_status = gr.HTML("", visible=False)
|
| 139 |
-
change_key_btn = gr.Button("🔑 Change Key / Model", variant="secondary", visible=False, size="sm")
|
| 140 |
-
|
| 141 |
-
api_key_input.change(fn=update_models, inputs=api_key_input, outputs=model_dropdown)
|
| 142 |
-
api_key_btn.click(
|
| 143 |
-
fn=set_api_key,
|
| 144 |
-
inputs=[api_key_input, model_dropdown],
|
| 145 |
-
outputs=[api_key_section, api_key_status, change_key_btn],
|
| 146 |
-
)
|
| 147 |
-
change_key_btn.click(fn=show_api_form, outputs=[api_key_section, api_key_status, change_key_btn])
|
| 148 |
-
|
| 149 |
-
header = gr.HTML("""
|
| 150 |
-
<header class="max-w-4xl mx-auto mb-12 text-center">
|
| 151 |
-
<h1 class="text-4xl font-bold text-gray-900 mb-4">Virtual Data Analyst</h1>
|
| 152 |
-
<p class="text-lg text-gray-600 mb-6">
|
| 153 |
-
A powerful tool for data analysis, visualizations, and insights
|
| 154 |
-
</p>
|
| 155 |
-
</header>
|
| 156 |
-
<main class="max-w-4xl mx-auto">
|
| 157 |
-
<div class="mt-12 grid md:grid-cols-3 gap-6" style="margin-bottom:3px !important;">
|
| 158 |
-
<div class="feature-card bg-white p-6 rounded-lg shadow-md">
|
| 159 |
-
<i class="feature-icon fas fa-chart-line text-primary text-2xl mb-4"></i>
|
| 160 |
-
<h3 class="font-semibold text-gray-800 mb-2">Advanced Analytics</h3>
|
| 161 |
-
<p class="text-gray-600 text-sm">Run SQL queries, perform regressions, and analyze results with ease</p>
|
| 162 |
-
</div>
|
| 163 |
-
<div class="feature-card bg-white p-6 rounded-lg shadow-md">
|
| 164 |
-
<i class="feature-icon fas fa-chart-pie text-primary text-2xl mb-4"></i>
|
| 165 |
-
<h3 class="font-semibold text-gray-800 mb-2">Rich Visualizations</h3>
|
| 166 |
-
<p class="text-gray-600 text-sm">Create scatter plots, line charts, pie charts, and more</p>
|
| 167 |
-
</div>
|
| 168 |
-
<div class="feature-card bg-white p-6 rounded-lg shadow-md">
|
| 169 |
-
<i class="feature-icon fas fa-magic text-primary text-2xl mb-4"></i>
|
| 170 |
-
<h3 class="font-semibold text-gray-800 mb-2">Automated Insights</h3>
|
| 171 |
-
<p class="text-gray-600 text-sm">Get instant insights and recommendations for your data</p>
|
| 172 |
-
</div>
|
| 173 |
-
</div>
|
| 174 |
-
</main>""")
|
| 175 |
-
|
| 176 |
-
with gr.Tab("📄 Data File"):
|
| 177 |
-
data_file.demo.render()
|
| 178 |
-
with gr.Tab("🗄 SQL Database"):
|
| 179 |
-
sql_db.demo.render()
|
| 180 |
-
with gr.Tab("🍃 MongoDB"):
|
| 181 |
-
doc_db.demo.render()
|
| 182 |
-
with gr.Tab("⚡ GraphQL API"):
|
| 183 |
-
graphql.demo.render()
|
| 184 |
-
|
| 185 |
-
footer = gr.HTML("""
|
| 186 |
-
<footer class="max-w-4xl mx-auto mt-12 text-center text-gray-500 text-sm">
|
| 187 |
-
<p>This application is under active development. For bugs or feedback, please open a discussion in the community tab.</p>
|
| 188 |
-
</footer>""")
|
| 189 |
-
|
| 190 |
-
demo.unload(delete_db)
|
| 191 |
-
|
| 192 |
-
demo.launch(debug=True, allowed_paths=["temp/", "assets/"])
|
|
|
|
| 1 |
+
from functions import demo
|
| 2 |
+
|
| 3 |
+
import os
|
| 4 |
+
from getpass import getpass
|
| 5 |
+
from dotenv import load_dotenv
|
| 6 |
+
|
| 7 |
+
load_dotenv()
|
| 8 |
+
|
| 9 |
+
if "OPENAI_API_KEY" not in os.environ:
|
| 10 |
+
os.environ["OPENAI_API_KEY"] = getpass("Enter OpenAI API key:")
|
| 11 |
+
|
| 12 |
+
## Uncomment the line below to launch the chat app with UI
|
| 13 |
+
demo.launch(debug=True)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
assets/styles.css
DELETED
|
@@ -1,198 +0,0 @@
|
|
| 1 |
-
/* Loading Animation */
|
| 2 |
-
.loading-spinner {
|
| 3 |
-
display: none;
|
| 4 |
-
width: 50px;
|
| 5 |
-
height: 50px;
|
| 6 |
-
border: 5px solid #f3f3f3;
|
| 7 |
-
border-top: 5px solid #3B82F6;
|
| 8 |
-
border-radius: 50%;
|
| 9 |
-
animation: spin 1s linear infinite;
|
| 10 |
-
margin: 0 auto;
|
| 11 |
-
}
|
| 12 |
-
|
| 13 |
-
@keyframes spin {
|
| 14 |
-
0% { transform: rotate(0deg); }
|
| 15 |
-
100% { transform: rotate(360deg); }
|
| 16 |
-
}
|
| 17 |
-
|
| 18 |
-
/* File Upload Progress */
|
| 19 |
-
.progress-bar {
|
| 20 |
-
width: 100%;
|
| 21 |
-
height: 6px;
|
| 22 |
-
background-color: #e5e7eb;
|
| 23 |
-
border-radius: 3px;
|
| 24 |
-
overflow: hidden;
|
| 25 |
-
display: none;
|
| 26 |
-
margin: 1rem auto;
|
| 27 |
-
max-width: 300px;
|
| 28 |
-
}
|
| 29 |
-
|
| 30 |
-
.progress-bar-fill {
|
| 31 |
-
height: 100%;
|
| 32 |
-
background-color: #3B82F6;
|
| 33 |
-
width: 0%;
|
| 34 |
-
transition: width 0.3s ease;
|
| 35 |
-
}
|
| 36 |
-
|
| 37 |
-
/* Tooltip */
|
| 38 |
-
.tooltip {
|
| 39 |
-
position: relative;
|
| 40 |
-
display: inline-block;
|
| 41 |
-
}
|
| 42 |
-
|
| 43 |
-
.tooltip .tooltip-text {
|
| 44 |
-
visibility: hidden;
|
| 45 |
-
background-color: #1f2937;
|
| 46 |
-
color: white;
|
| 47 |
-
text-align: center;
|
| 48 |
-
padding: 8px 12px;
|
| 49 |
-
border-radius: 6px;
|
| 50 |
-
position: absolute;
|
| 51 |
-
z-index: 1;
|
| 52 |
-
bottom: 125%;
|
| 53 |
-
left: 50%;
|
| 54 |
-
transform: translateX(-50%);
|
| 55 |
-
opacity: 0;
|
| 56 |
-
transition: opacity 0.3s;
|
| 57 |
-
font-size: 0.875rem;
|
| 58 |
-
white-space: nowrap;
|
| 59 |
-
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
|
| 60 |
-
}
|
| 61 |
-
|
| 62 |
-
.tooltip:hover .tooltip-text {
|
| 63 |
-
visibility: visible;
|
| 64 |
-
opacity: 1;
|
| 65 |
-
}
|
| 66 |
-
|
| 67 |
-
/* File Type Icons */
|
| 68 |
-
.file-type-icon {
|
| 69 |
-
font-size: 1.5rem;
|
| 70 |
-
margin-right: 0.5rem;
|
| 71 |
-
color: #3B82F6;
|
| 72 |
-
}
|
| 73 |
-
|
| 74 |
-
/* Success Animation */
|
| 75 |
-
@keyframes checkmark {
|
| 76 |
-
0% { transform: scale(0); opacity: 0; }
|
| 77 |
-
50% { transform: scale(1.2); opacity: 0.8; }
|
| 78 |
-
100% { transform: scale(1); opacity: 1; }
|
| 79 |
-
}
|
| 80 |
-
|
| 81 |
-
.success-checkmark {
|
| 82 |
-
display: none;
|
| 83 |
-
color: #10B981;
|
| 84 |
-
animation: checkmark 0.5s ease-in-out forwards;
|
| 85 |
-
}
|
| 86 |
-
|
| 87 |
-
/* Sample Data Cards */
|
| 88 |
-
.sample-btn {
|
| 89 |
-
transition: all 0.3s ease;
|
| 90 |
-
position: relative;
|
| 91 |
-
overflow: hidden;
|
| 92 |
-
background: linear-gradient(135deg, #3B82F6, #0ea5e9) !important;
|
| 93 |
-
}
|
| 94 |
-
|
| 95 |
-
.sample-btn::after {
|
| 96 |
-
content: '';
|
| 97 |
-
position: absolute;
|
| 98 |
-
top: 0;
|
| 99 |
-
left: 0;
|
| 100 |
-
width: 100%;
|
| 101 |
-
height: 100%;
|
| 102 |
-
background: linear-gradient(rgba(255,255,255,0.12), rgba(255,255,255,0));
|
| 103 |
-
transform: translateY(-100%);
|
| 104 |
-
transition: transform 0.3s ease;
|
| 105 |
-
}
|
| 106 |
-
|
| 107 |
-
.sample-btn:hover::after {
|
| 108 |
-
transform: translateY(0);
|
| 109 |
-
}
|
| 110 |
-
|
| 111 |
-
.sample-btn:hover {
|
| 112 |
-
transform: translateY(-2px);
|
| 113 |
-
box-shadow: 0 8px 20px rgba(59,130,246,0.3);
|
| 114 |
-
}
|
| 115 |
-
|
| 116 |
-
/* Status badge fade-in */
|
| 117 |
-
@keyframes fadeSlideIn {
|
| 118 |
-
from { opacity: 0; transform: translateY(-6px); }
|
| 119 |
-
to { opacity: 1; transform: translateY(0); }
|
| 120 |
-
}
|
| 121 |
-
|
| 122 |
-
.api-status-badge {
|
| 123 |
-
animation: fadeSlideIn 0.35s ease forwards;
|
| 124 |
-
}
|
| 125 |
-
|
| 126 |
-
/* Drop Zone Enhancements */
|
| 127 |
-
.drop-zone {
|
| 128 |
-
transition: all 0.3s ease;
|
| 129 |
-
position: relative;
|
| 130 |
-
overflow: hidden;
|
| 131 |
-
}
|
| 132 |
-
|
| 133 |
-
.drop-zone::before {
|
| 134 |
-
position: absolute;
|
| 135 |
-
top: 0;
|
| 136 |
-
left: 0;
|
| 137 |
-
right: 0;
|
| 138 |
-
bottom: 0;
|
| 139 |
-
border-radius: 8px;
|
| 140 |
-
border: 2px dashed #3B82F6;
|
| 141 |
-
opacity: 0;
|
| 142 |
-
transition: opacity 0.3s ease;
|
| 143 |
-
}
|
| 144 |
-
|
| 145 |
-
.drop-zone:hover::before {
|
| 146 |
-
opacity: 1;
|
| 147 |
-
}
|
| 148 |
-
|
| 149 |
-
/* File Info Card */
|
| 150 |
-
#fileInfo {
|
| 151 |
-
background: linear-gradient(to right, #f8fafc, #f1f5f9);
|
| 152 |
-
border: 1px solid #e2e8f0;
|
| 153 |
-
transition: all 0.3s ease;
|
| 154 |
-
}
|
| 155 |
-
|
| 156 |
-
#fileInfo:hover {
|
| 157 |
-
transform: translateY(-2px);
|
| 158 |
-
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
|
| 159 |
-
}
|
| 160 |
-
|
| 161 |
-
/* Features Section */
|
| 162 |
-
.feature-card {
|
| 163 |
-
transition: all 0.3s ease;
|
| 164 |
-
}
|
| 165 |
-
|
| 166 |
-
.feature-card:hover {
|
| 167 |
-
transform: translateY(-2px);
|
| 168 |
-
box-shadow: 0 8px 15px rgba(0,0,0,0.1);
|
| 169 |
-
}
|
| 170 |
-
|
| 171 |
-
.feature-icon {
|
| 172 |
-
transition: all 0.3s ease;
|
| 173 |
-
}
|
| 174 |
-
|
| 175 |
-
.feature-card:hover .feature-icon {
|
| 176 |
-
transform: scale(1.1);
|
| 177 |
-
color: #2563eb;
|
| 178 |
-
}
|
| 179 |
-
|
| 180 |
-
@media only screen and (max-width: 600px) {
|
| 181 |
-
.feature-card p {grid-column: 1/3;}
|
| 182 |
-
.feature-card i, .feature-card h3 {text-align: center;}
|
| 183 |
-
.feature-card {
|
| 184 |
-
display: grid;
|
| 185 |
-
grid-template-columns: 1fr 2fr;
|
| 186 |
-
align-items: baseline;
|
| 187 |
-
}
|
| 188 |
-
}
|
| 189 |
-
|
| 190 |
-
dialog {
|
| 191 |
-
margin: 10% auto;
|
| 192 |
-
width: 80%;
|
| 193 |
-
max-width: 350px;
|
| 194 |
-
background-color: #fff;
|
| 195 |
-
padding: 34px;
|
| 196 |
-
border: 0;
|
| 197 |
-
border-radius: 5px;
|
| 198 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
data_sources/__init__.py
CHANGED
|
@@ -1,6 +1,3 @@
|
|
| 1 |
from .upload_file import process_data_upload
|
| 2 |
-
from .connect_sql_db import connect_sql_db
|
| 3 |
-
from .connect_doc_db import connect_doc_db
|
| 4 |
-
from .connect_graphql import connect_graphql
|
| 5 |
|
| 6 |
-
__all__ = ["process_data_upload"
|
|
|
|
| 1 |
from .upload_file import process_data_upload
|
|
|
|
|
|
|
|
|
|
| 2 |
|
| 3 |
+
__all__ = ["process_data_upload"]
|
data_sources/connect_doc_db.py
DELETED
|
@@ -1,36 +0,0 @@
|
|
| 1 |
-
from pymongo import MongoClient
|
| 2 |
-
import os
|
| 3 |
-
from utils import TEMP_DIR
|
| 4 |
-
from pymongo_schema.extract import extract_pymongo_client_schema
|
| 5 |
-
|
| 6 |
-
def connect_doc_db(connection_string, nosql_db_name, session_hash):
|
| 7 |
-
try:
|
| 8 |
-
# Create a MongoClient object
|
| 9 |
-
client = MongoClient(connection_string)
|
| 10 |
-
print("Connected to NoSQL Mongo DB")
|
| 11 |
-
|
| 12 |
-
# Access a database
|
| 13 |
-
db = client[nosql_db_name]
|
| 14 |
-
|
| 15 |
-
collection_names = db.list_collection_names()
|
| 16 |
-
|
| 17 |
-
print(collection_names)
|
| 18 |
-
|
| 19 |
-
schema = extract_pymongo_client_schema(client)
|
| 20 |
-
|
| 21 |
-
# Close the connection
|
| 22 |
-
if 'client' in locals() and client:
|
| 23 |
-
client.close()
|
| 24 |
-
print("MongoDB Connection closed.")
|
| 25 |
-
|
| 26 |
-
session_path = 'doc_db'
|
| 27 |
-
|
| 28 |
-
dir_path = TEMP_DIR / str(session_hash) / str(session_path)
|
| 29 |
-
os.makedirs(dir_path, exist_ok=True)
|
| 30 |
-
|
| 31 |
-
return ["success","<p style='color:green;text-align:center;font-size:18px;'>Document database connected successful</p>", collection_names, schema]
|
| 32 |
-
except Exception as e:
|
| 33 |
-
print("DocDB CONNECTION ERROR")
|
| 34 |
-
print(e)
|
| 35 |
-
return ["error",f"<p style='color:red;text-align:center;font-size:18px;font-weight:bold;'>ERROR: {e}</p>"]
|
| 36 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
data_sources/connect_graphql.py
DELETED
|
@@ -1,148 +0,0 @@
|
|
| 1 |
-
import requests
|
| 2 |
-
import certifi
|
| 3 |
-
import os
|
| 4 |
-
import json
|
| 5 |
-
from utils import TEMP_DIR
|
| 6 |
-
|
| 7 |
-
def connect_graphql(graphql_url, api_token, graphql_token_header, session_hash):
|
| 8 |
-
try:
|
| 9 |
-
# Create the GraphQL Introspection Query
|
| 10 |
-
query = """
|
| 11 |
-
query IntrospectionQuery {
|
| 12 |
-
__schema {
|
| 13 |
-
queryType { name }
|
| 14 |
-
mutationType { name }
|
| 15 |
-
subscriptionType { name }
|
| 16 |
-
types {
|
| 17 |
-
...FullType
|
| 18 |
-
}
|
| 19 |
-
directives {
|
| 20 |
-
name
|
| 21 |
-
description
|
| 22 |
-
locations
|
| 23 |
-
args {
|
| 24 |
-
...InputValue
|
| 25 |
-
}
|
| 26 |
-
}
|
| 27 |
-
}
|
| 28 |
-
}
|
| 29 |
-
fragment FullType on __Type {
|
| 30 |
-
kind
|
| 31 |
-
name
|
| 32 |
-
description
|
| 33 |
-
fields(includeDeprecated: true) {
|
| 34 |
-
name
|
| 35 |
-
description
|
| 36 |
-
args {
|
| 37 |
-
...InputValue
|
| 38 |
-
}
|
| 39 |
-
type {
|
| 40 |
-
...TypeRef
|
| 41 |
-
}
|
| 42 |
-
isDeprecated
|
| 43 |
-
deprecationReason
|
| 44 |
-
}
|
| 45 |
-
inputFields {
|
| 46 |
-
...InputValue
|
| 47 |
-
}
|
| 48 |
-
interfaces {
|
| 49 |
-
...TypeRef
|
| 50 |
-
}
|
| 51 |
-
enumValues(includeDeprecated: true) {
|
| 52 |
-
name
|
| 53 |
-
description
|
| 54 |
-
isDeprecated
|
| 55 |
-
deprecationReason
|
| 56 |
-
}
|
| 57 |
-
possibleTypes {
|
| 58 |
-
...TypeRef
|
| 59 |
-
}
|
| 60 |
-
}
|
| 61 |
-
fragment InputValue on __InputValue {
|
| 62 |
-
name
|
| 63 |
-
description
|
| 64 |
-
type { ...TypeRef }
|
| 65 |
-
defaultValue
|
| 66 |
-
}
|
| 67 |
-
fragment TypeRef on __Type {
|
| 68 |
-
kind
|
| 69 |
-
name
|
| 70 |
-
ofType {
|
| 71 |
-
kind
|
| 72 |
-
name
|
| 73 |
-
ofType {
|
| 74 |
-
kind
|
| 75 |
-
name
|
| 76 |
-
ofType {
|
| 77 |
-
kind
|
| 78 |
-
name
|
| 79 |
-
ofType {
|
| 80 |
-
kind
|
| 81 |
-
name
|
| 82 |
-
ofType {
|
| 83 |
-
kind
|
| 84 |
-
name
|
| 85 |
-
ofType {
|
| 86 |
-
kind
|
| 87 |
-
name
|
| 88 |
-
ofType {
|
| 89 |
-
kind
|
| 90 |
-
name
|
| 91 |
-
}
|
| 92 |
-
}
|
| 93 |
-
}
|
| 94 |
-
}
|
| 95 |
-
}
|
| 96 |
-
}
|
| 97 |
-
}
|
| 98 |
-
}
|
| 99 |
-
"""
|
| 100 |
-
print("Connecting to GraphQL Endpoint")
|
| 101 |
-
|
| 102 |
-
# Access a database
|
| 103 |
-
headers = {"Content-Type": "application/json"}
|
| 104 |
-
if graphql_token_header and api_token:
|
| 105 |
-
headers[graphql_token_header] = api_token
|
| 106 |
-
response = requests.post(graphql_url, headers=headers, json={"query": query},
|
| 107 |
-
verify=certifi.where())
|
| 108 |
-
response.raise_for_status()
|
| 109 |
-
|
| 110 |
-
introspection_result = response.json()
|
| 111 |
-
|
| 112 |
-
client_schema = introspection_result["data"]["__schema"]
|
| 113 |
-
|
| 114 |
-
#Generate the list of types
|
| 115 |
-
type_names_query = """
|
| 116 |
-
query IntrospectionQuery {
|
| 117 |
-
__schema {
|
| 118 |
-
types {
|
| 119 |
-
name
|
| 120 |
-
}
|
| 121 |
-
}
|
| 122 |
-
}
|
| 123 |
-
"""
|
| 124 |
-
types_response = requests.post(graphql_url, headers=headers, json={"query": type_names_query},
|
| 125 |
-
verify=certifi.where())
|
| 126 |
-
|
| 127 |
-
types_response_results =types_response.json()
|
| 128 |
-
|
| 129 |
-
types_names = types_response_results["data"]
|
| 130 |
-
|
| 131 |
-
type_names = []
|
| 132 |
-
for name in types_names["__schema"]["types"]:
|
| 133 |
-
type_names.append(name["name"])
|
| 134 |
-
|
| 135 |
-
session_path = 'graphql'
|
| 136 |
-
|
| 137 |
-
dir_path = TEMP_DIR / str(session_hash) / str(session_path)
|
| 138 |
-
os.makedirs(dir_path, exist_ok=True)
|
| 139 |
-
|
| 140 |
-
with open(f'{dir_path}/schema.json', 'w') as fp:
|
| 141 |
-
json.dump(client_schema, fp, indent=2)
|
| 142 |
-
|
| 143 |
-
return ["success","<p style='color:green;text-align:center;font-size:18px;'>GraphQL API connected successful</p>", type_names]
|
| 144 |
-
except Exception as e:
|
| 145 |
-
print("GraphQL CONNECTION ERROR")
|
| 146 |
-
print(e)
|
| 147 |
-
return ["error",f"<p style='color:red;text-align:center;font-size:18px;font-weight:bold;'>ERROR: {e}</p>"]
|
| 148 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
data_sources/connect_sql_db.py
DELETED
|
@@ -1,42 +0,0 @@
|
|
| 1 |
-
import psycopg2
|
| 2 |
-
import os
|
| 3 |
-
from utils import TEMP_DIR
|
| 4 |
-
|
| 5 |
-
def connect_sql_db(url, sql_user, sql_port, sql_pass, sql_db_name, session_hash):
|
| 6 |
-
try:
|
| 7 |
-
conn = psycopg2.connect(
|
| 8 |
-
database=sql_db_name,
|
| 9 |
-
user=sql_user,
|
| 10 |
-
password=sql_pass,
|
| 11 |
-
host=url, # e.g., "localhost" or an IP address
|
| 12 |
-
port=sql_port # default is 5432
|
| 13 |
-
)
|
| 14 |
-
print("Connected to PostgreSQL")
|
| 15 |
-
|
| 16 |
-
# Create a cursor object to execute SQL queries
|
| 17 |
-
cur = conn.cursor()
|
| 18 |
-
# Example: Execute a query
|
| 19 |
-
cur.execute("""SELECT table_name FROM information_schema.tables WHERE table_schema = 'public'""")
|
| 20 |
-
table_tuples = cur.fetchall()
|
| 21 |
-
table_names = []
|
| 22 |
-
for table in table_tuples:
|
| 23 |
-
table_names.append(table[0])
|
| 24 |
-
|
| 25 |
-
print(table_names)
|
| 26 |
-
|
| 27 |
-
# Close the cursor and connection
|
| 28 |
-
cur.close()
|
| 29 |
-
conn.close()
|
| 30 |
-
print("Connection closed.")
|
| 31 |
-
|
| 32 |
-
session_path = 'sql'
|
| 33 |
-
|
| 34 |
-
dir_path = TEMP_DIR / str(session_hash) / str(session_path)
|
| 35 |
-
os.makedirs(dir_path, exist_ok=True)
|
| 36 |
-
|
| 37 |
-
return ["success","<p style='color:green;text-align:center;font-size:18px;'>SQL database connected successful</p>", table_names]
|
| 38 |
-
except Exception as e:
|
| 39 |
-
print("SQL DB CONNECTION ERROR")
|
| 40 |
-
print(e)
|
| 41 |
-
return ["error",f"<p style='color:red;text-align:center;font-size:18px;font-weight:bold;'>ERROR: {e}</p>"]
|
| 42 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
data_sources/upload_file.py
CHANGED
|
@@ -1,167 +1,21 @@
|
|
| 1 |
import pandas as pd
|
| 2 |
import sqlite3
|
| 3 |
-
import csv
|
| 4 |
-
import json
|
| 5 |
-
import time
|
| 6 |
-
import os
|
| 7 |
-
import re
|
| 8 |
-
from utils import TEMP_DIR
|
| 9 |
-
|
| 10 |
-
def is_file_done_saving(file_path):
|
| 11 |
-
try:
|
| 12 |
-
with open(file_path, 'r') as f:
|
| 13 |
-
contents = f
|
| 14 |
-
|
| 15 |
-
if contents:
|
| 16 |
-
return True
|
| 17 |
-
else:
|
| 18 |
-
return False
|
| 19 |
-
except PermissionError:
|
| 20 |
-
return False
|
| 21 |
-
|
| 22 |
-
def get_delimiter(file_path, bytes = 4096):
|
| 23 |
-
sniffer = csv.Sniffer()
|
| 24 |
-
data = open(file_path, "r").read(bytes)
|
| 25 |
-
delimiter = sniffer.sniff(data).delimiter
|
| 26 |
-
return delimiter
|
| 27 |
-
|
| 28 |
-
def read_file(file):
|
| 29 |
-
if file.endswith(('.csv', '.tsv', '.txt')) :
|
| 30 |
-
df = pd.read_csv(file, sep=get_delimiter(file))
|
| 31 |
-
elif file.endswith('.json'):
|
| 32 |
-
with open(file, 'r') as f:
|
| 33 |
-
contents = json.load(f)
|
| 34 |
-
df = pd.json_normalize(contents)
|
| 35 |
-
elif file.endswith('.ndjson'):
|
| 36 |
-
with open(file, 'r') as f:
|
| 37 |
-
contents = f.read()
|
| 38 |
-
data = [json.loads(str(item)) for item in contents.strip().split('\n')]
|
| 39 |
-
df = pd.json_normalize(data)
|
| 40 |
-
elif file.endswith('.xml'):
|
| 41 |
-
df = pd.read_xml(file)
|
| 42 |
-
elif file.endswith(('.xls','xlsx')):
|
| 43 |
-
df = pd.read_excel(file)
|
| 44 |
-
else:
|
| 45 |
-
raise ValueError(f'Unsupported filetype: {file}')
|
| 46 |
-
return df
|
| 47 |
|
| 48 |
def process_data_upload(data_file, session_hash):
|
| 49 |
-
|
| 50 |
-
|
| 51 |
-
|
| 52 |
-
|
| 53 |
-
|
| 54 |
-
|
| 55 |
-
|
| 56 |
-
|
| 57 |
-
|
| 58 |
-
|
| 59 |
-
|
| 60 |
-
|
| 61 |
-
|
| 62 |
-
|
| 63 |
-
|
| 64 |
-
|
| 65 |
-
|
| 66 |
-
for column in df.columns:
|
| 67 |
-
if type(column) is str:
|
| 68 |
-
if "date" in column.lower() or "time" in column.lower():
|
| 69 |
-
try:
|
| 70 |
-
df[column] = pd.to_datetime(df[column])
|
| 71 |
-
except:
|
| 72 |
-
pass
|
| 73 |
-
if 'year' in column.lower():
|
| 74 |
-
try:
|
| 75 |
-
df[column] = pd.to_datetime(df[column], format='%Y')
|
| 76 |
-
except:
|
| 77 |
-
pass
|
| 78 |
-
if df[column].dtype == 'object' and isinstance(df[column].iloc[0], list):
|
| 79 |
-
df[column] = df[column].explode()
|
| 80 |
-
|
| 81 |
-
session_path = 'file_upload'
|
| 82 |
-
|
| 83 |
-
dir_path = TEMP_DIR / str(session_hash) / str(session_path)
|
| 84 |
-
os.makedirs(dir_path, exist_ok=True)
|
| 85 |
-
|
| 86 |
-
connection = sqlite3.connect(f'{dir_path}/data_source.db')
|
| 87 |
-
print("Opened database successfully")
|
| 88 |
-
|
| 89 |
-
df.to_sql('data_source', connection, if_exists='replace', index = False)
|
| 90 |
-
|
| 91 |
-
cur=connection.execute('select * from data_source')
|
| 92 |
-
columns = [i[0] for i in cur.description]
|
| 93 |
-
print(columns)
|
| 94 |
-
|
| 95 |
-
connection.commit()
|
| 96 |
-
connection.close()
|
| 97 |
-
|
| 98 |
-
missing_per_col = {col: int(df[col].isnull().sum()) for col in df.columns}
|
| 99 |
-
total_missing = sum(missing_per_col.values())
|
| 100 |
-
|
| 101 |
-
def _simplify_dtype(d):
|
| 102 |
-
s = str(d)
|
| 103 |
-
if 'int' in s: return 'Integer'
|
| 104 |
-
if 'float' in s: return 'Float'
|
| 105 |
-
if 'datetime' in s: return 'DateTime'
|
| 106 |
-
if 'bool' in s: return 'Boolean'
|
| 107 |
-
return 'Text'
|
| 108 |
-
|
| 109 |
-
dtypes = {col: _simplify_dtype(df[col].dtype) for col in df.columns}
|
| 110 |
-
|
| 111 |
-
preview = []
|
| 112 |
-
for _, row in df.head(5).iterrows():
|
| 113 |
-
row_vals = []
|
| 114 |
-
for v in row:
|
| 115 |
-
try:
|
| 116 |
-
row_vals.append('' if pd.isna(v) else str(v)[:60])
|
| 117 |
-
except Exception:
|
| 118 |
-
row_vals.append(str(v)[:60])
|
| 119 |
-
preview.append(row_vals)
|
| 120 |
-
|
| 121 |
-
duplicate_rows = int(df.duplicated().sum())
|
| 122 |
-
unique_counts = {col: int(df[col].nunique()) for col in df.columns}
|
| 123 |
-
|
| 124 |
-
col_stats = {}
|
| 125 |
-
for col in df.columns:
|
| 126 |
-
dtype_str = str(df[col].dtype)
|
| 127 |
-
try:
|
| 128 |
-
if 'int' in dtype_str or 'float' in dtype_str:
|
| 129 |
-
col_stats[col] = {
|
| 130 |
-
'type': 'numeric',
|
| 131 |
-
'min': float(df[col].min()),
|
| 132 |
-
'max': float(df[col].max()),
|
| 133 |
-
'mean': float(df[col].mean()),
|
| 134 |
-
}
|
| 135 |
-
elif 'datetime' in dtype_str:
|
| 136 |
-
col_stats[col] = {
|
| 137 |
-
'type': 'datetime',
|
| 138 |
-
'min': str(df[col].min())[:10],
|
| 139 |
-
'max': str(df[col].max())[:10],
|
| 140 |
-
}
|
| 141 |
-
except Exception:
|
| 142 |
-
pass
|
| 143 |
-
|
| 144 |
-
try:
|
| 145 |
-
file_size_bytes = os.path.getsize(data_file)
|
| 146 |
-
except Exception:
|
| 147 |
-
file_size_bytes = 0
|
| 148 |
-
|
| 149 |
-
stats = {
|
| 150 |
-
'num_rows': len(df),
|
| 151 |
-
'num_cols': len(df.columns),
|
| 152 |
-
'total_missing': total_missing,
|
| 153 |
-
'missing_per_col': missing_per_col,
|
| 154 |
-
'dtypes': dtypes,
|
| 155 |
-
'preview_cols': list(df.columns),
|
| 156 |
-
'preview': preview,
|
| 157 |
-
'duplicate_rows': duplicate_rows,
|
| 158 |
-
'unique_counts': unique_counts,
|
| 159 |
-
'col_stats': col_stats,
|
| 160 |
-
'file_size_bytes': file_size_bytes,
|
| 161 |
-
}
|
| 162 |
-
|
| 163 |
-
return ["success","<p style='color:green;text-align:center;font-size:18px;'>Data upload successful</p>", columns, stats]
|
| 164 |
-
except Exception as e:
|
| 165 |
-
print("UPLOAD ERROR")
|
| 166 |
-
print(e)
|
| 167 |
-
return ["error",f"<p style='color:red;text-align:center;font-size:18px;font-weight:bold;'>ERROR: {e}</p>"]
|
|
|
|
| 1 |
import pandas as pd
|
| 2 |
import sqlite3
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3 |
|
| 4 |
def process_data_upload(data_file, session_hash):
|
| 5 |
+
df = pd.read_csv(data_file, sep=";")
|
| 6 |
+
|
| 7 |
+
# Read each sheet and store data in a DataFrame
|
| 8 |
+
#data = df.parse(sheet_name)
|
| 9 |
+
# Process the data as needed
|
| 10 |
+
# ...
|
| 11 |
+
df.columns = df.columns.str.replace(' ', '_')
|
| 12 |
+
df.columns = df.columns.str.replace('/', '_')
|
| 13 |
+
|
| 14 |
+
connection = sqlite3.connect(f'data_source_{session_hash}.db')
|
| 15 |
+
print("Opened database successfully");
|
| 16 |
+
print(df.columns)
|
| 17 |
+
|
| 18 |
+
df.to_sql('data_source', connection, if_exists='replace', index = False)
|
| 19 |
+
|
| 20 |
+
connection.commit()
|
| 21 |
+
connection.close()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
functions/__init__.py
CHANGED
|
@@ -1,17 +1,4 @@
|
|
| 1 |
-
from .
|
| 2 |
-
from .
|
| 3 |
-
line_chart_generation_func, bar_chart_generation_func, pie_chart_generation_func, \
|
| 4 |
-
histogram_generation_func, box_chart_generation_func, correlation_heatmap_func, \
|
| 5 |
-
scatter_chart_fig, rolling_stats_func
|
| 6 |
-
from .chat_functions import example_question_generator, chatbot_func
|
| 7 |
-
from .stat_functions import regression_func, descriptive_stats_func, \
|
| 8 |
-
kmeans_clustering_func, hypothesis_test_func
|
| 9 |
|
| 10 |
-
__all__ = [
|
| 11 |
-
"query_func", "graphql_schema_query", "graphql_csv_query",
|
| 12 |
-
"table_generation_func", "scatter_chart_generation_func", "line_chart_generation_func",
|
| 13 |
-
"bar_chart_generation_func", "pie_chart_generation_func", "histogram_generation_func",
|
| 14 |
-
"box_chart_generation_func", "correlation_heatmap_func", "rolling_stats_func",
|
| 15 |
-
"regression_func", "descriptive_stats_func", "kmeans_clustering_func", "hypothesis_test_func",
|
| 16 |
-
"scatter_chart_fig", "example_question_generator", "chatbot_func",
|
| 17 |
-
]
|
|
|
|
| 1 |
+
from .sqlite_functions import SQLiteQuery, sqlite_query_func
|
| 2 |
+
from .chat_functions import demo
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3 |
|
| 4 |
+
__all__ = ["SQLiteQuery","sqlite_query_func","demo"]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
functions/chart_functions.py
DELETED
|
@@ -1,526 +0,0 @@
|
|
| 1 |
-
from typing import List
|
| 2 |
-
import plotly.io as pio
|
| 3 |
-
import plotly.express as px
|
| 4 |
-
import pandas as pd
|
| 5 |
-
from utils import TEMP_DIR
|
| 6 |
-
import os
|
| 7 |
-
import ast
|
| 8 |
-
from dotenv import load_dotenv
|
| 9 |
-
|
| 10 |
-
load_dotenv()
|
| 11 |
-
|
| 12 |
-
root_url = os.getenv("ROOT_URL", "")
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
def _write_chart(fig, chart_path, chart_url):
|
| 16 |
-
"""Write a Plotly figure to disk and return a responsive iframe HTML string."""
|
| 17 |
-
pio.write_html(fig, chart_path, full_html=False, config={"responsive": True})
|
| 18 |
-
return (
|
| 19 |
-
'Please display this iframe: '
|
| 20 |
-
'<div style="width:100%;overflow-x:auto;">'
|
| 21 |
-
'<iframe style="width:100%;min-width:400px;" height="500" '
|
| 22 |
-
f'src="{chart_url}" frameborder="0" allowfullscreen>'
|
| 23 |
-
'</iframe></div>'
|
| 24 |
-
)
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
def llm_chart_data_scrub(data, layout):
|
| 28 |
-
#Processing data to account for variation from LLM
|
| 29 |
-
data_list = []
|
| 30 |
-
layout_dict = {}
|
| 31 |
-
|
| 32 |
-
if isinstance(data, list):
|
| 33 |
-
data_list = data
|
| 34 |
-
else:
|
| 35 |
-
data_list.append(data)
|
| 36 |
-
|
| 37 |
-
false_replace = [':false', ': false']
|
| 38 |
-
false_value = ':False'
|
| 39 |
-
true_replace = [':true', ': true']
|
| 40 |
-
true_value = ':True'
|
| 41 |
-
|
| 42 |
-
data_dict = {}
|
| 43 |
-
for data_obj in data_list:
|
| 44 |
-
if isinstance(data_obj, str):
|
| 45 |
-
data_obj = data_obj.replace("\n", "")
|
| 46 |
-
for replace in false_replace:
|
| 47 |
-
data_obj = data_obj.replace(replace, false_value)
|
| 48 |
-
for replace in true_replace:
|
| 49 |
-
data_obj = data_obj.replace(replace, true_value)
|
| 50 |
-
print(data_obj)
|
| 51 |
-
data_dict = ast.literal_eval(data_obj)
|
| 52 |
-
else:
|
| 53 |
-
data_dict = data_obj
|
| 54 |
-
|
| 55 |
-
if layout and isinstance(layout, list):
|
| 56 |
-
layout_obj = layout[0]
|
| 57 |
-
else:
|
| 58 |
-
layout_obj = layout
|
| 59 |
-
|
| 60 |
-
if layout_obj and isinstance(layout_obj, str):
|
| 61 |
-
for replace in false_replace:
|
| 62 |
-
layout_obj = layout_obj.replace(replace, false_value)
|
| 63 |
-
for replace in true_replace:
|
| 64 |
-
layout_obj = layout_obj.replace(replace, true_value)
|
| 65 |
-
print(layout_obj)
|
| 66 |
-
layout_dict = ast.literal_eval(layout_obj)
|
| 67 |
-
else:
|
| 68 |
-
layout_dict = layout_obj
|
| 69 |
-
|
| 70 |
-
return data_dict, layout_dict
|
| 71 |
-
|
| 72 |
-
def scatter_chart_fig(df, x_column: List[str], y_column: str, category: str="", trendline: str="",
|
| 73 |
-
trendline_options: List[dict]=[{}], marginal_x: str="", marginal_y: str="",
|
| 74 |
-
size: str=""):
|
| 75 |
-
|
| 76 |
-
function_args = {"data_frame":df, "x":x_column, "y":y_column}
|
| 77 |
-
|
| 78 |
-
if category:
|
| 79 |
-
function_args["color"] = category
|
| 80 |
-
if trendline:
|
| 81 |
-
function_args["trendline"] = trendline
|
| 82 |
-
if marginal_x:
|
| 83 |
-
function_args["marginal_x"] = marginal_x
|
| 84 |
-
if marginal_y:
|
| 85 |
-
function_args["marginal_y"] = marginal_y
|
| 86 |
-
if size:
|
| 87 |
-
df.loc[df[size] < 0, size] = 0
|
| 88 |
-
function_args["size"] = size
|
| 89 |
-
if trendline_options:
|
| 90 |
-
trendline_options_dict = {}
|
| 91 |
-
if trendline_options and isinstance(trendline_options, list):
|
| 92 |
-
trendline_options_obj = trendline_options[0]
|
| 93 |
-
else:
|
| 94 |
-
trendline_options_obj = trendline_options
|
| 95 |
-
|
| 96 |
-
if trendline_options_obj and isinstance(trendline_options_obj, str):
|
| 97 |
-
trendline_options_dict = ast.literal_eval(trendline_options_obj)
|
| 98 |
-
else:
|
| 99 |
-
trendline_options_dict = trendline_options_obj
|
| 100 |
-
function_args["trendline_options"] = trendline_options_dict
|
| 101 |
-
|
| 102 |
-
fig = px.scatter(**function_args)
|
| 103 |
-
|
| 104 |
-
return fig
|
| 105 |
-
|
| 106 |
-
def scatter_chart_generation_func(x_column: List[str], y_column: str, session_hash, session_folder, data: List[dict]=[{}], layout: List[dict]=[{}],
|
| 107 |
-
category: str="", trendline: str="", trendline_options: List[dict]=[{}], marginal_x: str="", marginal_y: str="",
|
| 108 |
-
size: str="", **kwargs):
|
| 109 |
-
try:
|
| 110 |
-
dir_path = TEMP_DIR / str(session_hash) / str(session_folder)
|
| 111 |
-
chart_path = f'{dir_path}/chart.html'
|
| 112 |
-
csv_query_path = f'{dir_path}/query.csv'
|
| 113 |
-
|
| 114 |
-
df = pd.read_csv(csv_query_path)
|
| 115 |
-
|
| 116 |
-
initial_graph = scatter_chart_fig(df, x_column=x_column, y_column=y_column,
|
| 117 |
-
category=category, trendline=trendline, trendline_options=trendline_options,
|
| 118 |
-
marginal_x=marginal_x, marginal_y=marginal_y, size=size)
|
| 119 |
-
|
| 120 |
-
fig = initial_graph.to_dict()
|
| 121 |
-
|
| 122 |
-
print(data)
|
| 123 |
-
print(layout)
|
| 124 |
-
|
| 125 |
-
data_dict,layout_dict = llm_chart_data_scrub(data,layout)
|
| 126 |
-
|
| 127 |
-
#Applying stylings and settings generated from LLM
|
| 128 |
-
if layout_dict:
|
| 129 |
-
fig["layout"] = layout_dict
|
| 130 |
-
|
| 131 |
-
data_ignore = ["x","y","type"]
|
| 132 |
-
|
| 133 |
-
if size:
|
| 134 |
-
data_ignore.append("marker")
|
| 135 |
-
|
| 136 |
-
for key, value in data_dict.items():
|
| 137 |
-
if key not in data_ignore:
|
| 138 |
-
for data_item in fig["data"]:
|
| 139 |
-
data_item[key] = value
|
| 140 |
-
|
| 141 |
-
chart_url = f'{root_url}/gradio_api/file/temp/{session_hash}/{session_folder}/chart.html'
|
| 142 |
-
return {"reply": _write_chart(fig, chart_path, chart_url)}
|
| 143 |
-
|
| 144 |
-
except Exception as e:
|
| 145 |
-
print("SCATTER PLOT ERROR")
|
| 146 |
-
print(e)
|
| 147 |
-
reply = f"""There was an error generating the Plotly Scatter Plot from {x_column}, {y_column}, {data}, and {layout}
|
| 148 |
-
The error is {e},
|
| 149 |
-
You should probably try again.
|
| 150 |
-
"""
|
| 151 |
-
return {"reply": reply}
|
| 152 |
-
|
| 153 |
-
def line_chart_generation_func(x_column: str, y_column: str, session_hash, session_folder, data: List[dict]=[{}], layout: List[dict]=[{}],
|
| 154 |
-
category: str="", **kwargs):
|
| 155 |
-
try:
|
| 156 |
-
dir_path = TEMP_DIR / str(session_hash) / str(session_folder)
|
| 157 |
-
chart_path = f'{dir_path}/chart.html'
|
| 158 |
-
csv_query_path = f'{dir_path}/query.csv'
|
| 159 |
-
|
| 160 |
-
df = pd.read_csv(csv_query_path)
|
| 161 |
-
|
| 162 |
-
function_args = {"data_frame":df, "x":x_column, "y":y_column}
|
| 163 |
-
|
| 164 |
-
if category:
|
| 165 |
-
function_args["color"] = category
|
| 166 |
-
|
| 167 |
-
initial_graph = px.line(**function_args)
|
| 168 |
-
|
| 169 |
-
fig = initial_graph.to_dict()
|
| 170 |
-
|
| 171 |
-
data_dict,layout_dict = llm_chart_data_scrub(data,layout)
|
| 172 |
-
|
| 173 |
-
print(data_dict)
|
| 174 |
-
print(layout_dict)
|
| 175 |
-
|
| 176 |
-
#Applying stylings and settings generated from LLM
|
| 177 |
-
if layout_dict:
|
| 178 |
-
fig["layout"] = layout_dict
|
| 179 |
-
|
| 180 |
-
for key, value in data_dict.items():
|
| 181 |
-
if key not in ["x","y","type"]:
|
| 182 |
-
for data_item in fig["data"]:
|
| 183 |
-
data_item[key] = value
|
| 184 |
-
|
| 185 |
-
print(fig)
|
| 186 |
-
|
| 187 |
-
chart_url = f'{root_url}/gradio_api/file/temp/{session_hash}/{session_folder}/chart.html'
|
| 188 |
-
return {"reply": _write_chart(fig, chart_path, chart_url)}
|
| 189 |
-
|
| 190 |
-
except Exception as e:
|
| 191 |
-
print("LINE CHART ERROR")
|
| 192 |
-
print(e)
|
| 193 |
-
reply = f"""There was an error generating the Plotly Line Chart from {x_column}, {y_column}, {data}, and {layout}
|
| 194 |
-
The error is {e},
|
| 195 |
-
You should probably try again.
|
| 196 |
-
"""
|
| 197 |
-
return {"reply": reply}
|
| 198 |
-
|
| 199 |
-
def bar_chart_generation_func(x_column: str, y_column: str, session_hash, session_folder, data: List[dict]=[{}], layout: List[dict]=[{}],
|
| 200 |
-
category: str="", facet_row: str="", facet_col: str="", **kwargs):
|
| 201 |
-
try:
|
| 202 |
-
dir_path = TEMP_DIR / str(session_hash) / str(session_folder)
|
| 203 |
-
chart_path = f'{dir_path}/chart.html'
|
| 204 |
-
csv_query_path = f'{dir_path}/query.csv'
|
| 205 |
-
|
| 206 |
-
df = pd.read_csv(csv_query_path)
|
| 207 |
-
|
| 208 |
-
function_args = {"data_frame":df, "x":x_column, "y":y_column}
|
| 209 |
-
|
| 210 |
-
if category:
|
| 211 |
-
function_args["color"] = category
|
| 212 |
-
if facet_row:
|
| 213 |
-
function_args["facet_row"] = facet_row
|
| 214 |
-
if facet_col:
|
| 215 |
-
function_args["facet_col"] = facet_col
|
| 216 |
-
|
| 217 |
-
initial_graph = px.bar(**function_args)
|
| 218 |
-
|
| 219 |
-
fig = initial_graph.to_dict()
|
| 220 |
-
|
| 221 |
-
data_dict,layout_dict = llm_chart_data_scrub(data,layout)
|
| 222 |
-
|
| 223 |
-
print(data_dict)
|
| 224 |
-
print(layout_dict)
|
| 225 |
-
|
| 226 |
-
#Applying stylings and settings generated from LLM
|
| 227 |
-
if layout_dict:
|
| 228 |
-
fig["layout"] = layout_dict
|
| 229 |
-
|
| 230 |
-
for key, value in data_dict.items():
|
| 231 |
-
if key not in ["x","y","type"]:
|
| 232 |
-
for data_item in fig["data"]:
|
| 233 |
-
data_item[key] = value
|
| 234 |
-
|
| 235 |
-
print(fig)
|
| 236 |
-
|
| 237 |
-
chart_url = f'{root_url}/gradio_api/file/temp/{session_hash}/{session_folder}/chart.html'
|
| 238 |
-
return {"reply": _write_chart(fig, chart_path, chart_url)}
|
| 239 |
-
|
| 240 |
-
except Exception as e:
|
| 241 |
-
print("BAR CHART ERROR")
|
| 242 |
-
print(e)
|
| 243 |
-
reply = f"""There was an error generating the Plotly Bar Chart from {x_column}, {y_column}, {data}, and {layout}
|
| 244 |
-
The error is {e},
|
| 245 |
-
You should probably try again.
|
| 246 |
-
"""
|
| 247 |
-
return {"reply": reply}
|
| 248 |
-
|
| 249 |
-
def pie_chart_generation_func(values: str, names: str, session_hash, session_folder, data: List[dict]=[{}], layout: List[dict]=[{}], **kwargs):
|
| 250 |
-
try:
|
| 251 |
-
dir_path = TEMP_DIR / str(session_hash) / str(session_folder)
|
| 252 |
-
chart_path = f'{dir_path}/chart.html'
|
| 253 |
-
csv_query_path = f'{dir_path}/query.csv'
|
| 254 |
-
|
| 255 |
-
df = pd.read_csv(csv_query_path)
|
| 256 |
-
|
| 257 |
-
function_args = {"data_frame":df, "values":values, "names":names}
|
| 258 |
-
|
| 259 |
-
initial_graph = px.pie(**function_args)
|
| 260 |
-
|
| 261 |
-
fig = initial_graph.to_dict()
|
| 262 |
-
|
| 263 |
-
data_dict,layout_dict = llm_chart_data_scrub(data,layout)
|
| 264 |
-
|
| 265 |
-
print(data_dict)
|
| 266 |
-
print(layout_dict)
|
| 267 |
-
|
| 268 |
-
#Applying stylings and settings generated from LLM
|
| 269 |
-
if layout_dict:
|
| 270 |
-
fig["layout"] = layout_dict
|
| 271 |
-
|
| 272 |
-
for key, value in data_dict.items():
|
| 273 |
-
if key not in ["x","y","type"]:
|
| 274 |
-
for data_item in fig["data"]:
|
| 275 |
-
data_item[key] = value
|
| 276 |
-
|
| 277 |
-
print(fig)
|
| 278 |
-
|
| 279 |
-
chart_url = f'{root_url}/gradio_api/file/temp/{session_hash}/{session_folder}/chart.html'
|
| 280 |
-
return {"reply": _write_chart(fig, chart_path, chart_url)}
|
| 281 |
-
|
| 282 |
-
except Exception as e:
|
| 283 |
-
print("PIE CHART ERROR")
|
| 284 |
-
print(e)
|
| 285 |
-
reply = f"""There was an error generating the Plotly Pie Chart from {values}, {names}, {data}, and {layout}
|
| 286 |
-
The error is {e},
|
| 287 |
-
You should probably try again.
|
| 288 |
-
"""
|
| 289 |
-
return {"reply": reply}
|
| 290 |
-
|
| 291 |
-
def histogram_generation_func(x_column: str, session_hash, session_folder, y_column: str="", data: List[dict]=[{}], layout: List[dict]=[{}], histnorm: str="", category: str="",
|
| 292 |
-
histfunc: str="", **kwargs):
|
| 293 |
-
try:
|
| 294 |
-
dir_path = TEMP_DIR / str(session_hash) / str(session_folder)
|
| 295 |
-
chart_path = f'{dir_path}/chart.html'
|
| 296 |
-
csv_query_path = f'{dir_path}/query.csv'
|
| 297 |
-
|
| 298 |
-
df = pd.read_csv(csv_query_path)
|
| 299 |
-
|
| 300 |
-
print(x_column)
|
| 301 |
-
|
| 302 |
-
function_args = {"data_frame":df, "x":x_column}
|
| 303 |
-
|
| 304 |
-
if y_column:
|
| 305 |
-
function_args["y"] = y_column
|
| 306 |
-
if histnorm:
|
| 307 |
-
function_args["histnorm"] = histnorm
|
| 308 |
-
if category:
|
| 309 |
-
function_args["color"] = category
|
| 310 |
-
if histfunc:
|
| 311 |
-
function_args["histfunc"] = histfunc
|
| 312 |
-
|
| 313 |
-
initial_graph = px.histogram(**function_args)
|
| 314 |
-
|
| 315 |
-
fig = initial_graph.to_dict()
|
| 316 |
-
|
| 317 |
-
data_dict,layout_dict = llm_chart_data_scrub(data,layout)
|
| 318 |
-
|
| 319 |
-
print(data_dict)
|
| 320 |
-
print(layout_dict)
|
| 321 |
-
|
| 322 |
-
#Applying stylings and settings generated from LLM
|
| 323 |
-
if layout_dict:
|
| 324 |
-
fig["layout"] = layout_dict
|
| 325 |
-
|
| 326 |
-
for key, value in data_dict.items():
|
| 327 |
-
if key not in ["x","y","type"]:
|
| 328 |
-
for data_item in fig["data"]:
|
| 329 |
-
data_item[key] = value
|
| 330 |
-
|
| 331 |
-
print(fig)
|
| 332 |
-
|
| 333 |
-
chart_url = f'{root_url}/gradio_api/file/temp/{session_hash}/{session_folder}/chart.html'
|
| 334 |
-
return {"reply": _write_chart(fig, chart_path, chart_url)}
|
| 335 |
-
|
| 336 |
-
except Exception as e:
|
| 337 |
-
print("HISTOGRAM ERROR")
|
| 338 |
-
print(e)
|
| 339 |
-
reply = f"""There was an error generating the Plotly Histogram from {x_column}.
|
| 340 |
-
The error is {e},
|
| 341 |
-
You should probably try again.
|
| 342 |
-
"""
|
| 343 |
-
return {"reply": reply}
|
| 344 |
-
|
| 345 |
-
def box_chart_generation_func(y_column: str, session_hash, session_folder,
|
| 346 |
-
x_column: str="", category: str="",
|
| 347 |
-
layout: List[dict]=[{}], **kwargs):
|
| 348 |
-
try:
|
| 349 |
-
dir_path = TEMP_DIR / str(session_hash) / str(session_folder)
|
| 350 |
-
chart_path = f'{dir_path}/chart.html'
|
| 351 |
-
csv_query_path = f'{dir_path}/query.csv'
|
| 352 |
-
|
| 353 |
-
df = pd.read_csv(csv_query_path)
|
| 354 |
-
|
| 355 |
-
function_args = {"data_frame": df, "y": y_column}
|
| 356 |
-
if x_column:
|
| 357 |
-
function_args["x"] = x_column
|
| 358 |
-
if category:
|
| 359 |
-
function_args["color"] = category
|
| 360 |
-
|
| 361 |
-
initial_graph = px.box(**function_args)
|
| 362 |
-
fig = initial_graph.to_dict()
|
| 363 |
-
|
| 364 |
-
_, layout_dict = llm_chart_data_scrub({}, layout)
|
| 365 |
-
if layout_dict:
|
| 366 |
-
fig["layout"] = layout_dict
|
| 367 |
-
|
| 368 |
-
chart_url = f'{root_url}/gradio_api/file/temp/{session_hash}/{session_folder}/chart.html'
|
| 369 |
-
return {"reply": _write_chart(fig, chart_path, chart_url)}
|
| 370 |
-
|
| 371 |
-
except Exception as e:
|
| 372 |
-
print("BOX CHART ERROR")
|
| 373 |
-
print(e)
|
| 374 |
-
return {"reply": f"There was an error generating the box plot. Error: {e}. You should probably try again."}
|
| 375 |
-
|
| 376 |
-
|
| 377 |
-
def correlation_heatmap_func(session_hash, session_folder, columns: List[str]=[], **kwargs):
|
| 378 |
-
try:
|
| 379 |
-
dir_path = TEMP_DIR / str(session_hash) / str(session_folder)
|
| 380 |
-
chart_path = f'{dir_path}/chart.html'
|
| 381 |
-
csv_query_path = f'{dir_path}/query.csv'
|
| 382 |
-
|
| 383 |
-
df = pd.read_csv(csv_query_path)
|
| 384 |
-
|
| 385 |
-
numeric_df = df[columns].select_dtypes(include='number') if columns else df.select_dtypes(include='number')
|
| 386 |
-
|
| 387 |
-
if numeric_df.shape[1] < 2:
|
| 388 |
-
return {"reply": "At least two numeric columns are needed for a correlation matrix. Please refine your query to include more numeric columns."}
|
| 389 |
-
|
| 390 |
-
corr = numeric_df.corr().round(3)
|
| 391 |
-
|
| 392 |
-
fig = px.imshow(
|
| 393 |
-
corr,
|
| 394 |
-
text_auto='.2f',
|
| 395 |
-
color_continuous_scale='RdBu_r',
|
| 396 |
-
zmin=-1,
|
| 397 |
-
zmax=1,
|
| 398 |
-
title='Correlation Matrix',
|
| 399 |
-
aspect='auto',
|
| 400 |
-
)
|
| 401 |
-
fig.update_layout(font=dict(family='Inter, system-ui, sans-serif'))
|
| 402 |
-
|
| 403 |
-
chart_url = f'{root_url}/gradio_api/file/temp/{session_hash}/{session_folder}/chart.html'
|
| 404 |
-
return {"reply": _write_chart(fig, chart_path, chart_url)}
|
| 405 |
-
|
| 406 |
-
except Exception as e:
|
| 407 |
-
print("CORRELATION HEATMAP ERROR")
|
| 408 |
-
print(e)
|
| 409 |
-
return {"reply": f"There was an error generating the correlation heatmap. Error: {e}. You should probably try again."}
|
| 410 |
-
|
| 411 |
-
|
| 412 |
-
def rolling_stats_func(x_column: str, y_column: str, session_hash, session_folder,
|
| 413 |
-
window: int = 7, stats: List[str] = ["mean"],
|
| 414 |
-
layout: List[dict] = [{}], category: str = "", **kwargs):
|
| 415 |
-
try:
|
| 416 |
-
import plotly.graph_objects as go
|
| 417 |
-
|
| 418 |
-
dir_path = TEMP_DIR / str(session_hash) / str(session_folder)
|
| 419 |
-
chart_path = f'{dir_path}/chart.html'
|
| 420 |
-
csv_query_path = f'{dir_path}/query.csv'
|
| 421 |
-
|
| 422 |
-
df = pd.read_csv(csv_query_path)
|
| 423 |
-
|
| 424 |
-
try:
|
| 425 |
-
df[x_column] = pd.to_datetime(df[x_column])
|
| 426 |
-
except Exception:
|
| 427 |
-
pass
|
| 428 |
-
df = df.sort_values(x_column)
|
| 429 |
-
|
| 430 |
-
valid_stats = {"mean", "std", "min", "max"}
|
| 431 |
-
selected_stats = [s for s in stats if s in valid_stats] or ["mean"]
|
| 432 |
-
|
| 433 |
-
fig = go.Figure()
|
| 434 |
-
|
| 435 |
-
groups = df[category].unique().tolist() if category and category in df.columns else [None]
|
| 436 |
-
|
| 437 |
-
for group in groups:
|
| 438 |
-
group_df = df[df[category] == group] if group is not None else df
|
| 439 |
-
prefix = f"{group} — " if group is not None else ""
|
| 440 |
-
|
| 441 |
-
fig.add_trace(go.Scatter(
|
| 442 |
-
x=group_df[x_column].values, y=group_df[y_column].values,
|
| 443 |
-
mode="lines", name=f"{prefix}{y_column} (raw)",
|
| 444 |
-
opacity=0.35, line=dict(width=1)
|
| 445 |
-
))
|
| 446 |
-
|
| 447 |
-
rolling_obj = group_df[y_column].rolling(window)
|
| 448 |
-
for stat in selected_stats:
|
| 449 |
-
rolled = getattr(rolling_obj, stat)()
|
| 450 |
-
fig.add_trace(go.Scatter(
|
| 451 |
-
x=group_df[x_column].values, y=rolled.values,
|
| 452 |
-
mode="lines", name=f"{prefix}Rolling {stat.capitalize()} (w={window})",
|
| 453 |
-
line=dict(width=2.5)
|
| 454 |
-
))
|
| 455 |
-
|
| 456 |
-
fig.update_layout(
|
| 457 |
-
title=f"Rolling Statistics (window={window}) — {y_column}",
|
| 458 |
-
xaxis_title=x_column,
|
| 459 |
-
yaxis_title=y_column,
|
| 460 |
-
font=dict(family="Inter, system-ui, sans-serif"),
|
| 461 |
-
legend=dict(orientation="h", yanchor="bottom", y=1.02, xanchor="right", x=1),
|
| 462 |
-
)
|
| 463 |
-
|
| 464 |
-
_, layout_dict = llm_chart_data_scrub({}, layout)
|
| 465 |
-
if layout_dict:
|
| 466 |
-
fig.update_layout(**layout_dict)
|
| 467 |
-
|
| 468 |
-
chart_url = f'{root_url}/gradio_api/file/temp/{session_hash}/{session_folder}/chart.html'
|
| 469 |
-
return {"reply": _write_chart(fig, chart_path, chart_url)}
|
| 470 |
-
|
| 471 |
-
except Exception as e:
|
| 472 |
-
print("ROLLING STATS ERROR")
|
| 473 |
-
print(e)
|
| 474 |
-
return {"reply": f"There was an error generating the rolling statistics chart. Error: {e}. You should probably try again."}
|
| 475 |
-
|
| 476 |
-
|
| 477 |
-
def table_generation_func(session_hash, session_folder, **kwargs):
|
| 478 |
-
print("TABLE GENERATION")
|
| 479 |
-
try:
|
| 480 |
-
from html import escape
|
| 481 |
-
|
| 482 |
-
dir_path = TEMP_DIR / str(session_hash) / str(session_folder)
|
| 483 |
-
csv_query_path = f'{dir_path}/query.csv'
|
| 484 |
-
|
| 485 |
-
df = pd.read_csv(csv_query_path)
|
| 486 |
-
|
| 487 |
-
total_rows = len(df)
|
| 488 |
-
max_rows = 200
|
| 489 |
-
if total_rows > max_rows:
|
| 490 |
-
df = df.head(max_rows)
|
| 491 |
-
note = (f'<p class="vda-table-note">Showing first {max_rows} of {total_rows} rows'
|
| 492 |
-
' — refine your query to see more specific results.</p>')
|
| 493 |
-
else:
|
| 494 |
-
note = ''
|
| 495 |
-
|
| 496 |
-
header_cells = ''.join(f'<th>{escape(str(col))}</th>' for col in df.columns)
|
| 497 |
-
row_html = [
|
| 498 |
-
'<tr>' + ''.join(f'<td>{escape(str(val))}</td>' for val in row) + '</tr>'
|
| 499 |
-
for _, row in df.iterrows()
|
| 500 |
-
]
|
| 501 |
-
|
| 502 |
-
style = (
|
| 503 |
-
'<style>'
|
| 504 |
-
'.vda-table-wrap{overflow-x:auto;margin:8px 0;border-radius:8px;border:1px solid #e5e7eb;}'
|
| 505 |
-
'.vda-table{width:100%;border-collapse:collapse;font-size:13px;font-family:Inter,system-ui,sans-serif;}'
|
| 506 |
-
'.vda-table thead th{background:#3B82F6;color:#fff;padding:9px 14px;text-align:left;white-space:nowrap;font-weight:600;}'
|
| 507 |
-
'.vda-table tbody td{padding:7px 14px;border-bottom:1px solid #f1f5f9;white-space:nowrap;}'
|
| 508 |
-
'.vda-table tbody tr:nth-child(even){background:#f8fafc;}'
|
| 509 |
-
'.vda-table tbody tr:last-child td{border-bottom:none;}'
|
| 510 |
-
'.vda-table-note{font-size:12px;color:#6b7280;margin:4px 0 0;text-align:right;}'
|
| 511 |
-
'</style>'
|
| 512 |
-
)
|
| 513 |
-
|
| 514 |
-
table = (
|
| 515 |
-
'<div class="vda-table-wrap"><table class="vda-table">'
|
| 516 |
-
f'<thead><tr>{header_cells}</tr></thead>'
|
| 517 |
-
'<tbody>' + '\n'.join(row_html) + '</tbody>'
|
| 518 |
-
'</table></div>'
|
| 519 |
-
)
|
| 520 |
-
|
| 521 |
-
return {"reply": style + table + note}
|
| 522 |
-
|
| 523 |
-
except Exception as e:
|
| 524 |
-
print("TABLE ERROR")
|
| 525 |
-
print(e)
|
| 526 |
-
return {"reply": f"There was an error generating the table. Error: {e}. You should probably try again."}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
functions/chat_functions.py
CHANGED
|
@@ -1,184 +1,111 @@
|
|
| 1 |
-
from
|
|
|
|
|
|
|
| 2 |
|
| 3 |
from haystack.dataclasses import ChatMessage
|
| 4 |
from haystack.components.generators.chat import OpenAIChatGenerator
|
| 5 |
-
from haystack.utils import Secret
|
| 6 |
-
|
| 7 |
-
def _get_generator(session_hash):
|
| 8 |
-
api_key = api_key_store.get(session_hash)
|
| 9 |
-
if not api_key:
|
| 10 |
-
raise ValueError("No API key found for this session. Please enter your API key at the top of the page.")
|
| 11 |
-
model = model_store.get(session_hash, "gpt-4o")
|
| 12 |
-
if api_key.startswith("sk-ant-"):
|
| 13 |
-
from haystack_integrations.components.generators.chat import AnthropicChatGenerator
|
| 14 |
-
return AnthropicChatGenerator(model=model, api_key=Secret.from_token(api_key))
|
| 15 |
-
return OpenAIChatGenerator(model=model, api_key=Secret.from_token(api_key))
|
| 16 |
|
| 17 |
-
|
|
|
|
|
|
|
| 18 |
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
f"""We have a SQLite database with the following {titles}.
|
| 24 |
-
We also have an AI agent with access to the same database that will be performing data analysis.
|
| 25 |
-
Please return an array of seven strings, each one being a question for our data analysis agent
|
| 26 |
-
that we can suggest that you believe will be insightful or helpful to a data analyst looking for
|
| 27 |
-
data insights. Return nothing more than the array of questions because I need that specific data structure
|
| 28 |
-
to process your response. No other response type or data structure will work."""],
|
| 29 |
-
|
| 30 |
-
'sql' : [f"You are a helpful and knowledgeable agent who has access to a PostgreSQL database called {name}.",
|
| 31 |
-
f"""We have a PostgreSQL database with the following tables: {titles}.
|
| 32 |
-
We also have an AI agent with access to the same database that will be performing data analysis.
|
| 33 |
-
Please return an array of seven strings, each one being a question for our data analysis agent
|
| 34 |
-
that we can suggest that you believe will be insightful or helpful to a data analyst looking for
|
| 35 |
-
data insights. Return nothing more than the array of questions because I need that specific data structure
|
| 36 |
-
to process your response. No other response type or data structure will work."""],
|
| 37 |
-
|
| 38 |
-
'doc_db' : [f"You are a helpful and knowledgeable agent who has access to an MongoDB NoSQL document database called {name}.",
|
| 39 |
-
f"""We have a MongoDB NoSQL document database with the following collections: {titles}.
|
| 40 |
-
The schema of these collections is: {schema}.
|
| 41 |
-
We also have an AI agent with access to the same database that will be performing data analysis.
|
| 42 |
-
Please return an array of seven strings, each one being a question for our data analysis agent
|
| 43 |
-
that we can suggest that you believe will be insightful or helpful to a data analyst looking for
|
| 44 |
-
data insights. Return nothing more than the array of questions because I need that specific data structure
|
| 45 |
-
to process your response. No other response type or data structure will work."""],
|
| 46 |
-
|
| 47 |
-
'graphql' : [f"You are a helpful and knowledgeable agent who has access to an GraphQL API endpoint called {name}.",
|
| 48 |
-
f"""We have a GraphQL API endpoint with the following types: {titles}.
|
| 49 |
-
We also have an AI agent with access to the same GraphQL API endpoint that will be performing data analysis.
|
| 50 |
-
Please return an array of seven strings, each one being a question for our data analysis agent
|
| 51 |
-
that we can suggest that you believe will be insightful or helpful to a data analyst looking for
|
| 52 |
-
data insights. Return nothing more than the array of questions because I need that specific data structure
|
| 53 |
-
to process your response. No other response type or data structure will work."""]
|
| 54 |
-
|
| 55 |
-
}
|
| 56 |
-
|
| 57 |
-
return example_message_dict[data_source]
|
| 58 |
-
|
| 59 |
-
def example_question_generator(session_hash, data_source, name, titles, schema):
|
| 60 |
-
example_response = None
|
| 61 |
-
example_message_list = example_question_message(data_source, name, titles, schema)
|
| 62 |
-
example_messages = [
|
| 63 |
-
ChatMessage.from_system(
|
| 64 |
-
example_message_list[0]
|
| 65 |
-
)
|
| 66 |
-
]
|
| 67 |
-
|
| 68 |
-
example_messages.append(ChatMessage.from_user(text=example_message_list[1]))
|
| 69 |
-
|
| 70 |
-
example_response = _get_generator(session_hash).run(messages=example_messages)
|
| 71 |
-
|
| 72 |
-
response_text = example_response["replies"][0].text
|
| 73 |
-
start = response_text.index("[") + 1
|
| 74 |
-
end = response_text.index("]")
|
| 75 |
-
response_content = response_text[start:end]
|
| 76 |
-
response_list = '[' + response_content + ']'
|
| 77 |
-
print(response_list)
|
| 78 |
-
|
| 79 |
-
return response_list
|
| 80 |
-
|
| 81 |
-
def system_message(data_source, titles, schema=""):
|
| 82 |
-
print("TITLES")
|
| 83 |
-
print(titles)
|
| 84 |
-
|
| 85 |
-
tools_desc = (
|
| 86 |
-
" You have access to tools for querying the data source, generating charts and visualisations,"
|
| 87 |
-
" and performing statistical analyses — use them proactively whenever they would help answer the user's question."
|
| 88 |
-
" Always display any charts, tables, and visualisations inline in your responses by outputting the returned HTML verbatim."
|
| 89 |
-
)
|
| 90 |
|
| 91 |
-
|
| 92 |
-
|
| 93 |
-
|
| 94 |
-
|
| 95 |
-
|
| 96 |
-
'sql': (
|
| 97 |
-
f"You are a helpful and knowledgeable agent who has access to a PostgreSQL database which has a series of tables called {titles}."
|
| 98 |
-
+ tools_desc
|
| 99 |
-
),
|
| 100 |
-
'doc_db': (
|
| 101 |
-
f"You are a helpful and knowledgeable agent who has access to a NoSQL MongoDB Document database which has a series of collections called {titles}. "
|
| 102 |
-
f"The schema of these collections is: {schema}."
|
| 103 |
-
+ tools_desc
|
| 104 |
-
),
|
| 105 |
-
'graphql': (
|
| 106 |
-
f"You are a helpful and knowledgeable agent who has access to a GraphQL API which has the following types: {titles}. "
|
| 107 |
-
"We have also saved a schema.json file that contains the entire introspection query that we can use to find out more about each type before making a query."
|
| 108 |
-
+ tools_desc
|
| 109 |
-
),
|
| 110 |
-
}
|
| 111 |
-
|
| 112 |
-
return system_message_dict[data_source]
|
| 113 |
-
|
| 114 |
-
def chatbot_func(message, history, session_hash, data_source, titles, schema, *args):
|
| 115 |
-
try:
|
| 116 |
-
chat_generator = _get_generator(session_hash)
|
| 117 |
-
except ValueError as e:
|
| 118 |
-
return str(e)
|
| 119 |
-
|
| 120 |
-
from functions import (
|
| 121 |
-
table_generation_func, regression_func, descriptive_stats_func,
|
| 122 |
-
scatter_chart_generation_func, line_chart_generation_func, bar_chart_generation_func,
|
| 123 |
-
pie_chart_generation_func, histogram_generation_func,
|
| 124 |
-
box_chart_generation_func, correlation_heatmap_func, rolling_stats_func,
|
| 125 |
-
query_func, graphql_schema_query, graphql_csv_query,
|
| 126 |
-
kmeans_clustering_func, hypothesis_test_func,
|
| 127 |
)
|
| 128 |
-
|
| 129 |
-
|
| 130 |
-
|
| 131 |
-
|
| 132 |
-
|
| 133 |
-
|
| 134 |
-
|
| 135 |
-
|
| 136 |
-
|
| 137 |
-
|
| 138 |
-
"pie_chart_generation_func": pie_chart_generation_func,
|
| 139 |
-
"histogram_generation_func": histogram_generation_func,
|
| 140 |
-
"box_chart_generation_func": box_chart_generation_func,
|
| 141 |
-
"correlation_heatmap_func": correlation_heatmap_func,
|
| 142 |
-
"rolling_stats_func": rolling_stats_func,
|
| 143 |
-
"regression_func": regression_func,
|
| 144 |
-
"descriptive_stats_func": descriptive_stats_func,
|
| 145 |
-
"kmeans_clustering_func": kmeans_clustering_func,
|
| 146 |
-
"hypothesis_test_func": hypothesis_test_func,
|
| 147 |
-
}
|
| 148 |
-
|
| 149 |
-
if message_dict[session_hash][data_source] != None:
|
| 150 |
-
message_dict[session_hash][data_source].append(ChatMessage.from_user(message))
|
| 151 |
-
else:
|
| 152 |
-
messages = [
|
| 153 |
-
ChatMessage.from_system(system_message(data_source, titles, schema))
|
| 154 |
-
]
|
| 155 |
-
messages.append(ChatMessage.from_user(message))
|
| 156 |
-
message_dict[session_hash][data_source] = messages
|
| 157 |
-
|
| 158 |
-
active_tools = tools.tools_call(session_hash, data_source, titles)
|
| 159 |
-
response = chat_generator.run(messages=message_dict[session_hash][data_source], tools=active_tools)
|
| 160 |
|
| 161 |
while True:
|
| 162 |
-
# if
|
| 163 |
if response and response["replies"][0].meta["finish_reason"] == "tool_calls" or response["replies"][0].tool_calls:
|
| 164 |
function_calls = response["replies"][0].tool_calls
|
| 165 |
for function_call in function_calls:
|
| 166 |
-
|
| 167 |
## Parse function calling information
|
| 168 |
function_name = function_call.tool_name
|
| 169 |
function_args = function_call.arguments
|
| 170 |
|
| 171 |
-
## Find the
|
| 172 |
function_to_call = available_functions[function_name]
|
| 173 |
-
function_response = function_to_call(**function_args, session_hash=session_hash
|
| 174 |
-
print(function_name)
|
| 175 |
## Append function response to the messages list using `ChatMessage.from_tool`
|
| 176 |
-
|
| 177 |
-
response = chat_generator.run(messages=
|
| 178 |
|
| 179 |
# Regular Conversation
|
| 180 |
else:
|
| 181 |
-
|
| 182 |
break
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 183 |
|
| 184 |
-
|
|
|
|
| 1 |
+
from data_sources import process_data_upload
|
| 2 |
+
|
| 3 |
+
import gradio as gr
|
| 4 |
|
| 5 |
from haystack.dataclasses import ChatMessage
|
| 6 |
from haystack.components.generators.chat import OpenAIChatGenerator
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 7 |
|
| 8 |
+
import os
|
| 9 |
+
from getpass import getpass
|
| 10 |
+
from dotenv import load_dotenv
|
| 11 |
|
| 12 |
+
load_dotenv()
|
| 13 |
+
|
| 14 |
+
if "OPENAI_API_KEY" not in os.environ:
|
| 15 |
+
os.environ["OPENAI_API_KEY"] = getpass("Enter OpenAI API key:")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 16 |
|
| 17 |
+
chat_generator = OpenAIChatGenerator(model="gpt-4o")
|
| 18 |
+
response = None
|
| 19 |
+
messages = [
|
| 20 |
+
ChatMessage.from_system(
|
| 21 |
+
"You are a helpful and knowledgeable agent who has access to an SQL database which has a table called 'data_source'"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 22 |
)
|
| 23 |
+
]
|
| 24 |
+
|
| 25 |
+
def chatbot_with_fc(message, history, session_hash):
|
| 26 |
+
from functions import sqlite_query_func
|
| 27 |
+
from pipelines import rag_pipeline_func
|
| 28 |
+
import tools
|
| 29 |
+
|
| 30 |
+
available_functions = {"sql_query_func": sqlite_query_func, "rag_pipeline_func": rag_pipeline_func}
|
| 31 |
+
messages.append(ChatMessage.from_user(message))
|
| 32 |
+
response = chat_generator.run(messages=messages, generation_kwargs={"tools": tools.tools_call(session_hash)})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 33 |
|
| 34 |
while True:
|
| 35 |
+
# if OpenAI response is a tool call
|
| 36 |
if response and response["replies"][0].meta["finish_reason"] == "tool_calls" or response["replies"][0].tool_calls:
|
| 37 |
function_calls = response["replies"][0].tool_calls
|
| 38 |
for function_call in function_calls:
|
| 39 |
+
messages.append(ChatMessage.from_assistant(tool_calls=[function_call]))
|
| 40 |
## Parse function calling information
|
| 41 |
function_name = function_call.tool_name
|
| 42 |
function_args = function_call.arguments
|
| 43 |
|
| 44 |
+
## Find the correspoding function and call it with the given arguments
|
| 45 |
function_to_call = available_functions[function_name]
|
| 46 |
+
function_response = function_to_call(**function_args, session_hash=session_hash)
|
|
|
|
| 47 |
## Append function response to the messages list using `ChatMessage.from_tool`
|
| 48 |
+
messages.append(ChatMessage.from_tool(tool_result=function_response['reply'], origin=function_call))
|
| 49 |
+
response = chat_generator.run(messages=messages, generation_kwargs={"tools": tools.tools_call(session_hash)})
|
| 50 |
|
| 51 |
# Regular Conversation
|
| 52 |
else:
|
| 53 |
+
messages.append(response["replies"][0])
|
| 54 |
break
|
| 55 |
+
return response["replies"][0].text
|
| 56 |
+
|
| 57 |
+
def delete_db(req: gr.Request):
|
| 58 |
+
db_file_path = f'data_source_{req.session_hash}.db'
|
| 59 |
+
if os.path.exists(db_file_path):
|
| 60 |
+
os.remove(db_file_path)
|
| 61 |
+
|
| 62 |
+
def run_example(input):
|
| 63 |
+
return input
|
| 64 |
+
|
| 65 |
+
def example_display(input):
|
| 66 |
+
if input == None:
|
| 67 |
+
display = True
|
| 68 |
+
else:
|
| 69 |
+
display = False
|
| 70 |
+
return gr.update(visible=display)
|
| 71 |
+
|
| 72 |
+
css= ".file_marker .large{min-height:50px !important;} .example_btn{max-width:300px;}"
|
| 73 |
+
|
| 74 |
+
with gr.Blocks(css=css) as demo:
|
| 75 |
+
title = gr.HTML("<h1 style='text-align:center;'>Virtual Data Analyst</h1>")
|
| 76 |
+
description = gr.HTML("<p style='text-align:center;'>Upload a CSV file and chat with our virtual data analyst to get insights on your data set</p>")
|
| 77 |
+
example_file = gr.File(visible=False, value="samples/bank_marketing_campaign.csv")
|
| 78 |
+
example_btn = gr.Button(value="Try Me: bank_marketing_campaign.csv", elem_classes="example_btn", size="md", variant="primary")
|
| 79 |
+
file_output = gr.File(label="CSV File", show_label=True, elem_classes="file_marker", file_types=['.csv'])
|
| 80 |
+
example_btn.click(fn=run_example, inputs=example_file, outputs=file_output)
|
| 81 |
+
file_output.change(fn=example_display, inputs=file_output, outputs=example_btn)
|
| 82 |
+
|
| 83 |
+
@gr.render(inputs=file_output)
|
| 84 |
+
def data_options(filename, request: gr.Request):
|
| 85 |
+
print(filename)
|
| 86 |
+
if filename:
|
| 87 |
+
parameters = gr.Textbox(visible=False, value=request.session_hash)
|
| 88 |
+
bot = gr.Chatbot(type='messages', label="CSV Chat Window", show_label=True, render=False, visible=True, elem_classes="chatbot")
|
| 89 |
+
chat = gr.ChatInterface(
|
| 90 |
+
fn=chatbot_with_fc,
|
| 91 |
+
type='messages',
|
| 92 |
+
chatbot=bot,
|
| 93 |
+
title="Chat with your data file",
|
| 94 |
+
concurrency_limit=None,
|
| 95 |
+
examples=[
|
| 96 |
+
["Describe the dataset"],
|
| 97 |
+
["List the columns in the dataset"],
|
| 98 |
+
["What could this data be used for?"],
|
| 99 |
+
],
|
| 100 |
+
additional_inputs=parameters
|
| 101 |
+
)
|
| 102 |
+
process_upload(filename, request.session_hash)
|
| 103 |
+
|
| 104 |
+
def process_upload(upload_value, session_hash):
|
| 105 |
+
if upload_value:
|
| 106 |
+
process_data_upload(upload_value, session_hash)
|
| 107 |
+
return [], []
|
| 108 |
+
|
| 109 |
+
demo.unload(delete_db)
|
| 110 |
|
| 111 |
+
|
functions/query_functions.py
DELETED
|
@@ -1,229 +0,0 @@
|
|
| 1 |
-
from typing import List
|
| 2 |
-
from typing import AnyStr
|
| 3 |
-
from haystack import component
|
| 4 |
-
import pandas as pd
|
| 5 |
-
from pandasql import sqldf
|
| 6 |
-
pd.set_option('display.max_rows', None)
|
| 7 |
-
pd.set_option('display.max_columns', None)
|
| 8 |
-
pd.set_option('display.width', None)
|
| 9 |
-
pd.set_option('display.max_colwidth', None)
|
| 10 |
-
import sqlite3
|
| 11 |
-
import psycopg2
|
| 12 |
-
from pymongo import MongoClient
|
| 13 |
-
import pymongoarrow.monkey
|
| 14 |
-
import json
|
| 15 |
-
import pluck
|
| 16 |
-
from utils import TEMP_DIR
|
| 17 |
-
import ast
|
| 18 |
-
|
| 19 |
-
@component
|
| 20 |
-
class SQLiteQuery:
|
| 21 |
-
|
| 22 |
-
def __init__(self, sql_database: str):
|
| 23 |
-
self.connection = sqlite3.connect(sql_database, check_same_thread=False)
|
| 24 |
-
|
| 25 |
-
@component.output_types(results=List[str], queries=List[str])
|
| 26 |
-
def run(self, queries: AnyStr, session_hash):
|
| 27 |
-
print("ATTEMPTING TO RUN SQLITE QUERY")
|
| 28 |
-
dir_path = TEMP_DIR / str(session_hash)
|
| 29 |
-
results = []
|
| 30 |
-
result = pd.read_sql(queries, self.connection)
|
| 31 |
-
result.to_csv(f'{dir_path}/file_upload/query.csv', index=False)
|
| 32 |
-
column_names = list(result.columns)
|
| 33 |
-
results.append(f"{result}")
|
| 34 |
-
self.connection.close()
|
| 35 |
-
return {"results": results, "queries": queries, "csv_columns": column_names}
|
| 36 |
-
|
| 37 |
-
@component
|
| 38 |
-
class PostgreSQLQuery:
|
| 39 |
-
|
| 40 |
-
def __init__(self, url: str, sql_port: int, sql_user: str, sql_pass: str, sql_db_name: str):
|
| 41 |
-
self.connection = psycopg2.connect(
|
| 42 |
-
database=sql_db_name,
|
| 43 |
-
user=sql_user,
|
| 44 |
-
password=sql_pass,
|
| 45 |
-
host=url, # e.g., "localhost" or an IP address
|
| 46 |
-
port=sql_port # default is 5432
|
| 47 |
-
)
|
| 48 |
-
|
| 49 |
-
@component.output_types(results=List[str], queries=List[str])
|
| 50 |
-
def run(self, queries: AnyStr, session_hash):
|
| 51 |
-
print("ATTEMPTING TO RUN POSTGRESQL QUERY")
|
| 52 |
-
dir_path = TEMP_DIR / str(session_hash)
|
| 53 |
-
results = []
|
| 54 |
-
result = pd.read_sql_query(queries, self.connection)
|
| 55 |
-
result.to_csv(f'{dir_path}/sql/query.csv', index=False)
|
| 56 |
-
column_names = list(result.columns)
|
| 57 |
-
results.append(f"{result}")
|
| 58 |
-
self.connection.close()
|
| 59 |
-
return {"results": results, "queries": queries, "csv_columns": column_names}
|
| 60 |
-
|
| 61 |
-
@component
|
| 62 |
-
class DocDBQuery:
|
| 63 |
-
|
| 64 |
-
def __init__(self, connection_string: str, doc_db_name: str):
|
| 65 |
-
client = MongoClient(connection_string)
|
| 66 |
-
|
| 67 |
-
self.client = client
|
| 68 |
-
self.connection = client[doc_db_name]
|
| 69 |
-
|
| 70 |
-
@component.output_types(results=List[str], queries=List[str])
|
| 71 |
-
def run(self, aggregation_pipeline: List[str], db_collection, session_hash):
|
| 72 |
-
pymongoarrow.monkey.patch_all()
|
| 73 |
-
print("ATTEMPTING TO RUN MONGODB QUERY")
|
| 74 |
-
dir_path = TEMP_DIR / str(session_hash)
|
| 75 |
-
results = []
|
| 76 |
-
print(aggregation_pipeline)
|
| 77 |
-
|
| 78 |
-
aggregation_pipeline = aggregation_pipeline.replace(" ", "")
|
| 79 |
-
|
| 80 |
-
false_replace = [':false', ': false']
|
| 81 |
-
false_value = ':False'
|
| 82 |
-
true_replace = [':true', ': true']
|
| 83 |
-
true_value = ':True'
|
| 84 |
-
|
| 85 |
-
for replace in false_replace:
|
| 86 |
-
aggregation_pipeline = aggregation_pipeline.replace(replace, false_value)
|
| 87 |
-
for replace in true_replace:
|
| 88 |
-
aggregation_pipeline = aggregation_pipeline.replace(replace, true_value)
|
| 89 |
-
|
| 90 |
-
query_list = ast.literal_eval(aggregation_pipeline)
|
| 91 |
-
|
| 92 |
-
print("QUERY List")
|
| 93 |
-
print(query_list)
|
| 94 |
-
print(db_collection)
|
| 95 |
-
|
| 96 |
-
db = self.connection
|
| 97 |
-
collection = db[db_collection]
|
| 98 |
-
|
| 99 |
-
print(collection)
|
| 100 |
-
docs = collection.aggregate_pandas_all(query_list)
|
| 101 |
-
print("DATA FRAME COMPLETE")
|
| 102 |
-
docs.to_csv(f'{dir_path}/doc_db/query.csv', index=False)
|
| 103 |
-
column_names = list(docs.columns)
|
| 104 |
-
print("CSV COMPLETE")
|
| 105 |
-
results.append(f"{docs}")
|
| 106 |
-
self.client.close()
|
| 107 |
-
return {"results": results, "queries": aggregation_pipeline, "csv_columns": column_names}
|
| 108 |
-
|
| 109 |
-
@component
|
| 110 |
-
class GraphQLQuery:
|
| 111 |
-
|
| 112 |
-
def __init__(self):
|
| 113 |
-
|
| 114 |
-
self.connection = pluck
|
| 115 |
-
|
| 116 |
-
@component.output_types(results=List[str], queries=List[str])
|
| 117 |
-
def run(self, graphql_query, graphql_api_string, graphql_api_token, graphql_token_header, session_hash):
|
| 118 |
-
print("ATTEMPTING TO RUN GRAPHQL QUERY")
|
| 119 |
-
dir_path = TEMP_DIR / str(session_hash)
|
| 120 |
-
results = []
|
| 121 |
-
|
| 122 |
-
headers = {"Content-Type": "application/json"}
|
| 123 |
-
if graphql_token_header and graphql_api_token:
|
| 124 |
-
headers[graphql_token_header] = graphql_api_token
|
| 125 |
-
|
| 126 |
-
print(graphql_query)
|
| 127 |
-
|
| 128 |
-
response = self.connection.execute(url=graphql_api_string, headers=headers, query=graphql_query, column_names="short")
|
| 129 |
-
|
| 130 |
-
if response.errors:
|
| 131 |
-
raise ValueError(response.errors)
|
| 132 |
-
elif response.data:
|
| 133 |
-
print("DATA FRAME COMPLETE")
|
| 134 |
-
print(response)
|
| 135 |
-
response_frame = response.frames['default']
|
| 136 |
-
print("RESPONSE FRAME")
|
| 137 |
-
#print(response_frame)
|
| 138 |
-
|
| 139 |
-
response_frame.to_csv(f'{dir_path}/graphql/query.csv', index=False)
|
| 140 |
-
column_names = list(response_frame.columns)
|
| 141 |
-
print("CSV COMPLETE")
|
| 142 |
-
results.append(f"{response_frame}")
|
| 143 |
-
return {"results": results, "queries": graphql_query, "csv_columns": column_names}
|
| 144 |
-
|
| 145 |
-
def query_func(queries:AnyStr, session_hash, session_folder, args, **kwargs):
|
| 146 |
-
try:
|
| 147 |
-
print("QUERY")
|
| 148 |
-
print(queries)
|
| 149 |
-
if session_folder == "file_upload":
|
| 150 |
-
dir_path = TEMP_DIR / str(session_hash)
|
| 151 |
-
sql_query = SQLiteQuery(f'{dir_path}/file_upload/data_source.db')
|
| 152 |
-
result = sql_query.run(queries, session_hash)
|
| 153 |
-
elif session_folder == "sql":
|
| 154 |
-
sql_query = PostgreSQLQuery(args[0], args[1], args[2], args[3], args[4])
|
| 155 |
-
result = sql_query.run(queries, session_hash)
|
| 156 |
-
elif session_folder == 'doc_db':
|
| 157 |
-
doc_db_query = DocDBQuery(args[0], args[1])
|
| 158 |
-
result = doc_db_query.run(queries, kwargs['db_collection'], session_hash)
|
| 159 |
-
elif session_folder == 'graphql':
|
| 160 |
-
graphql_object = GraphQLQuery()
|
| 161 |
-
result = graphql_object.run(queries, args[0], args[1], args[2], session_hash)
|
| 162 |
-
print("RESULT")
|
| 163 |
-
print(result["csv_columns"])
|
| 164 |
-
if len(result["results"][0]) > 1000:
|
| 165 |
-
print("QUERY TOO LARGE")
|
| 166 |
-
return {"reply": f"""query result too large to be processed by llm, the query results are in our query.csv file.
|
| 167 |
-
The column names of this query.csv file are: {result["csv_columns"]}.
|
| 168 |
-
If you need to display the results directly, perhaps use the table_generation_func function."""}
|
| 169 |
-
else:
|
| 170 |
-
return {"reply": result["results"][0]}
|
| 171 |
-
|
| 172 |
-
except Exception as e:
|
| 173 |
-
reply = f"""There was an error running the {session_folder} Query = {queries}
|
| 174 |
-
The error is {e},
|
| 175 |
-
You should probably try again.
|
| 176 |
-
"""
|
| 177 |
-
print(reply)
|
| 178 |
-
return {"reply": reply}
|
| 179 |
-
|
| 180 |
-
def graphql_schema_query(graphql_type: AnyStr, session_hash, **kwargs):
|
| 181 |
-
dir_path = TEMP_DIR / str(session_hash)
|
| 182 |
-
try:
|
| 183 |
-
with open(f'{dir_path}/graphql/schema.json', 'r') as file:
|
| 184 |
-
data = json.load(file)
|
| 185 |
-
|
| 186 |
-
types_list = data["types"]
|
| 187 |
-
result = list(filter(lambda item: item["name"] == graphql_type, types_list))
|
| 188 |
-
|
| 189 |
-
print("SCHEMA RESULT")
|
| 190 |
-
print(graphql_type)
|
| 191 |
-
print(str(result))
|
| 192 |
-
|
| 193 |
-
return {"reply": str(result)}
|
| 194 |
-
|
| 195 |
-
except Exception as e:
|
| 196 |
-
reply = f"""There was an error querying our schema.json file with the type:{graphql_type}
|
| 197 |
-
The error is {e},
|
| 198 |
-
You should probably try again.
|
| 199 |
-
"""
|
| 200 |
-
print(reply)
|
| 201 |
-
return {"reply": reply}
|
| 202 |
-
|
| 203 |
-
def graphql_csv_query(csv_query: AnyStr, session_hash, **kwargs):
|
| 204 |
-
dir_path = TEMP_DIR / str(session_hash)
|
| 205 |
-
try:
|
| 206 |
-
query = pd.read_csv(f'{dir_path}/graphql/query.csv')
|
| 207 |
-
query.Name = 'query'
|
| 208 |
-
print("GRAPHQL CSV QUERY")
|
| 209 |
-
print(csv_query)
|
| 210 |
-
queried_df = sqldf(csv_query, locals())
|
| 211 |
-
print(queried_df)
|
| 212 |
-
column_names = list(queried_df.columns)
|
| 213 |
-
queried_df.to_csv(f'{dir_path}/graphql/query.csv', index=False)
|
| 214 |
-
|
| 215 |
-
if len(queried_df) > 1000:
|
| 216 |
-
print("CSV QUERY TOO LARGE")
|
| 217 |
-
return {"reply": f"""The new query results are in our query.csv file.
|
| 218 |
-
The column names of this query.csv file are: {column_names}.
|
| 219 |
-
If you need to display the results directly, perhaps use the table_generation_func function."""}
|
| 220 |
-
else:
|
| 221 |
-
return {"reply": str(queried_df)}
|
| 222 |
-
|
| 223 |
-
except Exception as e:
|
| 224 |
-
reply = f"""There was an error querying our query.csv file with the query:{csv_query}
|
| 225 |
-
The error is {e},
|
| 226 |
-
You should probably try again.
|
| 227 |
-
"""
|
| 228 |
-
print(reply)
|
| 229 |
-
return {"reply": reply}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
functions/sqlite_functions.py
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from typing import List
|
| 2 |
+
from haystack import component
|
| 3 |
+
import pandas as pd
|
| 4 |
+
import sqlite3
|
| 5 |
+
|
| 6 |
+
@component
|
| 7 |
+
class SQLiteQuery:
|
| 8 |
+
|
| 9 |
+
def __init__(self, sql_database: str):
|
| 10 |
+
self.connection = sqlite3.connect(sql_database, check_same_thread=False)
|
| 11 |
+
|
| 12 |
+
@component.output_types(results=List[str], queries=List[str])
|
| 13 |
+
def run(self, queries: List[str]):
|
| 14 |
+
print("ATTEMPTING TO RUN QUERY")
|
| 15 |
+
results = []
|
| 16 |
+
for query in queries:
|
| 17 |
+
result = pd.read_sql(query, self.connection)
|
| 18 |
+
results.append(f"{result}")
|
| 19 |
+
self.connection.close()
|
| 20 |
+
return {"results": results, "queries": queries}
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
def sqlite_query_func(queries: List[str], session_hash):
|
| 25 |
+
sql_query = SQLiteQuery(f'data_source_{session_hash}.db')
|
| 26 |
+
try:
|
| 27 |
+
result = sql_query.run(queries)
|
| 28 |
+
return {"reply": result["results"][0]}
|
| 29 |
+
|
| 30 |
+
except Exception as e:
|
| 31 |
+
reply = f"""There was an error running the SQL Query = {queries}
|
| 32 |
+
The error is {e},
|
| 33 |
+
You should probably try again.
|
| 34 |
+
"""
|
| 35 |
+
return {"reply": reply}
|
functions/stat_functions.py
DELETED
|
@@ -1,285 +0,0 @@
|
|
| 1 |
-
|
| 2 |
-
import pandas as pd
|
| 3 |
-
from typing import List
|
| 4 |
-
from utils import TEMP_DIR
|
| 5 |
-
import plotly.express as px
|
| 6 |
-
import plotly.io as pio
|
| 7 |
-
import os
|
| 8 |
-
from functions.chart_functions import scatter_chart_fig, llm_chart_data_scrub, _write_chart
|
| 9 |
-
from dotenv import load_dotenv
|
| 10 |
-
|
| 11 |
-
load_dotenv()
|
| 12 |
-
|
| 13 |
-
root_url = os.getenv("ROOT_URL", "")
|
| 14 |
-
|
| 15 |
-
def descriptive_stats_func(session_hash, session_folder, columns: List[str]=[], **kwargs):
|
| 16 |
-
print("DESCRIPTIVE STATISTICS")
|
| 17 |
-
try:
|
| 18 |
-
from html import escape
|
| 19 |
-
|
| 20 |
-
dir_path = TEMP_DIR / str(session_hash) / str(session_folder)
|
| 21 |
-
csv_query_path = f'{dir_path}/query.csv'
|
| 22 |
-
|
| 23 |
-
df = pd.read_csv(csv_query_path)
|
| 24 |
-
|
| 25 |
-
if columns:
|
| 26 |
-
df = df[[c for c in columns if c in df.columns]]
|
| 27 |
-
|
| 28 |
-
desc = df.describe().round(4)
|
| 29 |
-
|
| 30 |
-
header_cells = '<th style="background:#1e40af;">Statistic</th>' + ''.join(
|
| 31 |
-
f'<th>{escape(str(col))}</th>' for col in desc.columns
|
| 32 |
-
)
|
| 33 |
-
row_html = [
|
| 34 |
-
'<tr>'
|
| 35 |
-
+ f'<td style="font-weight:600;color:#1e40af;background:#eff6ff;white-space:nowrap;">{escape(str(idx))}</td>'
|
| 36 |
-
+ ''.join(f'<td>{escape(str(val))}</td>' for val in row)
|
| 37 |
-
+ '</tr>'
|
| 38 |
-
for idx, row in desc.iterrows()
|
| 39 |
-
]
|
| 40 |
-
|
| 41 |
-
style = (
|
| 42 |
-
'<style>'
|
| 43 |
-
'.vda-table-wrap{overflow-x:auto;margin:8px 0;border-radius:8px;border:1px solid #e5e7eb;}'
|
| 44 |
-
'.vda-table{width:100%;border-collapse:collapse;font-size:13px;font-family:Inter,system-ui,sans-serif;}'
|
| 45 |
-
'.vda-table thead th{background:#3B82F6;color:#fff;padding:9px 14px;text-align:left;white-space:nowrap;font-weight:600;}'
|
| 46 |
-
'.vda-table tbody td{padding:7px 14px;border-bottom:1px solid #f1f5f9;white-space:nowrap;}'
|
| 47 |
-
'.vda-table tbody tr:nth-child(even){background:#f8fafc;}'
|
| 48 |
-
'.vda-table tbody tr:last-child td{border-bottom:none;}'
|
| 49 |
-
'</style>'
|
| 50 |
-
)
|
| 51 |
-
table = (
|
| 52 |
-
'<div class="vda-table-wrap"><table class="vda-table">'
|
| 53 |
-
f'<thead><tr>{header_cells}</tr></thead>'
|
| 54 |
-
'<tbody>' + '\n'.join(row_html) + '</tbody>'
|
| 55 |
-
'</table></div>'
|
| 56 |
-
)
|
| 57 |
-
|
| 58 |
-
return {"reply": style + table}
|
| 59 |
-
|
| 60 |
-
except Exception as e:
|
| 61 |
-
print("DESCRIPTIVE STATS ERROR")
|
| 62 |
-
print(e)
|
| 63 |
-
return {"reply": f"There was an error generating descriptive statistics. Error: {e}. You should probably try again."}
|
| 64 |
-
|
| 65 |
-
|
| 66 |
-
def kmeans_clustering_func(feature_columns: List[str], x_column: str, y_column: str,
|
| 67 |
-
session_hash, session_folder, n_clusters: int = 3,
|
| 68 |
-
layout: List[dict] = [{}], **kwargs):
|
| 69 |
-
print("KMEANS CLUSTERING")
|
| 70 |
-
try:
|
| 71 |
-
from sklearn.cluster import KMeans
|
| 72 |
-
from sklearn.preprocessing import StandardScaler
|
| 73 |
-
from html import escape
|
| 74 |
-
|
| 75 |
-
dir_path = TEMP_DIR / str(session_hash) / str(session_folder)
|
| 76 |
-
chart_path = f'{dir_path}/chart.html'
|
| 77 |
-
csv_query_path = f'{dir_path}/query.csv'
|
| 78 |
-
|
| 79 |
-
df = pd.read_csv(csv_query_path)
|
| 80 |
-
|
| 81 |
-
feature_df = df[feature_columns].select_dtypes(include='number').dropna()
|
| 82 |
-
if feature_df.shape[1] < 1:
|
| 83 |
-
return {"reply": "No numeric feature columns found for clustering. Please refine your query to include numeric columns."}
|
| 84 |
-
|
| 85 |
-
X_scaled = StandardScaler().fit_transform(feature_df)
|
| 86 |
-
labels = KMeans(n_clusters=n_clusters, random_state=42, n_init=10).fit_predict(X_scaled)
|
| 87 |
-
|
| 88 |
-
df_clustered = df.loc[feature_df.index].copy()
|
| 89 |
-
df_clustered['Cluster'] = [f'Cluster {l}' for l in labels]
|
| 90 |
-
|
| 91 |
-
fig = px.scatter(
|
| 92 |
-
df_clustered, x=x_column, y=y_column, color='Cluster',
|
| 93 |
-
title=f'K-Means Clustering (k={n_clusters})',
|
| 94 |
-
)
|
| 95 |
-
fig.update_layout(font=dict(family='Inter, system-ui, sans-serif'))
|
| 96 |
-
|
| 97 |
-
_, layout_dict = llm_chart_data_scrub({}, layout)
|
| 98 |
-
if layout_dict:
|
| 99 |
-
fig.update_layout(**layout_dict)
|
| 100 |
-
|
| 101 |
-
chart_url = f'{root_url}/gradio_api/file/temp/{session_hash}/{session_folder}/chart.html'
|
| 102 |
-
iframe = _write_chart(fig, chart_path, chart_url)
|
| 103 |
-
|
| 104 |
-
cluster_summary = df_clustered.groupby('Cluster')[feature_columns].mean().round(3)
|
| 105 |
-
header_cells = '<th style="background:#1e40af;">Cluster</th>' + ''.join(
|
| 106 |
-
f'<th>{escape(str(col))}</th>' for col in cluster_summary.columns
|
| 107 |
-
)
|
| 108 |
-
row_html = [
|
| 109 |
-
'<tr>'
|
| 110 |
-
+ f'<td style="font-weight:600;color:#1e40af;background:#eff6ff;white-space:nowrap;">{escape(str(idx))}</td>'
|
| 111 |
-
+ ''.join(f'<td>{escape(str(val))}</td>' for val in row)
|
| 112 |
-
+ '</tr>'
|
| 113 |
-
for idx, row in cluster_summary.iterrows()
|
| 114 |
-
]
|
| 115 |
-
style = (
|
| 116 |
-
'<style>'
|
| 117 |
-
'.vda-table-wrap{overflow-x:auto;margin:8px 0;border-radius:8px;border:1px solid #e5e7eb;}'
|
| 118 |
-
'.vda-table{width:100%;border-collapse:collapse;font-size:13px;font-family:Inter,system-ui,sans-serif;}'
|
| 119 |
-
'.vda-table thead th{background:#3B82F6;color:#fff;padding:9px 14px;text-align:left;white-space:nowrap;font-weight:600;}'
|
| 120 |
-
'.vda-table tbody td{padding:7px 14px;border-bottom:1px solid #f1f5f9;white-space:nowrap;}'
|
| 121 |
-
'.vda-table tbody tr:nth-child(even){background:#f8fafc;}'
|
| 122 |
-
'.vda-table tbody tr:last-child td{border-bottom:none;}'
|
| 123 |
-
'</style>'
|
| 124 |
-
)
|
| 125 |
-
summary_table = (
|
| 126 |
-
'<div class="vda-table-wrap"><table class="vda-table">'
|
| 127 |
-
f'<thead><tr>{header_cells}</tr></thead>'
|
| 128 |
-
'<tbody>' + '\n'.join(row_html) + '</tbody>'
|
| 129 |
-
'</table></div>'
|
| 130 |
-
)
|
| 131 |
-
|
| 132 |
-
return {"reply": f'{iframe}\n\n**Cluster Centroids (feature means per cluster):**\n{style}{summary_table}'}
|
| 133 |
-
|
| 134 |
-
except Exception as e:
|
| 135 |
-
print("KMEANS CLUSTERING ERROR")
|
| 136 |
-
print(e)
|
| 137 |
-
return {"reply": f"There was an error running K-Means clustering. Error: {e}. You should probably try again."}
|
| 138 |
-
|
| 139 |
-
|
| 140 |
-
def hypothesis_test_func(test_type: str, column: str, session_hash, session_folder,
|
| 141 |
-
column2: str = "", group_column: str = "",
|
| 142 |
-
group_values: List[str] = [], pop_mean: float = 0.0, **kwargs):
|
| 143 |
-
print("HYPOTHESIS TEST")
|
| 144 |
-
try:
|
| 145 |
-
from scipy import stats
|
| 146 |
-
from html import escape
|
| 147 |
-
|
| 148 |
-
dir_path = TEMP_DIR / str(session_hash) / str(session_folder)
|
| 149 |
-
csv_query_path = f'{dir_path}/query.csv'
|
| 150 |
-
df = pd.read_csv(csv_query_path)
|
| 151 |
-
|
| 152 |
-
if test_type == "t_test_independent":
|
| 153 |
-
if not group_column or group_column not in df.columns:
|
| 154 |
-
return {"reply": "Please specify a valid group_column for the independent t-test."}
|
| 155 |
-
unique_groups = df[group_column].dropna().unique().tolist()
|
| 156 |
-
if group_values and len(group_values) == 2:
|
| 157 |
-
g1_label, g2_label = group_values[0], group_values[1]
|
| 158 |
-
elif len(unique_groups) == 2:
|
| 159 |
-
g1_label, g2_label = unique_groups[0], unique_groups[1]
|
| 160 |
-
else:
|
| 161 |
-
return {"reply": f"For an independent t-test, exactly 2 groups are needed. Found: {unique_groups}. Specify group_values with 2 entries."}
|
| 162 |
-
|
| 163 |
-
g1 = df[df[group_column] == g1_label][column].dropna()
|
| 164 |
-
g2 = df[df[group_column] == g2_label][column].dropna()
|
| 165 |
-
t_stat, p_value = stats.ttest_ind(g1, g2)
|
| 166 |
-
|
| 167 |
-
result_rows = [
|
| 168 |
-
("Test", "Independent Samples T-Test"),
|
| 169 |
-
("Column", column),
|
| 170 |
-
("Group Column", group_column),
|
| 171 |
-
(f"Group 1", str(g1_label)),
|
| 172 |
-
(f"Group 2", str(g2_label)),
|
| 173 |
-
(f"Group 1 Mean (n={len(g1)})", f"{g1.mean():.4f}"),
|
| 174 |
-
(f"Group 2 Mean (n={len(g2)})", f"{g2.mean():.4f}"),
|
| 175 |
-
("T-Statistic", f"{t_stat:.4f}"),
|
| 176 |
-
("P-Value", f"{p_value:.6f}"),
|
| 177 |
-
("Significant at α=0.05", "Yes ✓" if p_value < 0.05 else "No ✗"),
|
| 178 |
-
]
|
| 179 |
-
title = f"T-Test: {column} by {group_column}"
|
| 180 |
-
|
| 181 |
-
elif test_type == "t_test_one_sample":
|
| 182 |
-
sample = df[column].dropna()
|
| 183 |
-
t_stat, p_value = stats.ttest_1samp(sample, pop_mean)
|
| 184 |
-
result_rows = [
|
| 185 |
-
("Test", "One-Sample T-Test"),
|
| 186 |
-
("Column", column),
|
| 187 |
-
("Hypothesized Mean (μ₀)", f"{pop_mean:.4f}"),
|
| 188 |
-
(f"Sample Mean (n={len(sample)})", f"{sample.mean():.4f}"),
|
| 189 |
-
("Sample Std Dev", f"{sample.std():.4f}"),
|
| 190 |
-
("T-Statistic", f"{t_stat:.4f}"),
|
| 191 |
-
("P-Value", f"{p_value:.6f}"),
|
| 192 |
-
("Significant at α=0.05", "Yes ✓" if p_value < 0.05 else "No ✗"),
|
| 193 |
-
]
|
| 194 |
-
title = f"One-Sample T-Test: {column} vs μ={pop_mean}"
|
| 195 |
-
|
| 196 |
-
elif test_type == "chi_square":
|
| 197 |
-
if not column2 or column2 not in df.columns:
|
| 198 |
-
return {"reply": "Please specify a valid column2 for the chi-square test."}
|
| 199 |
-
contingency = pd.crosstab(df[column], df[column2])
|
| 200 |
-
chi2, p_value, dof, _ = stats.chi2_contingency(contingency)
|
| 201 |
-
result_rows = [
|
| 202 |
-
("Test", "Chi-Square Test of Independence"),
|
| 203 |
-
("Column 1", column),
|
| 204 |
-
("Column 2", column2),
|
| 205 |
-
("Chi-Square Statistic", f"{chi2:.4f}"),
|
| 206 |
-
("Degrees of Freedom", str(dof)),
|
| 207 |
-
("P-Value", f"{p_value:.6f}"),
|
| 208 |
-
("Significant at α=0.05", "Yes ✓" if p_value < 0.05 else "No ✗"),
|
| 209 |
-
]
|
| 210 |
-
title = f"Chi-Square: {column} × {column2}"
|
| 211 |
-
|
| 212 |
-
else:
|
| 213 |
-
return {"reply": f"Unknown test_type '{test_type}'. Use one of: t_test_independent, t_test_one_sample, chi_square."}
|
| 214 |
-
|
| 215 |
-
style = (
|
| 216 |
-
'<style>'
|
| 217 |
-
'.vda-table-wrap{overflow-x:auto;margin:8px 0;border-radius:8px;border:1px solid #e5e7eb;}'
|
| 218 |
-
'.vda-table{width:100%;border-collapse:collapse;font-size:13px;font-family:Inter,system-ui,sans-serif;}'
|
| 219 |
-
'.vda-table thead th{background:#3B82F6;color:#fff;padding:9px 14px;text-align:left;white-space:nowrap;font-weight:600;}'
|
| 220 |
-
'.vda-table tbody td{padding:7px 14px;border-bottom:1px solid #f1f5f9;white-space:nowrap;}'
|
| 221 |
-
'.vda-table tbody tr:nth-child(even){background:#f8fafc;}'
|
| 222 |
-
'.vda-table tbody tr:last-child td{border-bottom:none;}'
|
| 223 |
-
'</style>'
|
| 224 |
-
)
|
| 225 |
-
header_cells = f'<th style="background:#1e40af;" colspan="2">{escape(title)}</th>'
|
| 226 |
-
row_html = [
|
| 227 |
-
'<tr>'
|
| 228 |
-
+ f'<td style="font-weight:600;color:#1e40af;background:#eff6ff;white-space:nowrap;">{escape(label)}</td>'
|
| 229 |
-
+ f'<td>{escape(value)}</td>'
|
| 230 |
-
+ '</tr>'
|
| 231 |
-
for label, value in result_rows
|
| 232 |
-
]
|
| 233 |
-
table = (
|
| 234 |
-
'<div class="vda-table-wrap"><table class="vda-table">'
|
| 235 |
-
f'<thead><tr>{header_cells}</tr></thead>'
|
| 236 |
-
'<tbody>' + '\n'.join(row_html) + '</tbody>'
|
| 237 |
-
'</table></div>'
|
| 238 |
-
)
|
| 239 |
-
return {"reply": style + table}
|
| 240 |
-
|
| 241 |
-
except Exception as e:
|
| 242 |
-
print("HYPOTHESIS TEST ERROR")
|
| 243 |
-
print(e)
|
| 244 |
-
return {"reply": f"There was an error running the hypothesis test. Error: {e}. You should probably try again."}
|
| 245 |
-
|
| 246 |
-
|
| 247 |
-
def regression_func(independent_variables: List[str], dependent_variable: str, session_hash, session_folder, category: str='', **kwargs):
|
| 248 |
-
print("LINEAR REGRESSION CALCULATION")
|
| 249 |
-
print(independent_variables)
|
| 250 |
-
print(dependent_variable)
|
| 251 |
-
try:
|
| 252 |
-
dir_path = TEMP_DIR / str(session_hash) / str(session_folder)
|
| 253 |
-
chart_path = f'{dir_path}/chart.html'
|
| 254 |
-
csv_query_path = f'{dir_path}/query.csv'
|
| 255 |
-
|
| 256 |
-
df = pd.read_csv(csv_query_path)
|
| 257 |
-
|
| 258 |
-
if category in df.columns:
|
| 259 |
-
fig = scatter_chart_fig(df=df, x_column=independent_variables,y_column=dependent_variable,
|
| 260 |
-
category=category,trendline="ols")
|
| 261 |
-
else:
|
| 262 |
-
fig = scatter_chart_fig(df=df,x_column=independent_variables,y_column=dependent_variable,
|
| 263 |
-
trendline="ols")
|
| 264 |
-
|
| 265 |
-
chart_url = f'{root_url}/gradio_api/file/temp/{session_hash}/{session_folder}/chart.html'
|
| 266 |
-
iframe = _write_chart(fig, chart_path, chart_url)
|
| 267 |
-
|
| 268 |
-
results_frame = px.get_trendline_results(fig)
|
| 269 |
-
|
| 270 |
-
print("RESULTS")
|
| 271 |
-
print(results_frame)
|
| 272 |
-
print(results_frame.at[0, 'px_fit_results'])
|
| 273 |
-
results = results_frame.at[0, 'px_fit_results']
|
| 274 |
-
print(results.summary())
|
| 275 |
-
|
| 276 |
-
return {"reply": '{"regression_summary": %s, "regression_chart": %s' % (str(results.summary()), str(iframe))}
|
| 277 |
-
|
| 278 |
-
except Exception as e:
|
| 279 |
-
print("LINEAR REGRESSION ERROR")
|
| 280 |
-
print(e)
|
| 281 |
-
reply = f"""There was an error generating the linear regression calculation from {independent_variables} and {dependent_variable}
|
| 282 |
-
The error is {e},
|
| 283 |
-
You should probably try again.
|
| 284 |
-
"""
|
| 285 |
-
return {"reply": reply}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pipelines/__init__.py
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from .pipelines import rag_pipeline_func
|
| 2 |
+
|
| 3 |
+
__all__ = ["rag_pipeline_func"]
|
pipelines/pipelines.py
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from haystack import Pipeline
|
| 2 |
+
from haystack.components.builders import PromptBuilder
|
| 3 |
+
from haystack.components.generators.openai import OpenAIGenerator
|
| 4 |
+
from haystack.components.routers import ConditionalRouter
|
| 5 |
+
|
| 6 |
+
from functions import SQLiteQuery
|
| 7 |
+
|
| 8 |
+
from typing import List
|
| 9 |
+
import sqlite3
|
| 10 |
+
|
| 11 |
+
import os
|
| 12 |
+
from getpass import getpass
|
| 13 |
+
from dotenv import load_dotenv
|
| 14 |
+
|
| 15 |
+
load_dotenv()
|
| 16 |
+
|
| 17 |
+
if "OPENAI_API_KEY" not in os.environ:
|
| 18 |
+
os.environ["OPENAI_API_KEY"] = getpass("Enter OpenAI API key:")
|
| 19 |
+
|
| 20 |
+
from haystack.components.builders import PromptBuilder
|
| 21 |
+
from haystack.components.generators import OpenAIGenerator
|
| 22 |
+
|
| 23 |
+
llm = OpenAIGenerator(model="gpt-4o")
|
| 24 |
+
def rag_pipeline_func(queries: str, columns: str, session_hash):
|
| 25 |
+
sql_query = SQLiteQuery(f'data_source_{session_hash}.db')
|
| 26 |
+
|
| 27 |
+
connection = sqlite3.connect(f'data_source_{session_hash}.db')
|
| 28 |
+
cur=connection.execute('select * from data_source')
|
| 29 |
+
columns = [i[0] for i in cur.description]
|
| 30 |
+
cur.close()
|
| 31 |
+
|
| 32 |
+
#Rag Pipeline
|
| 33 |
+
prompt = PromptBuilder(template="""Please generate an SQL query. The query should answer the following Question: {{question}};
|
| 34 |
+
If the question cannot be answered given the provided table and columns, return 'no_answer'
|
| 35 |
+
The query is to be answered for the table is called 'data_source' with the following
|
| 36 |
+
Columns: {{columns}};
|
| 37 |
+
Answer:""")
|
| 38 |
+
|
| 39 |
+
routes = [
|
| 40 |
+
{
|
| 41 |
+
"condition": "{{'no_answer' not in replies[0]}}",
|
| 42 |
+
"output": "{{replies}}",
|
| 43 |
+
"output_name": "sql",
|
| 44 |
+
"output_type": List[str],
|
| 45 |
+
},
|
| 46 |
+
{
|
| 47 |
+
"condition": "{{'no_answer' in replies[0]}}",
|
| 48 |
+
"output": "{{question}}",
|
| 49 |
+
"output_name": "go_to_fallback",
|
| 50 |
+
"output_type": str,
|
| 51 |
+
},
|
| 52 |
+
]
|
| 53 |
+
|
| 54 |
+
router = ConditionalRouter(routes)
|
| 55 |
+
|
| 56 |
+
fallback_prompt = PromptBuilder(template="""User entered a query that cannot be answered with the given table.
|
| 57 |
+
The query was: {{question}} and the table had columns: {{columns}}.
|
| 58 |
+
Let the user know why the question cannot be answered""")
|
| 59 |
+
fallback_llm = OpenAIGenerator(model="gpt-4")
|
| 60 |
+
|
| 61 |
+
conditional_sql_pipeline = Pipeline()
|
| 62 |
+
conditional_sql_pipeline.add_component("prompt", prompt)
|
| 63 |
+
conditional_sql_pipeline.add_component("llm", llm)
|
| 64 |
+
conditional_sql_pipeline.add_component("router", router)
|
| 65 |
+
conditional_sql_pipeline.add_component("fallback_prompt", fallback_prompt)
|
| 66 |
+
conditional_sql_pipeline.add_component("fallback_llm", fallback_llm)
|
| 67 |
+
conditional_sql_pipeline.add_component("sql_querier", sql_query)
|
| 68 |
+
|
| 69 |
+
conditional_sql_pipeline.connect("prompt", "llm")
|
| 70 |
+
conditional_sql_pipeline.connect("llm.replies", "router.replies")
|
| 71 |
+
conditional_sql_pipeline.connect("router.sql", "sql_querier.queries")
|
| 72 |
+
conditional_sql_pipeline.connect("router.go_to_fallback", "fallback_prompt.question")
|
| 73 |
+
conditional_sql_pipeline.connect("fallback_prompt", "fallback_llm")
|
| 74 |
+
|
| 75 |
+
print("RAG PIPELINE FUNCTION")
|
| 76 |
+
result = conditional_sql_pipeline.run({"prompt": {"question": queries,
|
| 77 |
+
"columns": columns},
|
| 78 |
+
"router": {"question": queries},
|
| 79 |
+
"fallback_prompt": {"columns": columns}})
|
| 80 |
+
|
| 81 |
+
if 'sql_querier' in result:
|
| 82 |
+
reply = result['sql_querier']['results'][0]
|
| 83 |
+
elif 'fallback_llm' in result:
|
| 84 |
+
reply = result['fallback_llm']['replies'][0]
|
| 85 |
+
else:
|
| 86 |
+
reply = result["llm"]["replies"][0]
|
| 87 |
+
|
| 88 |
+
print("reply content")
|
| 89 |
+
print(reply.content)
|
| 90 |
+
|
| 91 |
+
return {"reply": reply.content}
|
requirements.txt
CHANGED
|
@@ -1,18 +1,4 @@
|
|
| 1 |
-
haystack-ai
|
| 2 |
-
anthropic-haystack
|
| 3 |
python-dotenv
|
| 4 |
gradio
|
| 5 |
-
pandas
|
| 6 |
-
plotly
|
| 7 |
-
openpyxl
|
| 8 |
-
statsmodels
|
| 9 |
-
xlrd
|
| 10 |
-
psycopg2-binary
|
| 11 |
-
pymongo
|
| 12 |
-
pymongoarrow
|
| 13 |
-
pymongo_schema
|
| 14 |
-
pandasql
|
| 15 |
-
pluck-graphql
|
| 16 |
-
certifi==2025.1.31
|
| 17 |
-
scipy
|
| 18 |
-
scikit-learn
|
|
|
|
| 1 |
+
haystack-ai
|
|
|
|
| 2 |
python-dotenv
|
| 3 |
gradio
|
| 4 |
+
pandas
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
samples/online_retail_data.csv
DELETED
|
The diff for this file is too large to render.
See raw diff
|
|
|
samples/tb_illness_data.csv
DELETED
|
The diff for this file is too large to render.
See raw diff
|
|
|
temp/.gitignore
DELETED
|
@@ -1,2 +0,0 @@
|
|
| 1 |
-
*
|
| 2 |
-
!.gitignore
|
|
|
|
|
|
|
|
|
templates/data_file.py
DELETED
|
@@ -1,286 +0,0 @@
|
|
| 1 |
-
import gradio as gr
|
| 2 |
-
from functions import example_question_generator, chatbot_func
|
| 3 |
-
from data_sources import process_data_upload
|
| 4 |
-
from utils import message_dict
|
| 5 |
-
import ast
|
| 6 |
-
import html as _html
|
| 7 |
-
|
| 8 |
-
def build_summary_modal(stats):
|
| 9 |
-
num_rows = stats['num_rows']
|
| 10 |
-
num_cols = stats['num_cols']
|
| 11 |
-
total_missing = stats['total_missing']
|
| 12 |
-
duplicate_rows = stats.get('duplicate_rows', 0)
|
| 13 |
-
file_size_bytes = stats.get('file_size_bytes', 0)
|
| 14 |
-
|
| 15 |
-
def _fmt_num(v):
|
| 16 |
-
try:
|
| 17 |
-
if v != v: return '—' # NaN
|
| 18 |
-
abs_v = abs(v)
|
| 19 |
-
if abs_v >= 1e9: return f"{v/1e9:.1f}B"
|
| 20 |
-
if abs_v >= 1e6: return f"{v/1e6:.1f}M"
|
| 21 |
-
if abs_v >= 1e3: return f"{v:,.0f}" if v == int(v) else f"{v:,.1f}"
|
| 22 |
-
return f"{v:,.0f}" if v == int(v) else f"{v:.2f}"
|
| 23 |
-
except Exception:
|
| 24 |
-
return str(v)
|
| 25 |
-
|
| 26 |
-
def _fmt_size(b):
|
| 27 |
-
if not b: return ''
|
| 28 |
-
if b < 1024: return f"{b} B"
|
| 29 |
-
if b < 1024 ** 2: return f"{b / 1024:.1f} KB"
|
| 30 |
-
if b < 1024 ** 3: return f"{b / 1024 ** 2:.1f} MB"
|
| 31 |
-
return f"{b / 1024 ** 3:.2f} GB"
|
| 32 |
-
|
| 33 |
-
file_size_label = _fmt_size(file_size_bytes)
|
| 34 |
-
dup_color = "#ef4444" if duplicate_rows > 0 else "#a16207"
|
| 35 |
-
dup_bg = "#fef2f2" if duplicate_rows > 0 else "#fefce8"
|
| 36 |
-
dup_border = "#fecaca" if duplicate_rows > 0 else "#fde68a"
|
| 37 |
-
|
| 38 |
-
dtype_rows_html = ""
|
| 39 |
-
for i, (col, dtype) in enumerate(stats['dtypes'].items()):
|
| 40 |
-
bg = "#ffffff" if i % 2 == 0 else "#f9fafb"
|
| 41 |
-
missing = stats['missing_per_col'].get(col, 0)
|
| 42 |
-
pct_missing = (missing / num_rows * 100) if num_rows > 0 else 0
|
| 43 |
-
missing_color = "#ef4444" if missing > 0 else "#9ca3af"
|
| 44 |
-
missing_weight = "600" if missing > 0 else "400"
|
| 45 |
-
missing_cell = f'{missing:,} <span style="color:#9ca3af;font-size:0.7rem;">({pct_missing:.1f}%)</span>'
|
| 46 |
-
|
| 47 |
-
unique = stats.get('unique_counts', {}).get(col, '—')
|
| 48 |
-
is_id = isinstance(unique, int) and num_rows > 0 and (unique / num_rows) >= 0.95 and unique > 10
|
| 49 |
-
id_badge = ' <span style="background:#fef3c7;color:#92400e;padding:1px 5px;border-radius:3px;font-size:0.65rem;vertical-align:middle;">ID?</span>' if is_id else ''
|
| 50 |
-
unique_cell = f'{unique:,}{id_badge}' if isinstance(unique, int) else str(unique)
|
| 51 |
-
|
| 52 |
-
cs = stats.get('col_stats', {}).get(col, {})
|
| 53 |
-
if cs.get('type') == 'numeric':
|
| 54 |
-
stats_cell = (
|
| 55 |
-
f'<span style="font-size:0.74rem;color:#6b7280;line-height:1.6;">'
|
| 56 |
-
f'{_fmt_num(cs["min"])} – {_fmt_num(cs["max"])}'
|
| 57 |
-
f'<br><span style="color:#9ca3af;">avg {_fmt_num(cs["mean"])}</span></span>'
|
| 58 |
-
)
|
| 59 |
-
elif cs.get('type') == 'datetime':
|
| 60 |
-
stats_cell = (
|
| 61 |
-
f'<span style="font-size:0.74rem;color:#6b7280;line-height:1.6;">'
|
| 62 |
-
f'{cs["min"]}<br>→ {cs["max"]}</span>'
|
| 63 |
-
)
|
| 64 |
-
else:
|
| 65 |
-
stats_cell = '<span style="color:#d1d5db;">—</span>'
|
| 66 |
-
|
| 67 |
-
dtype_rows_html += (
|
| 68 |
-
f'<tr style="background:{bg}">'
|
| 69 |
-
f'<td style="padding:7px 12px;border-bottom:1px solid #f3f4f6;color:#111827;white-space:nowrap;">{_html.escape(col)}</td>'
|
| 70 |
-
f'<td style="padding:7px 12px;border-bottom:1px solid #f3f4f6;white-space:nowrap;"><span style="background:#dbeafe;color:#1e40af;padding:2px 8px;border-radius:4px;font-size:0.74rem;">{dtype}</span></td>'
|
| 71 |
-
f'<td style="padding:7px 12px;border-bottom:1px solid #f3f4f6;text-align:right;color:{missing_color};font-weight:{missing_weight};white-space:nowrap;">{missing_cell}</td>'
|
| 72 |
-
f'<td style="padding:7px 12px;border-bottom:1px solid #f3f4f6;text-align:right;white-space:nowrap;color:#374151;">{unique_cell}</td>'
|
| 73 |
-
f'<td style="padding:7px 12px;border-bottom:1px solid #f3f4f6;">{stats_cell}</td>'
|
| 74 |
-
f'</tr>'
|
| 75 |
-
)
|
| 76 |
-
|
| 77 |
-
preview_headers_html = "".join(
|
| 78 |
-
f'<th style="padding:8px 12px;color:#6b7280;font-weight:500;border-bottom:1px solid #e5e7eb;white-space:nowrap;text-align:left;">{_html.escape(col)}</th>'
|
| 79 |
-
for col in stats['preview_cols']
|
| 80 |
-
)
|
| 81 |
-
|
| 82 |
-
preview_rows_html = ""
|
| 83 |
-
for i, row in enumerate(stats['preview']):
|
| 84 |
-
bg = "#ffffff" if i % 2 == 0 else "#f9fafb"
|
| 85 |
-
cells = "".join(
|
| 86 |
-
f'<td style="padding:7px 12px;border-bottom:1px solid #f3f4f6;color:#374151;white-space:nowrap;">{_html.escape(str(cell))}</td>'
|
| 87 |
-
for cell in row
|
| 88 |
-
)
|
| 89 |
-
preview_rows_html += f'<tr style="background:{bg}">{cells}</tr>'
|
| 90 |
-
|
| 91 |
-
size_tag = f'<span style="background:rgba(255,255,255,0.2);color:#fff;padding:2px 10px;border-radius:12px;font-size:0.75rem;font-weight:400;">{file_size_label}</span>' if file_size_label else ''
|
| 92 |
-
|
| 93 |
-
return f"""
|
| 94 |
-
<div class="vda-modal-overlay" style="position:fixed;inset:0;background:rgba(0,0,0,0.55);z-index:9999;display:flex;align-items:center;justify-content:center;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;">
|
| 95 |
-
<div style="background:#fff;border-radius:14px;width:90%;max-width:800px;max-height:88vh;display:flex;flex-direction:column;box-shadow:0 25px 50px -12px rgba(0,0,0,0.35);overflow:hidden;">
|
| 96 |
-
<div style="background:linear-gradient(135deg,#3B82F6,#0ea5e9);padding:16px 20px;display:flex;justify-content:space-between;align-items:center;flex-shrink:0;gap:12px;">
|
| 97 |
-
<div style="display:flex;align-items:center;gap:10px;">
|
| 98 |
-
<span style="color:#fff;font-weight:600;font-size:1rem;">Dataset Summary</span>
|
| 99 |
-
{size_tag}
|
| 100 |
-
</div>
|
| 101 |
-
<button onclick="document.querySelectorAll('.vda-modal-overlay').forEach(function(e){{e.remove()}})" style="background:rgba(255,255,255,0.2);border:none;color:#fff;width:30px;height:30px;border-radius:50%;cursor:pointer;font-size:1rem;line-height:1;flex-shrink:0;">✕</button>
|
| 102 |
-
</div>
|
| 103 |
-
<div style="padding:20px;overflow-y:auto;flex:1;">
|
| 104 |
-
<div style="display:grid;grid-template-columns:1fr 1fr 1fr 1fr;gap:10px;margin-bottom:20px;">
|
| 105 |
-
<div style="background:#eff6ff;border:1px solid #bfdbfe;border-radius:8px;padding:12px;text-align:center;">
|
| 106 |
-
<div style="font-size:1.4rem;font-weight:700;color:#1d4ed8;">{num_rows:,}</div>
|
| 107 |
-
<div style="font-size:0.7rem;color:#64748b;text-transform:uppercase;letter-spacing:0.06em;margin-top:4px;">Rows</div>
|
| 108 |
-
</div>
|
| 109 |
-
<div style="background:#f0fdf4;border:1px solid #bbf7d0;border-radius:8px;padding:12px;text-align:center;">
|
| 110 |
-
<div style="font-size:1.4rem;font-weight:700;color:#15803d;">{num_cols}</div>
|
| 111 |
-
<div style="font-size:0.7rem;color:#64748b;text-transform:uppercase;letter-spacing:0.06em;margin-top:4px;">Columns</div>
|
| 112 |
-
</div>
|
| 113 |
-
<div style="background:#fefce8;border:1px solid #fde68a;border-radius:8px;padding:12px;text-align:center;">
|
| 114 |
-
<div style="font-size:1.4rem;font-weight:700;color:#a16207;">{total_missing:,}</div>
|
| 115 |
-
<div style="font-size:0.7rem;color:#64748b;text-transform:uppercase;letter-spacing:0.06em;margin-top:4px;">Missing Values</div>
|
| 116 |
-
</div>
|
| 117 |
-
<div style="background:{dup_bg};border:1px solid {dup_border};border-radius:8px;padding:12px;text-align:center;">
|
| 118 |
-
<div style="font-size:1.4rem;font-weight:700;color:{dup_color};">{duplicate_rows:,}</div>
|
| 119 |
-
<div style="font-size:0.7rem;color:#64748b;text-transform:uppercase;letter-spacing:0.06em;margin-top:4px;">Duplicate Rows</div>
|
| 120 |
-
</div>
|
| 121 |
-
</div>
|
| 122 |
-
<div style="margin-bottom:20px;">
|
| 123 |
-
<div style="font-size:0.78rem;font-weight:600;color:#374151;text-transform:uppercase;letter-spacing:0.06em;margin-bottom:10px;">Column Info</div>
|
| 124 |
-
<div style="border:1px solid #e5e7eb;border-radius:8px;overflow:hidden;">
|
| 125 |
-
<div style="max-height:210px;overflow:auto;">
|
| 126 |
-
<table style="border-collapse:collapse;font-size:0.83rem;min-width:100%;">
|
| 127 |
-
<thead style="background:#f9fafb;position:sticky;top:0;z-index:1;">
|
| 128 |
-
<tr>
|
| 129 |
-
<th style="text-align:left;padding:8px 12px;color:#6b7280;font-weight:500;border-bottom:1px solid #e5e7eb;white-space:nowrap;">Column</th>
|
| 130 |
-
<th style="text-align:left;padding:8px 12px;color:#6b7280;font-weight:500;border-bottom:1px solid #e5e7eb;white-space:nowrap;">Type</th>
|
| 131 |
-
<th style="text-align:right;padding:8px 12px;color:#6b7280;font-weight:500;border-bottom:1px solid #e5e7eb;white-space:nowrap;">Missing</th>
|
| 132 |
-
<th style="text-align:right;padding:8px 12px;color:#6b7280;font-weight:500;border-bottom:1px solid #e5e7eb;white-space:nowrap;">Unique</th>
|
| 133 |
-
<th style="text-align:left;padding:8px 12px;color:#6b7280;font-weight:500;border-bottom:1px solid #e5e7eb;white-space:nowrap;">Stats / Range</th>
|
| 134 |
-
</tr>
|
| 135 |
-
</thead>
|
| 136 |
-
<tbody>{dtype_rows_html}</tbody>
|
| 137 |
-
</table>
|
| 138 |
-
</div>
|
| 139 |
-
</div>
|
| 140 |
-
</div>
|
| 141 |
-
<div>
|
| 142 |
-
<div style="font-size:0.78rem;font-weight:600;color:#374151;text-transform:uppercase;letter-spacing:0.06em;margin-bottom:10px;">Data Preview (first 5 rows)</div>
|
| 143 |
-
<div style="border:1px solid #e5e7eb;border-radius:8px;overflow:hidden;">
|
| 144 |
-
<div style="overflow:auto;max-height:200px;">
|
| 145 |
-
<table style="border-collapse:collapse;font-size:0.8rem;">
|
| 146 |
-
<thead style="background:#f9fafb;position:sticky;top:0;z-index:1;">
|
| 147 |
-
<tr>{preview_headers_html}</tr>
|
| 148 |
-
</thead>
|
| 149 |
-
<tbody>{preview_rows_html}</tbody>
|
| 150 |
-
</table>
|
| 151 |
-
</div>
|
| 152 |
-
</div>
|
| 153 |
-
</div>
|
| 154 |
-
</div>
|
| 155 |
-
</div>
|
| 156 |
-
</div>
|
| 157 |
-
"""
|
| 158 |
-
|
| 159 |
-
def run_example(input):
|
| 160 |
-
return input
|
| 161 |
-
|
| 162 |
-
def example_display(input):
|
| 163 |
-
if input == None:
|
| 164 |
-
display = True
|
| 165 |
-
else:
|
| 166 |
-
display = False
|
| 167 |
-
return [gr.update(visible=display), gr.update(visible=display), gr.update(visible=display), gr.update(visible=display)]
|
| 168 |
-
|
| 169 |
-
with gr.Blocks() as demo:
|
| 170 |
-
description = gr.HTML("""
|
| 171 |
-
<div class="max-w-4xl mx-auto mb-12 text-center">
|
| 172 |
-
<div class="bg-blue-50 border border-blue-200 rounded-lg max-w-2xl mx-auto">
|
| 173 |
-
<h2 class="font-semibold text-blue-800 ">
|
| 174 |
-
<i class="fas fa-info-circle mr-2"></i>Supported Files
|
| 175 |
-
</h2>
|
| 176 |
-
<div class="flex flex-wrap justify-center gap-3 pb-4 text-blue-700">
|
| 177 |
-
<span class="tooltip">
|
| 178 |
-
<i class="fas fa-file-csv mr-1"></i>CSV
|
| 179 |
-
<span class="tooltip-text">Comma-separated values</span>
|
| 180 |
-
</span>
|
| 181 |
-
<span class="tooltip">
|
| 182 |
-
<i class="fas fa-file-alt mr-1"></i>TSV
|
| 183 |
-
<span class="tooltip-text">Tab-separated values</span>
|
| 184 |
-
</span>
|
| 185 |
-
<span class="tooltip">
|
| 186 |
-
<i class="fas fa-file-alt mr-1"></i>TXT
|
| 187 |
-
<span class="tooltip-text">Text files</span>
|
| 188 |
-
</span>
|
| 189 |
-
<span class="tooltip">
|
| 190 |
-
<i class="fas fa-file-excel mr-1"></i>XLS/XLSX
|
| 191 |
-
<span class="tooltip-text">Excel spreadsheets</span>
|
| 192 |
-
</span>
|
| 193 |
-
<span class="tooltip">
|
| 194 |
-
<i class="fas fa-file-code mr-1"></i>XML
|
| 195 |
-
<span class="tooltip-text">XML documents</span>
|
| 196 |
-
</span>
|
| 197 |
-
<span class="tooltip">
|
| 198 |
-
<i class="fas fa-file-code mr-1"></i>JSON
|
| 199 |
-
<span class="tooltip-text">JSON data files</span>
|
| 200 |
-
</span>
|
| 201 |
-
</div>
|
| 202 |
-
</div>
|
| 203 |
-
</div>
|
| 204 |
-
""", elem_classes="description_component")
|
| 205 |
-
example_file_1 = gr.File(visible=False, value="samples/bank_marketing_campaign.csv")
|
| 206 |
-
example_file_2 = gr.File(visible=False, value="samples/online_retail_data.csv")
|
| 207 |
-
example_file_3 = gr.File(visible=False, value="samples/tb_illness_data.csv")
|
| 208 |
-
with gr.Row():
|
| 209 |
-
example_btn_1 = gr.Button(value="Try Me: bank_marketing_campaign.csv", elem_classes="sample-btn bg-gradient-to-r from-blue-500 to-sky-600 text-white p-6 rounded-lg text-left hover:shadow-lg", size="md", variant="primary")
|
| 210 |
-
example_btn_2 = gr.Button(value="Try Me: online_retail_data.csv", elem_classes="sample-btn bg-gradient-to-r from-blue-500 to-sky-600 text-white p-6 rounded-lg text-left hover:shadow-lg", size="md", variant="primary")
|
| 211 |
-
example_btn_3 = gr.Button(value="Try Me: tb_illness_data.csv", elem_classes="sample-btn bg-gradient-to-r from-blue-500 to-sky-600 text-white p-6 rounded-lg text-left hover:shadow-lg", size="md", variant="primary")
|
| 212 |
-
|
| 213 |
-
file_output = gr.File(label="Data File (CSV, TSV, TXT, XLS, XLSX, XML, JSON)", show_label=True, elem_classes="file_marker drop-zone border-2 border-dashed border-gray-300 rounded-lg hover:border-primary cursor-pointer bg-gray-50 hover:bg-blue-50 transition-colors duration-300", file_types=['.csv', '.xlsx', '.txt', '.json', '.ndjson', '.xml', '.xls', '.tsv'])
|
| 214 |
-
example_btn_1.click(fn=run_example, inputs=example_file_1, outputs=file_output)
|
| 215 |
-
example_btn_2.click(fn=run_example, inputs=example_file_2, outputs=file_output)
|
| 216 |
-
example_btn_3.click(fn=run_example, inputs=example_file_3, outputs=file_output)
|
| 217 |
-
file_output.change(fn=example_display, inputs=file_output, outputs=[example_btn_1, example_btn_2, example_btn_3, description])
|
| 218 |
-
|
| 219 |
-
@gr.render(inputs=file_output)
|
| 220 |
-
def data_options(filename, request: gr.Request):
|
| 221 |
-
print(filename)
|
| 222 |
-
if request.session_hash not in message_dict:
|
| 223 |
-
message_dict[request.session_hash] = {}
|
| 224 |
-
message_dict[request.session_hash]['file_upload'] = None
|
| 225 |
-
if filename:
|
| 226 |
-
process_message = process_upload(filename, request.session_hash)
|
| 227 |
-
gr.HTML(value=process_message[1], padding=False)
|
| 228 |
-
if process_message[0] == "success":
|
| 229 |
-
gr.HTML(value=build_summary_modal(process_message[3]), padding=False)
|
| 230 |
-
if "bank_marketing_campaign" in filename:
|
| 231 |
-
example_questions = [
|
| 232 |
-
["Describe the dataset"],
|
| 233 |
-
["What levels of education have the highest and lowest average balance?"],
|
| 234 |
-
["What job is most and least common for a yes response from the individuals, not counting 'unknown'?"],
|
| 235 |
-
["Can you generate a bar chart of education vs. average balance?"],
|
| 236 |
-
["Can you generate a table of levels of education versus average balance, percent married, percent with a loan, and percent in default?"],
|
| 237 |
-
["Can we predict the relationship between the number of contacts performed before this campaign and the average balance?"],
|
| 238 |
-
["Can you plot the number of contacts performed before this campaign versus the duration and use balance as the size in a bubble chart?"]
|
| 239 |
-
]
|
| 240 |
-
elif "online_retail_data" in filename:
|
| 241 |
-
example_questions = [
|
| 242 |
-
["Describe the dataset"],
|
| 243 |
-
["What month had the highest revenue?"],
|
| 244 |
-
["Is revenue higher in the morning or afternoon?"],
|
| 245 |
-
["Can you generate a line graph of revenue per month?"],
|
| 246 |
-
["Can you generate a table of revenue per month?"],
|
| 247 |
-
["Can we predict how time of day affects transaction value in this data set?"],
|
| 248 |
-
["Can you plot revenue per month with size being the number of units sold that month in a bubble chart?"]
|
| 249 |
-
]
|
| 250 |
-
else:
|
| 251 |
-
try:
|
| 252 |
-
generated_examples = ast.literal_eval(example_question_generator(request.session_hash, 'file_upload', '', process_message[2], ''))
|
| 253 |
-
example_questions = [["Describe the dataset"]]
|
| 254 |
-
for example in generated_examples:
|
| 255 |
-
example_questions.append([example])
|
| 256 |
-
except Exception as e:
|
| 257 |
-
print("DATA FILE QUESTION GENERATION ERROR")
|
| 258 |
-
print(e)
|
| 259 |
-
example_questions = [
|
| 260 |
-
["Describe the dataset"],
|
| 261 |
-
["List the columns in the dataset"],
|
| 262 |
-
["What could this data be used for?"],
|
| 263 |
-
]
|
| 264 |
-
session_hash = gr.Textbox(visible=False, value=request.session_hash)
|
| 265 |
-
data_source = gr.Textbox(visible=False, value='file_upload')
|
| 266 |
-
schema = gr.Textbox(visible=False, value='')
|
| 267 |
-
titles = gr.Textbox(value=process_message[2], interactive=False, visible=False)
|
| 268 |
-
bot = gr.Chatbot(type='messages', label="CSV Chat Window", render_markdown=True, sanitize_html=False, show_label=True, render=False, visible=True, elem_classes="chatbot")
|
| 269 |
-
chat = gr.ChatInterface(
|
| 270 |
-
fn=chatbot_func,
|
| 271 |
-
type='messages',
|
| 272 |
-
chatbot=bot,
|
| 273 |
-
title="Chat with your data file",
|
| 274 |
-
concurrency_limit=None,
|
| 275 |
-
examples=example_questions,
|
| 276 |
-
additional_inputs=[session_hash, data_source, titles, schema]
|
| 277 |
-
)
|
| 278 |
-
|
| 279 |
-
def process_upload(upload_value, session_hash):
|
| 280 |
-
if upload_value:
|
| 281 |
-
process_message = process_data_upload(upload_value, session_hash)
|
| 282 |
-
return process_message
|
| 283 |
-
|
| 284 |
-
|
| 285 |
-
if __name__ == "__main__":
|
| 286 |
-
demo.launch()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
templates/doc_db.py
DELETED
|
@@ -1,105 +0,0 @@
|
|
| 1 |
-
import ast
|
| 2 |
-
import gradio as gr
|
| 3 |
-
from functions import example_question_generator, chatbot_func
|
| 4 |
-
from data_sources import connect_doc_db
|
| 5 |
-
from utils import message_dict
|
| 6 |
-
|
| 7 |
-
with gr.Blocks() as demo:
|
| 8 |
-
with gr.Accordion("ℹ️ About the MongoDB Connector", open=False):
|
| 9 |
-
gr.HTML("""
|
| 10 |
-
<div class="max-w-4xl mx-auto text-center">
|
| 11 |
-
<div class="bg-blue-50 border border-blue-200 rounded-lg max-w-2xl mx-auto p-4">
|
| 12 |
-
<p>Connect to a MongoDB database and query it using natural language.</p>
|
| 13 |
-
<p style="font-weight:bold;">
|
| 14 |
-
No credentials are retained — they are passed as session variables and disappear when you leave or refresh.
|
| 15 |
-
Queries use PyMongoArrow's <code>aggregate_pandas_all</code>, which cannot delete, drop, or insert documents.
|
| 16 |
-
Use caution connecting production databases to third-party tools.
|
| 17 |
-
</p>
|
| 18 |
-
<p>Contact me if you'd like this built for your organization with proper infrastructure and security controls.</p>
|
| 19 |
-
</div>
|
| 20 |
-
</div>
|
| 21 |
-
""")
|
| 22 |
-
|
| 23 |
-
gr.HTML("""
|
| 24 |
-
<div style="max-width:560px;margin:8px auto 4px;padding:8px 14px;background:#f0f9ff;
|
| 25 |
-
border:1px solid #bae6fd;border-radius:8px;text-align:center;">
|
| 26 |
-
<p style="margin:0;font-size:13px;color:#0369a1;">
|
| 27 |
-
<i class="fas fa-flask" style="margin-right:6px;"></i>
|
| 28 |
-
<strong>Demo credentials pre-filled.</strong>
|
| 29 |
-
Replace with your own database details to analyze your own data.
|
| 30 |
-
</p>
|
| 31 |
-
</div>
|
| 32 |
-
""")
|
| 33 |
-
|
| 34 |
-
connection_string = gr.Textbox(label="Connection String", value="dataanalyst0.l1klmww.mongodb.net/")
|
| 35 |
-
with gr.Row():
|
| 36 |
-
connection_user = gr.Textbox(label="Connection User", value="virtual-data-analyst")
|
| 37 |
-
connection_password = gr.Textbox(label="Connection Password", value="zcpbmoGJ3mC8o", type="password")
|
| 38 |
-
doc_db_name = gr.Textbox(label="Database Name", value="sample_mflix")
|
| 39 |
-
|
| 40 |
-
gr.HTML("""
|
| 41 |
-
<p style="text-align:center;font-size:13px;color:#6b7280;margin:4px 0 8px;">
|
| 42 |
-
<i class="fas fa-circle-info" style="margin-right:4px;"></i>
|
| 43 |
-
Schema analysis runs on connect — this may take 1–2 minutes for large databases.
|
| 44 |
-
</p>
|
| 45 |
-
""")
|
| 46 |
-
submit = gr.Button(value="Connect", variant="primary")
|
| 47 |
-
|
| 48 |
-
@gr.render(inputs=[connection_string, connection_user, connection_password, doc_db_name], triggers=[submit.click])
|
| 49 |
-
def db_chat(request: gr.Request, connection_string=connection_string.value, connection_user=connection_user.value, connection_password=connection_password.value, doc_db_name=doc_db_name.value):
|
| 50 |
-
if request.session_hash not in message_dict:
|
| 51 |
-
message_dict[request.session_hash] = {}
|
| 52 |
-
message_dict[request.session_hash]['doc_db'] = None
|
| 53 |
-
connection_login_value = "mongodb+srv://" + connection_user + ":" + connection_password + "@" + connection_string
|
| 54 |
-
if connection_login_value:
|
| 55 |
-
print("MONGO APP")
|
| 56 |
-
process_message = process_doc_db(connection_login_value, doc_db_name, request.session_hash)
|
| 57 |
-
gr.HTML(value=process_message[1], padding=False)
|
| 58 |
-
if process_message[0] == "success":
|
| 59 |
-
if "dataanalyst0.l1klmww.mongodb.net" in connection_login_value:
|
| 60 |
-
example_questions = [
|
| 61 |
-
["Describe the dataset"],
|
| 62 |
-
["What are the top 5 most common movie genres?"],
|
| 63 |
-
["How do user comment counts on a movie correlate with the movie award wins?"],
|
| 64 |
-
["Can you generate a pie chart showing the top 10 states with the most movie theaters?"],
|
| 65 |
-
["What are the top 10 most represented directors in the database?"],
|
| 66 |
-
["What are the different movie categories and how many movies are in each category?"]
|
| 67 |
-
]
|
| 68 |
-
else:
|
| 69 |
-
try:
|
| 70 |
-
generated_examples = ast.literal_eval(example_question_generator(request.session_hash, 'doc_db', doc_db_name, process_message[2], process_message[3]))
|
| 71 |
-
example_questions = [["Describe the dataset"]]
|
| 72 |
-
for example in generated_examples:
|
| 73 |
-
example_questions.append([example])
|
| 74 |
-
except Exception as e:
|
| 75 |
-
print("DOC DB QUESTION GENERATION ERROR")
|
| 76 |
-
print(e)
|
| 77 |
-
example_questions = [
|
| 78 |
-
["Describe the dataset"],
|
| 79 |
-
["List the collections in the database"],
|
| 80 |
-
["What could this data be used for?"],
|
| 81 |
-
]
|
| 82 |
-
session_hash = gr.Textbox(visible=False, value=request.session_hash)
|
| 83 |
-
db_connection_string = gr.Textbox(visible=False, value=connection_login_value)
|
| 84 |
-
db_name = gr.Textbox(visible=False, value=doc_db_name)
|
| 85 |
-
titles = gr.Textbox(value=process_message[2], interactive=False, label="DB Collections")
|
| 86 |
-
data_source = gr.Textbox(visible=False, value='doc_db')
|
| 87 |
-
schema = gr.Textbox(visible=False, value=process_message[3])
|
| 88 |
-
bot = gr.Chatbot(type='messages', label="MongoDB Chat Window", render_markdown=True, sanitize_html=False, show_label=True, render=False, visible=True, elem_classes="chatbot")
|
| 89 |
-
chat = gr.ChatInterface(
|
| 90 |
-
fn=chatbot_func,
|
| 91 |
-
type='messages',
|
| 92 |
-
chatbot=bot,
|
| 93 |
-
title="Chat with your Database",
|
| 94 |
-
examples=example_questions,
|
| 95 |
-
concurrency_limit=None,
|
| 96 |
-
additional_inputs=[session_hash, data_source, titles, schema, db_connection_string, db_name]
|
| 97 |
-
)
|
| 98 |
-
|
| 99 |
-
def process_doc_db(connection_string, nosql_db_name, session_hash):
|
| 100 |
-
if connection_string:
|
| 101 |
-
process_message = connect_doc_db(connection_string, nosql_db_name, session_hash)
|
| 102 |
-
return process_message
|
| 103 |
-
|
| 104 |
-
if __name__ == "__main__":
|
| 105 |
-
demo.launch()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
templates/graphql.py
DELETED
|
@@ -1,110 +0,0 @@
|
|
| 1 |
-
import ast
|
| 2 |
-
import gradio as gr
|
| 3 |
-
from functions import example_question_generator, chatbot_func
|
| 4 |
-
from data_sources import connect_graphql
|
| 5 |
-
from utils import message_dict
|
| 6 |
-
|
| 7 |
-
import os
|
| 8 |
-
from dotenv import load_dotenv
|
| 9 |
-
|
| 10 |
-
load_dotenv()
|
| 11 |
-
|
| 12 |
-
graphql_sample_endpoint = os.getenv("GRAPHQL_SAMPLE_ENDPOINT")
|
| 13 |
-
graphql_sample_api_token = os.getenv("GRAPHQL_SAMPLE_API_TOKEN")
|
| 14 |
-
graphql_sample_header_name = os.getenv("GRAPHQL_SAMPLE_HEADER_NAME")
|
| 15 |
-
|
| 16 |
-
with gr.Blocks() as demo:
|
| 17 |
-
with gr.Accordion("ℹ️ About the GraphQL Connector", open=False):
|
| 18 |
-
gr.HTML("""
|
| 19 |
-
<div class="max-w-4xl mx-auto text-center">
|
| 20 |
-
<div class="bg-blue-50 border border-blue-200 rounded-lg max-w-2xl mx-auto p-4">
|
| 21 |
-
<p>Connect to any GraphQL API endpoint and query it using natural language.</p>
|
| 22 |
-
<p style="font-weight:bold;">
|
| 23 |
-
API querying is the most experimental feature and performance may vary.
|
| 24 |
-
No credentials are retained — they are passed as session variables and disappear when you leave or refresh.
|
| 25 |
-
Mutations are not exposed and the agent is instructed not to alter data, though restricting
|
| 26 |
-
your API token's permissions is still strongly recommended.
|
| 27 |
-
</p>
|
| 28 |
-
<p>Contact me if you'd like this built for your organization with proper infrastructure and security controls.</p>
|
| 29 |
-
</div>
|
| 30 |
-
</div>
|
| 31 |
-
""")
|
| 32 |
-
|
| 33 |
-
gr.HTML("""
|
| 34 |
-
<div style="max-width:560px;margin:8px auto 4px;padding:8px 14px;background:#f0f9ff;
|
| 35 |
-
border:1px solid #bae6fd;border-radius:8px;text-align:center;">
|
| 36 |
-
<p style="margin:0;font-size:13px;color:#0369a1;">
|
| 37 |
-
<i class="fas fa-flask" style="margin-right:6px;"></i>
|
| 38 |
-
<strong>Demo credentials pre-filled.</strong>
|
| 39 |
-
Replace with your own endpoint and token to analyze your own API.
|
| 40 |
-
</p>
|
| 41 |
-
</div>
|
| 42 |
-
""")
|
| 43 |
-
|
| 44 |
-
graphql_url = gr.Textbox(label="GraphQL Endpoint URL", value=graphql_sample_endpoint)
|
| 45 |
-
with gr.Row():
|
| 46 |
-
api_token_header_name = gr.Textbox(label="API Token Header Name", value=graphql_sample_header_name)
|
| 47 |
-
api_token = gr.Textbox(label="API Token", value=graphql_sample_api_token, type="password")
|
| 48 |
-
|
| 49 |
-
submit = gr.Button(value="Connect", variant="primary")
|
| 50 |
-
|
| 51 |
-
@gr.render(inputs=[graphql_url, api_token, api_token_header_name], triggers=[submit.click])
|
| 52 |
-
def api_chat(request: gr.Request, graphql_url=graphql_url.value, api_token=api_token.value, api_token_header_name=api_token_header_name.value):
|
| 53 |
-
if request.session_hash not in message_dict:
|
| 54 |
-
message_dict[request.session_hash] = {}
|
| 55 |
-
message_dict[request.session_hash]['graphql'] = None
|
| 56 |
-
if graphql_url:
|
| 57 |
-
print("GraphQL API")
|
| 58 |
-
process_message = process_graphql(graphql_url, api_token, api_token_header_name, request.session_hash)
|
| 59 |
-
gr.HTML(value=process_message[1], padding=False)
|
| 60 |
-
if process_message[0] == "success":
|
| 61 |
-
if "qdl-app-testing" in graphql_url:
|
| 62 |
-
example_questions = [
|
| 63 |
-
["Describe the dataset"],
|
| 64 |
-
["What is the total revenue for this shopify store?"],
|
| 65 |
-
["What is the average duration from the fulfillment of an order to its delivery?"],
|
| 66 |
-
["What is the total value of orders processed in the current month?"],
|
| 67 |
-
["Which product has the highest number of variants in the inventory?"],
|
| 68 |
-
["How many gift cards have been issued this year, and what is their total value?"],
|
| 69 |
-
["How many active apps are currently installed on the store?"],
|
| 70 |
-
["What is the total count of abandoned checkouts over the last month?"]
|
| 71 |
-
]
|
| 72 |
-
else:
|
| 73 |
-
try:
|
| 74 |
-
generated_examples = ast.literal_eval(example_question_generator(request.session_hash, 'graphql', graphql_url, process_message[2], ''))
|
| 75 |
-
example_questions = [["Describe the dataset"]]
|
| 76 |
-
for example in generated_examples:
|
| 77 |
-
example_questions.append([example])
|
| 78 |
-
except Exception as e:
|
| 79 |
-
print("GRAPHQL QUESTION GENERATION ERROR")
|
| 80 |
-
print(e)
|
| 81 |
-
example_questions = [
|
| 82 |
-
["Describe the dataset"],
|
| 83 |
-
["List the types in this API"],
|
| 84 |
-
["What could this data be used for?"],
|
| 85 |
-
]
|
| 86 |
-
session_hash = gr.Textbox(visible=False, value=request.session_hash)
|
| 87 |
-
graphql_api_string = gr.Textbox(visible=False, value=graphql_url)
|
| 88 |
-
graphql_api_token = gr.Textbox(visible=False, value=api_token)
|
| 89 |
-
graphql_token_header = gr.Textbox(visible=False, value=api_token_header_name)
|
| 90 |
-
titles = gr.Textbox(value=process_message[2], interactive=False, label="GraphQL Types")
|
| 91 |
-
data_source = gr.Textbox(visible=False, value='graphql')
|
| 92 |
-
schema = gr.Textbox(visible=False, value='')
|
| 93 |
-
bot = gr.Chatbot(type='messages', label="GraphQL Chat Window", render_markdown=True, sanitize_html=False, show_label=True, render=False, visible=True, elem_classes="chatbot")
|
| 94 |
-
chat = gr.ChatInterface(
|
| 95 |
-
fn=chatbot_func,
|
| 96 |
-
type='messages',
|
| 97 |
-
chatbot=bot,
|
| 98 |
-
title="Chat with your GraphQL API",
|
| 99 |
-
examples=example_questions,
|
| 100 |
-
concurrency_limit=None,
|
| 101 |
-
additional_inputs=[session_hash, data_source, titles, schema, graphql_api_string, graphql_api_token, graphql_token_header]
|
| 102 |
-
)
|
| 103 |
-
|
| 104 |
-
def process_graphql(graphql_url, api_token, api_token_header_name, session_hash):
|
| 105 |
-
if graphql_url:
|
| 106 |
-
process_message = connect_graphql(graphql_url, api_token, api_token_header_name, session_hash)
|
| 107 |
-
return process_message
|
| 108 |
-
|
| 109 |
-
if __name__ == "__main__":
|
| 110 |
-
demo.launch()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
templates/sql_db.py
DELETED
|
@@ -1,102 +0,0 @@
|
|
| 1 |
-
import ast
|
| 2 |
-
import gradio as gr
|
| 3 |
-
from functions import example_question_generator, chatbot_func
|
| 4 |
-
from data_sources import connect_sql_db
|
| 5 |
-
from utils import message_dict
|
| 6 |
-
|
| 7 |
-
with gr.Blocks() as demo:
|
| 8 |
-
with gr.Accordion("ℹ️ About the SQL Connector", open=False):
|
| 9 |
-
gr.HTML("""
|
| 10 |
-
<div class="max-w-4xl mx-auto text-center">
|
| 11 |
-
<div class="bg-blue-50 border border-blue-200 rounded-lg max-w-2xl mx-auto p-4">
|
| 12 |
-
<p>Connect to a PostgreSQL database and query it using natural language.</p>
|
| 13 |
-
<p style="font-weight:bold;">
|
| 14 |
-
No credentials are retained — they are passed as session variables and disappear when you leave or refresh.
|
| 15 |
-
Queries run through Pandas <code>read_sql_query</code>, which cannot delete, drop, or insert rows.
|
| 16 |
-
Use caution connecting production databases to third-party tools.
|
| 17 |
-
</p>
|
| 18 |
-
<p>Contact me if you'd like this built for your organization with proper infrastructure and security controls.</p>
|
| 19 |
-
</div>
|
| 20 |
-
</div>
|
| 21 |
-
""")
|
| 22 |
-
|
| 23 |
-
gr.HTML("""
|
| 24 |
-
<div style="max-width:560px;margin:8px auto 4px;padding:8px 14px;background:#f0f9ff;
|
| 25 |
-
border:1px solid #bae6fd;border-radius:8px;text-align:center;">
|
| 26 |
-
<p style="margin:0;font-size:13px;color:#0369a1;">
|
| 27 |
-
<i class="fas fa-flask" style="margin-right:6px;"></i>
|
| 28 |
-
<strong>Demo credentials pre-filled.</strong>
|
| 29 |
-
Replace with your own database details to analyze your own data.
|
| 30 |
-
</p>
|
| 31 |
-
</div>
|
| 32 |
-
""")
|
| 33 |
-
|
| 34 |
-
sql_url = gr.Textbox(label="URL", value="virtual-data-analyst-pg.cyetm2yjzppu.us-west-1.rds.amazonaws.com")
|
| 35 |
-
with gr.Row():
|
| 36 |
-
sql_port = gr.Textbox(label="Port", value="5432")
|
| 37 |
-
sql_user = gr.Textbox(label="Username", value="postgres")
|
| 38 |
-
sql_pass = gr.Textbox(label="Password", value="Vda-1988", type="password")
|
| 39 |
-
sql_db_name = gr.Textbox(label="Database Name", value="dvdrental")
|
| 40 |
-
|
| 41 |
-
submit = gr.Button(value="Connect", variant="primary")
|
| 42 |
-
|
| 43 |
-
@gr.render(inputs=[sql_url, sql_port, sql_user, sql_pass, sql_db_name], triggers=[submit.click])
|
| 44 |
-
def sql_chat(request: gr.Request, url=sql_url.value, sql_port=sql_port.value, sql_user=sql_user.value, sql_pass=sql_pass.value, sql_db_name=sql_db_name.value):
|
| 45 |
-
if request.session_hash not in message_dict:
|
| 46 |
-
message_dict[request.session_hash] = {}
|
| 47 |
-
message_dict[request.session_hash]['sql'] = None
|
| 48 |
-
if url:
|
| 49 |
-
print("SQL APP")
|
| 50 |
-
process_message = process_sql_db(url, sql_user, sql_port, sql_pass, sql_db_name, request.session_hash)
|
| 51 |
-
gr.HTML(value=process_message[1], padding=False)
|
| 52 |
-
if process_message[0] == "success":
|
| 53 |
-
if "virtual-data-analyst-pg.cyetm2yjzppu.us-west-1.rds.amazonaws.com" in url:
|
| 54 |
-
example_questions = [
|
| 55 |
-
["Describe the dataset"],
|
| 56 |
-
["What is the total revenue generated by each store?"],
|
| 57 |
-
["Can you generate and display a bar chart of film category to number of films in that category?"],
|
| 58 |
-
["Can you generate a pie chart showing the top 10 most rented films by revenue?"],
|
| 59 |
-
["Can you generate a line chart of rental revenue over time?"],
|
| 60 |
-
["What is the relationship between film length and rental frequency?"]
|
| 61 |
-
]
|
| 62 |
-
else:
|
| 63 |
-
try:
|
| 64 |
-
generated_examples = ast.literal_eval(example_question_generator(request.session_hash, 'sql', sql_db_name, process_message[2], ""))
|
| 65 |
-
example_questions = [["Describe the dataset"]]
|
| 66 |
-
for example in generated_examples:
|
| 67 |
-
example_questions.append([example])
|
| 68 |
-
except Exception as e:
|
| 69 |
-
print("SQL QUESTION GENERATION ERROR")
|
| 70 |
-
print(e)
|
| 71 |
-
example_questions = [
|
| 72 |
-
["Describe the dataset"],
|
| 73 |
-
["List the tables in the database"],
|
| 74 |
-
["What could this data be used for?"],
|
| 75 |
-
]
|
| 76 |
-
session_hash = gr.Textbox(visible=False, value=request.session_hash)
|
| 77 |
-
db_url = gr.Textbox(visible=False, value=url)
|
| 78 |
-
db_port = gr.Textbox(visible=False, value=sql_port)
|
| 79 |
-
db_user = gr.Textbox(visible=False, value=sql_user)
|
| 80 |
-
db_pass = gr.Textbox(visible=False, value=sql_pass)
|
| 81 |
-
db_name = gr.Textbox(visible=False, value=sql_db_name)
|
| 82 |
-
titles = gr.Textbox(value=process_message[2], interactive=False, label="SQL Tables")
|
| 83 |
-
data_source = gr.Textbox(visible=False, value='sql')
|
| 84 |
-
schema = gr.Textbox(visible=False, value='')
|
| 85 |
-
bot = gr.Chatbot(type='messages', label="SQL DB Chat Window", render_markdown=True, sanitize_html=False, show_label=True, render=False, visible=True, elem_classes="chatbot")
|
| 86 |
-
chat = gr.ChatInterface(
|
| 87 |
-
fn=chatbot_func,
|
| 88 |
-
type='messages',
|
| 89 |
-
chatbot=bot,
|
| 90 |
-
title="Chat with your Database",
|
| 91 |
-
examples=example_questions,
|
| 92 |
-
concurrency_limit=None,
|
| 93 |
-
additional_inputs=[session_hash, data_source, titles, schema, db_url, db_port, db_user, db_pass, db_name]
|
| 94 |
-
)
|
| 95 |
-
|
| 96 |
-
def process_sql_db(url, sql_user, sql_port, sql_pass, sql_db_name, session_hash):
|
| 97 |
-
if url:
|
| 98 |
-
process_message = connect_sql_db(url, sql_user, sql_port, sql_pass, sql_db_name, session_hash)
|
| 99 |
-
return process_message
|
| 100 |
-
|
| 101 |
-
if __name__ == "__main__":
|
| 102 |
-
demo.launch()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
tools.py
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import sqlite3
|
| 2 |
+
|
| 3 |
+
def tools_call(session_hash):
|
| 4 |
+
connection = sqlite3.connect(f'data_source_{session_hash}.db')
|
| 5 |
+
print("Querying Database in Tools.py");
|
| 6 |
+
cur=connection.execute('select * from data_source')
|
| 7 |
+
columns = [i[0] for i in cur.description]
|
| 8 |
+
print("COLUMNS 2")
|
| 9 |
+
print(columns)
|
| 10 |
+
cur.close()
|
| 11 |
+
connection.close()
|
| 12 |
+
|
| 13 |
+
return [
|
| 14 |
+
{
|
| 15 |
+
"type": "function",
|
| 16 |
+
"function": {
|
| 17 |
+
"name": "sql_query_func",
|
| 18 |
+
"description": f"This a tool useful to query a SQL table called 'data_source' with the following Columns: {columns}",
|
| 19 |
+
"parameters": {
|
| 20 |
+
"type": "object",
|
| 21 |
+
"properties": {
|
| 22 |
+
"queries": {
|
| 23 |
+
"type": "array",
|
| 24 |
+
"description": "The query to use in the search. Infer this from the user's message. It should be a question or a statement",
|
| 25 |
+
"items": {
|
| 26 |
+
"type": "string",
|
| 27 |
+
}
|
| 28 |
+
}
|
| 29 |
+
},
|
| 30 |
+
"required": ["question"],
|
| 31 |
+
},
|
| 32 |
+
},
|
| 33 |
+
},
|
| 34 |
+
{
|
| 35 |
+
"type": "function",
|
| 36 |
+
"function": {
|
| 37 |
+
"name": "rag_pipeline_func",
|
| 38 |
+
"description": f"This a tool useful to query a SQL table called 'data_source' with the following Columns: {columns}",
|
| 39 |
+
"parameters": {
|
| 40 |
+
"type": "object",
|
| 41 |
+
"properties": {
|
| 42 |
+
"queries": {
|
| 43 |
+
"type": "array",
|
| 44 |
+
"description": "The query to use in the search. Infer this from the user's message. It should be a question or a statement",
|
| 45 |
+
"items": {
|
| 46 |
+
"type": "string",
|
| 47 |
+
}
|
| 48 |
+
}
|
| 49 |
+
},
|
| 50 |
+
"required": ["question"],
|
| 51 |
+
},
|
| 52 |
+
},
|
| 53 |
+
}
|
| 54 |
+
]
|
tools/__init__.py
DELETED
|
File without changes
|
tools/chart_tools.py
DELETED
|
@@ -1,308 +0,0 @@
|
|
| 1 |
-
# Shared parameter snippets reused across chart tool schemas.
|
| 2 |
-
# Update here to change the description everywhere at once.
|
| 3 |
-
|
| 4 |
-
_LAYOUT_PARAM = {
|
| 5 |
-
"type": "array",
|
| 6 |
-
"description": (
|
| 7 |
-
"Optional. An array containing a single JSON-formatted Plotly layout dictionary. "
|
| 8 |
-
"Use to set chart title, axis labels, colours, fonts, and other layout properties. "
|
| 9 |
-
"Example: [{\"title\": \"Monthly Sales\", \"xaxis\": {\"title\": \"Month\"}}]"
|
| 10 |
-
),
|
| 11 |
-
"items": {"type": "string"},
|
| 12 |
-
}
|
| 13 |
-
|
| 14 |
-
_TRACE_STYLE_PARAM = {
|
| 15 |
-
"type": "array",
|
| 16 |
-
"description": (
|
| 17 |
-
"Optional. An array containing a single JSON-formatted Plotly trace styling dictionary. "
|
| 18 |
-
"Use to control visual properties such as line colour, opacity, and marker style. "
|
| 19 |
-
"Do NOT include 'x', 'y', or 'type' keys — those are set automatically from query.csv."
|
| 20 |
-
),
|
| 21 |
-
"items": {"type": "string"},
|
| 22 |
-
}
|
| 23 |
-
|
| 24 |
-
chart_tool_schemas = [
|
| 25 |
-
{
|
| 26 |
-
"name": "scatter_chart_generation_func",
|
| 27 |
-
"description": (
|
| 28 |
-
"Generates a Plotly scatter plot from query.csv data. "
|
| 29 |
-
"Use when the user wants to visualise the relationship between two numeric columns, "
|
| 30 |
-
"create a bubble chart (via the size parameter), or overlay a trendline. "
|
| 31 |
-
"Returns an HTML iframe — display it verbatim in the chat."
|
| 32 |
-
),
|
| 33 |
-
"parameters": {
|
| 34 |
-
"type": "object",
|
| 35 |
-
"properties": {
|
| 36 |
-
"x_column": {
|
| 37 |
-
"type": "array",
|
| 38 |
-
"description": (
|
| 39 |
-
"One or more column names from query.csv to plot on the x-axis. "
|
| 40 |
-
"Multiple columns produce multiple series, each plotted against y_column."
|
| 41 |
-
),
|
| 42 |
-
"items": {"type": "string"},
|
| 43 |
-
},
|
| 44 |
-
"y_column": {
|
| 45 |
-
"type": "string",
|
| 46 |
-
"description": "Column name from query.csv to plot on the y-axis.",
|
| 47 |
-
},
|
| 48 |
-
"category": {
|
| 49 |
-
"type": "string",
|
| 50 |
-
"description": "Optional column name used to colour-code points by a categorical grouping.",
|
| 51 |
-
},
|
| 52 |
-
"trendline": {
|
| 53 |
-
"type": "string",
|
| 54 |
-
"description": (
|
| 55 |
-
"Optional trendline type. One of: 'ols' (linear regression), "
|
| 56 |
-
"'lowess' (local smoothing), 'rolling', 'ewm', 'expanding'. "
|
| 57 |
-
"Requires trendline_options when using 'lowess', 'rolling', or 'ewm'."
|
| 58 |
-
),
|
| 59 |
-
},
|
| 60 |
-
"trendline_options": {
|
| 61 |
-
"type": "array",
|
| 62 |
-
"description": (
|
| 63 |
-
"Required when trendline is 'lowess', 'rolling', or 'ewm'. "
|
| 64 |
-
"An array containing a single JSON-formatted dict of trendline options "
|
| 65 |
-
"(e.g. [{\"window\": 7}] for a 7-point rolling average)."
|
| 66 |
-
),
|
| 67 |
-
"items": {"type": "string"},
|
| 68 |
-
},
|
| 69 |
-
"marginal_x": {
|
| 70 |
-
"type": "string",
|
| 71 |
-
"description": "Optional marginal distribution plot along the x-axis. One of: 'histogram', 'rug', 'box', 'violin'.",
|
| 72 |
-
},
|
| 73 |
-
"marginal_y": {
|
| 74 |
-
"type": "string",
|
| 75 |
-
"description": "Optional marginal distribution plot along the y-axis. One of: 'histogram', 'rug', 'box', 'violin'.",
|
| 76 |
-
},
|
| 77 |
-
"size": {
|
| 78 |
-
"type": "string",
|
| 79 |
-
"description": "Optional column name whose values control the size of each point (bubble chart). Negative values are clamped to zero.",
|
| 80 |
-
},
|
| 81 |
-
"data": _TRACE_STYLE_PARAM,
|
| 82 |
-
"layout": _LAYOUT_PARAM,
|
| 83 |
-
},
|
| 84 |
-
"required": ["x_column", "y_column"],
|
| 85 |
-
},
|
| 86 |
-
},
|
| 87 |
-
{
|
| 88 |
-
"name": "line_chart_generation_func",
|
| 89 |
-
"description": (
|
| 90 |
-
"Generates a Plotly line chart from query.csv data. "
|
| 91 |
-
"Use for trends over time or any ordered sequence. "
|
| 92 |
-
"Returns an HTML iframe — display it verbatim in the chat."
|
| 93 |
-
),
|
| 94 |
-
"parameters": {
|
| 95 |
-
"type": "object",
|
| 96 |
-
"properties": {
|
| 97 |
-
"x_column": {
|
| 98 |
-
"type": "string",
|
| 99 |
-
"description": "Column name from query.csv for the x-axis (typically a date or ordered index).",
|
| 100 |
-
},
|
| 101 |
-
"y_column": {
|
| 102 |
-
"type": "string",
|
| 103 |
-
"description": "Column name from query.csv for the y-axis (numeric values).",
|
| 104 |
-
},
|
| 105 |
-
"category": {
|
| 106 |
-
"type": "string",
|
| 107 |
-
"description": "Optional column name used to split the data into multiple colour-coded lines.",
|
| 108 |
-
},
|
| 109 |
-
"data": _TRACE_STYLE_PARAM,
|
| 110 |
-
"layout": _LAYOUT_PARAM,
|
| 111 |
-
},
|
| 112 |
-
"required": ["x_column", "y_column"],
|
| 113 |
-
},
|
| 114 |
-
},
|
| 115 |
-
{
|
| 116 |
-
"name": "bar_chart_generation_func",
|
| 117 |
-
"description": (
|
| 118 |
-
"Generates a Plotly bar chart from query.csv data. "
|
| 119 |
-
"Use for comparing values across categories. Supports grouped/stacked bars via category, "
|
| 120 |
-
"and faceted subplots via facet_row or facet_col. "
|
| 121 |
-
"Returns an HTML iframe — display it verbatim in the chat."
|
| 122 |
-
),
|
| 123 |
-
"parameters": {
|
| 124 |
-
"type": "object",
|
| 125 |
-
"properties": {
|
| 126 |
-
"x_column": {
|
| 127 |
-
"type": "string",
|
| 128 |
-
"description": "Column name from query.csv for the x-axis (category labels).",
|
| 129 |
-
},
|
| 130 |
-
"y_column": {
|
| 131 |
-
"type": "string",
|
| 132 |
-
"description": "Column name from query.csv for the y-axis (numeric values).",
|
| 133 |
-
},
|
| 134 |
-
"category": {
|
| 135 |
-
"type": "string",
|
| 136 |
-
"description": "Optional column name used to colour-code bars into grouped or stacked series.",
|
| 137 |
-
},
|
| 138 |
-
"facet_row": {
|
| 139 |
-
"type": "string",
|
| 140 |
-
"description": "Optional column name. Creates one subplot row per unique value — useful for comparing distributions across a second dimension.",
|
| 141 |
-
},
|
| 142 |
-
"facet_col": {
|
| 143 |
-
"type": "string",
|
| 144 |
-
"description": "Optional column name. Creates one subplot column per unique value.",
|
| 145 |
-
},
|
| 146 |
-
"data": _TRACE_STYLE_PARAM,
|
| 147 |
-
"layout": _LAYOUT_PARAM,
|
| 148 |
-
},
|
| 149 |
-
"required": ["x_column", "y_column"],
|
| 150 |
-
},
|
| 151 |
-
},
|
| 152 |
-
{
|
| 153 |
-
"name": "pie_chart_generation_func",
|
| 154 |
-
"description": (
|
| 155 |
-
"Generates a Plotly pie chart from query.csv data. "
|
| 156 |
-
"Use when the user wants to show part-to-whole proportions. "
|
| 157 |
-
"Returns an HTML iframe — display it verbatim in the chat."
|
| 158 |
-
),
|
| 159 |
-
"parameters": {
|
| 160 |
-
"type": "object",
|
| 161 |
-
"properties": {
|
| 162 |
-
"values": {
|
| 163 |
-
"type": "string",
|
| 164 |
-
"description": "Column name from query.csv containing the numeric value for each slice.",
|
| 165 |
-
},
|
| 166 |
-
"names": {
|
| 167 |
-
"type": "string",
|
| 168 |
-
"description": "Column name from query.csv containing the label for each slice.",
|
| 169 |
-
},
|
| 170 |
-
"data": _TRACE_STYLE_PARAM,
|
| 171 |
-
"layout": _LAYOUT_PARAM,
|
| 172 |
-
},
|
| 173 |
-
"required": ["values", "names"],
|
| 174 |
-
},
|
| 175 |
-
},
|
| 176 |
-
{
|
| 177 |
-
"name": "histogram_generation_func",
|
| 178 |
-
"description": (
|
| 179 |
-
"Generates a Plotly histogram from query.csv data. "
|
| 180 |
-
"Use to show the frequency distribution of a numeric column. "
|
| 181 |
-
"Supports normalisation (percent, probability, density) and aggregation functions per bin. "
|
| 182 |
-
"Returns an HTML iframe — display it verbatim in the chat."
|
| 183 |
-
),
|
| 184 |
-
"parameters": {
|
| 185 |
-
"type": "object",
|
| 186 |
-
"properties": {
|
| 187 |
-
"x_column": {
|
| 188 |
-
"type": "string",
|
| 189 |
-
"description": "Column name from query.csv whose values are binned on the x-axis.",
|
| 190 |
-
},
|
| 191 |
-
"y_column": {
|
| 192 |
-
"type": "string",
|
| 193 |
-
"description": "Optional column name aggregated per bin via histfunc (e.g. sum of sales per price bucket).",
|
| 194 |
-
},
|
| 195 |
-
"histnorm": {
|
| 196 |
-
"type": "string",
|
| 197 |
-
"description": "Optional normalisation. One of: 'percent', 'probability', 'density', 'probability density'.",
|
| 198 |
-
},
|
| 199 |
-
"category": {
|
| 200 |
-
"type": "string",
|
| 201 |
-
"description": "Optional column name used to overlay multiple colour-coded histograms.",
|
| 202 |
-
},
|
| 203 |
-
"histfunc": {
|
| 204 |
-
"type": "string",
|
| 205 |
-
"description": "Optional aggregation function applied to y_column per bin. One of: 'avg', 'sum', 'count'.",
|
| 206 |
-
},
|
| 207 |
-
"data": _TRACE_STYLE_PARAM,
|
| 208 |
-
"layout": _LAYOUT_PARAM,
|
| 209 |
-
},
|
| 210 |
-
"required": ["x_column"],
|
| 211 |
-
},
|
| 212 |
-
},
|
| 213 |
-
{
|
| 214 |
-
"name": "box_chart_generation_func",
|
| 215 |
-
"description": (
|
| 216 |
-
"Generates a Plotly box plot from query.csv data. "
|
| 217 |
-
"Use to visualise the distribution of a numeric column and identify outliers. "
|
| 218 |
-
"Especially useful for comparing distributions across categories. "
|
| 219 |
-
"Returns an HTML iframe — display it verbatim in the chat."
|
| 220 |
-
),
|
| 221 |
-
"parameters": {
|
| 222 |
-
"type": "object",
|
| 223 |
-
"properties": {
|
| 224 |
-
"y_column": {
|
| 225 |
-
"type": "string",
|
| 226 |
-
"description": "Column name from query.csv containing the numeric values to distribute on the y-axis.",
|
| 227 |
-
},
|
| 228 |
-
"x_column": {
|
| 229 |
-
"type": "string",
|
| 230 |
-
"description": "Optional column name. Groups data into one box per unique value on the x-axis.",
|
| 231 |
-
},
|
| 232 |
-
"category": {
|
| 233 |
-
"type": "string",
|
| 234 |
-
"description": "Optional column name used to colour-code boxes by a secondary grouping.",
|
| 235 |
-
},
|
| 236 |
-
"layout": _LAYOUT_PARAM,
|
| 237 |
-
},
|
| 238 |
-
"required": ["y_column"],
|
| 239 |
-
},
|
| 240 |
-
},
|
| 241 |
-
{
|
| 242 |
-
"name": "correlation_heatmap_func",
|
| 243 |
-
"description": (
|
| 244 |
-
"Computes pairwise Pearson correlations between numeric columns in query.csv and renders "
|
| 245 |
-
"the result as a colour-coded heatmap (blue = positive, red = negative). "
|
| 246 |
-
"Use when the user asks which variables are related, correlated, or associated with each other. "
|
| 247 |
-
"Returns an HTML iframe — display it verbatim in the chat."
|
| 248 |
-
),
|
| 249 |
-
"parameters": {
|
| 250 |
-
"type": "object",
|
| 251 |
-
"properties": {
|
| 252 |
-
"columns": {
|
| 253 |
-
"type": "array",
|
| 254 |
-
"description": "Optional list of numeric column names to include in the matrix. If omitted, all numeric columns from query.csv are used. Avoid ID or index columns.",
|
| 255 |
-
"items": {"type": "string"},
|
| 256 |
-
},
|
| 257 |
-
},
|
| 258 |
-
"required": [],
|
| 259 |
-
},
|
| 260 |
-
},
|
| 261 |
-
{
|
| 262 |
-
"name": "rolling_stats_func",
|
| 263 |
-
"description": (
|
| 264 |
-
"Generates a rolling statistics / moving average chart from query.csv data. "
|
| 265 |
-
"Overlays rolling aggregations (mean, std, min, max) on top of the original series. "
|
| 266 |
-
"Use when the user asks for a moving average, rolling average, rolling statistics, or wants to smooth a time series. "
|
| 267 |
-
"Returns an HTML iframe — display it verbatim in the chat."
|
| 268 |
-
),
|
| 269 |
-
"parameters": {
|
| 270 |
-
"type": "object",
|
| 271 |
-
"properties": {
|
| 272 |
-
"x_column": {
|
| 273 |
-
"type": "string",
|
| 274 |
-
"description": "Column name from query.csv for the x-axis — typically a date or sequential index.",
|
| 275 |
-
},
|
| 276 |
-
"y_column": {
|
| 277 |
-
"type": "string",
|
| 278 |
-
"description": "Column name from query.csv containing the numeric values to compute rolling stats on.",
|
| 279 |
-
},
|
| 280 |
-
"window": {
|
| 281 |
-
"type": "integer",
|
| 282 |
-
"description": "Rolling window size in number of rows. Default 7. Infer from the user's request.",
|
| 283 |
-
},
|
| 284 |
-
"stats": {
|
| 285 |
-
"type": "array",
|
| 286 |
-
"description": "Statistics to overlay. Valid values: 'mean', 'std', 'min', 'max'. Defaults to ['mean'] if omitted.",
|
| 287 |
-
"items": {"type": "string"},
|
| 288 |
-
},
|
| 289 |
-
"category": {
|
| 290 |
-
"type": "string",
|
| 291 |
-
"description": "Optional column name to group the data, producing separate rolling stat lines per group.",
|
| 292 |
-
},
|
| 293 |
-
"layout": _LAYOUT_PARAM,
|
| 294 |
-
},
|
| 295 |
-
"required": ["x_column", "y_column"],
|
| 296 |
-
},
|
| 297 |
-
},
|
| 298 |
-
{
|
| 299 |
-
"name": "table_generation_func",
|
| 300 |
-
"description": (
|
| 301 |
-
"Formats query.csv results as a styled HTML table. "
|
| 302 |
-
"Use when the user wants to view raw query results in a readable format, "
|
| 303 |
-
"or when result data is too large to describe in text. Displays up to 200 rows. "
|
| 304 |
-
"Returns an HTML table — display it verbatim in the chat."
|
| 305 |
-
),
|
| 306 |
-
"parameters": {"type": "object", "properties": {}},
|
| 307 |
-
},
|
| 308 |
-
]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
tools/stats_tools.py
DELETED
|
@@ -1,130 +0,0 @@
|
|
| 1 |
-
stats_tool_schemas = [
|
| 2 |
-
{
|
| 3 |
-
"name": "descriptive_stats_func",
|
| 4 |
-
"description": (
|
| 5 |
-
"Computes summary statistics for numeric columns in query.csv: "
|
| 6 |
-
"count, mean, std, min, 25th/50th/75th percentile, and max. "
|
| 7 |
-
"Use when the user asks for summary statistics, descriptive statistics, or a statistical overview. "
|
| 8 |
-
"Returns a formatted HTML table."
|
| 9 |
-
),
|
| 10 |
-
"parameters": {
|
| 11 |
-
"type": "object",
|
| 12 |
-
"properties": {
|
| 13 |
-
"columns": {
|
| 14 |
-
"type": "array",
|
| 15 |
-
"description": "Optional list of column names to include. If omitted, all numeric columns from query.csv are used. Avoid ID or index columns.",
|
| 16 |
-
"items": {"type": "string"},
|
| 17 |
-
},
|
| 18 |
-
},
|
| 19 |
-
"required": [],
|
| 20 |
-
},
|
| 21 |
-
},
|
| 22 |
-
{
|
| 23 |
-
"name": "kmeans_clustering_func",
|
| 24 |
-
"description": (
|
| 25 |
-
"Runs K-Means clustering on numeric feature columns from query.csv. "
|
| 26 |
-
"Groups rows into k clusters, displays a scatter plot coloured by cluster assignment, "
|
| 27 |
-
"and returns a centroid summary table showing the mean of each feature per cluster. "
|
| 28 |
-
"Use when the user asks to cluster the data, find natural segments or groups, or apply K-Means. "
|
| 29 |
-
"Returns an HTML iframe and summary table."
|
| 30 |
-
),
|
| 31 |
-
"parameters": {
|
| 32 |
-
"type": "object",
|
| 33 |
-
"properties": {
|
| 34 |
-
"feature_columns": {
|
| 35 |
-
"type": "array",
|
| 36 |
-
"description": "List of numeric column names from query.csv to use as clustering features.",
|
| 37 |
-
"items": {"type": "string"},
|
| 38 |
-
},
|
| 39 |
-
"x_column": {
|
| 40 |
-
"type": "string",
|
| 41 |
-
"description": "Column name from query.csv for the x-axis of the scatter plot. Usually one of the feature columns.",
|
| 42 |
-
},
|
| 43 |
-
"y_column": {
|
| 44 |
-
"type": "string",
|
| 45 |
-
"description": "Column name from query.csv for the y-axis of the scatter plot. Usually one of the feature columns.",
|
| 46 |
-
},
|
| 47 |
-
"n_clusters": {
|
| 48 |
-
"type": "integer",
|
| 49 |
-
"description": "Number of clusters (k). Default 3. Infer from the user's request.",
|
| 50 |
-
},
|
| 51 |
-
"layout": {
|
| 52 |
-
"type": "array",
|
| 53 |
-
"description": "Optional. An array containing a single JSON-formatted Plotly layout dictionary.",
|
| 54 |
-
"items": {"type": "string"},
|
| 55 |
-
},
|
| 56 |
-
},
|
| 57 |
-
"required": ["feature_columns", "x_column", "y_column"],
|
| 58 |
-
},
|
| 59 |
-
},
|
| 60 |
-
{
|
| 61 |
-
"name": "hypothesis_test_func",
|
| 62 |
-
"description": (
|
| 63 |
-
"Performs a statistical hypothesis test on query.csv data and returns a formatted results table "
|
| 64 |
-
"with test statistic, p-value, and significance at α=0.05. "
|
| 65 |
-
"Supported tests:\n"
|
| 66 |
-
"- 't_test_independent': compare means of a numeric column across two groups "
|
| 67 |
-
"(requires group_column; use group_values if the column has more than 2 unique values).\n"
|
| 68 |
-
"- 't_test_one_sample': test whether a column's mean equals a hypothesized value (requires pop_mean).\n"
|
| 69 |
-
"- 'chi_square': test independence between two categorical columns (requires column and column2)."
|
| 70 |
-
),
|
| 71 |
-
"parameters": {
|
| 72 |
-
"type": "object",
|
| 73 |
-
"properties": {
|
| 74 |
-
"test_type": {
|
| 75 |
-
"type": "string",
|
| 76 |
-
"description": "Test to run. One of: 't_test_independent', 't_test_one_sample', 'chi_square'.",
|
| 77 |
-
},
|
| 78 |
-
"column": {
|
| 79 |
-
"type": "string",
|
| 80 |
-
"description": "Primary column for the test. Numeric for t-tests; first categorical column for chi-square.",
|
| 81 |
-
},
|
| 82 |
-
"column2": {
|
| 83 |
-
"type": "string",
|
| 84 |
-
"description": "Second categorical column. Required for 'chi_square'.",
|
| 85 |
-
},
|
| 86 |
-
"group_column": {
|
| 87 |
-
"type": "string",
|
| 88 |
-
"description": "Grouping column. Required for 't_test_independent'. Must have exactly 2 unique values, or specify group_values.",
|
| 89 |
-
},
|
| 90 |
-
"group_values": {
|
| 91 |
-
"type": "array",
|
| 92 |
-
"description": "Exactly 2 group labels to compare. Use when group_column has more than 2 unique values.",
|
| 93 |
-
"items": {"type": "string"},
|
| 94 |
-
},
|
| 95 |
-
"pop_mean": {
|
| 96 |
-
"type": "number",
|
| 97 |
-
"description": "Hypothesized population mean (μ₀). Required for 't_test_one_sample'.",
|
| 98 |
-
},
|
| 99 |
-
},
|
| 100 |
-
"required": ["test_type", "column"],
|
| 101 |
-
},
|
| 102 |
-
},
|
| 103 |
-
{
|
| 104 |
-
"name": "regression_func",
|
| 105 |
-
"description": (
|
| 106 |
-
"Runs an OLS linear regression on query.csv data. "
|
| 107 |
-
"Use when the user wants to model the relationship between variables, assess predictors, or run a regression. "
|
| 108 |
-
"Returns a regression summary (coefficients, R², p-values) and a scatter plot with the fitted line as an HTML iframe."
|
| 109 |
-
),
|
| 110 |
-
"parameters": {
|
| 111 |
-
"type": "object",
|
| 112 |
-
"properties": {
|
| 113 |
-
"independent_variables": {
|
| 114 |
-
"type": "array",
|
| 115 |
-
"description": "Column names from query.csv to use as independent (predictor) variables.",
|
| 116 |
-
"items": {"type": "string"},
|
| 117 |
-
},
|
| 118 |
-
"dependent_variable": {
|
| 119 |
-
"type": "string",
|
| 120 |
-
"description": "Column name from query.csv to use as the dependent (outcome) variable.",
|
| 121 |
-
},
|
| 122 |
-
"category": {
|
| 123 |
-
"type": "string",
|
| 124 |
-
"description": "Optional column name used to colour-code points and fit separate regression lines per group.",
|
| 125 |
-
},
|
| 126 |
-
},
|
| 127 |
-
"required": ["independent_variables", "dependent_variable"],
|
| 128 |
-
},
|
| 129 |
-
},
|
| 130 |
-
]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
tools/tools.py
DELETED
|
@@ -1,130 +0,0 @@
|
|
| 1 |
-
from .stats_tools import stats_tool_schemas
|
| 2 |
-
from .chart_tools import chart_tool_schemas
|
| 3 |
-
|
| 4 |
-
def tools_call(session_hash, data_source, titles):
|
| 5 |
-
from haystack.tools import Tool
|
| 6 |
-
|
| 7 |
-
_noop = lambda **kwargs: None
|
| 8 |
-
|
| 9 |
-
def make_tool(schema):
|
| 10 |
-
return Tool(
|
| 11 |
-
name=schema["name"],
|
| 12 |
-
description=schema["description"],
|
| 13 |
-
parameters=schema["parameters"],
|
| 14 |
-
function=_noop,
|
| 15 |
-
)
|
| 16 |
-
|
| 17 |
-
titles_string = (titles[:625] + '..') if len(titles) > 625 else titles
|
| 18 |
-
|
| 19 |
-
query_tool_schemas = {
|
| 20 |
-
'file_upload': {
|
| 21 |
-
"name": "query_func",
|
| 22 |
-
"description": f"""This is a tool useful to query a SQLite table called 'data_source' with the following Columns: {titles_string}.
|
| 23 |
-
There may also be more columns in the table if the number of columns is too large to process.
|
| 24 |
-
This function also saves the results of the query to csv file called query.csv.""",
|
| 25 |
-
"parameters": {
|
| 26 |
-
"type": "object",
|
| 27 |
-
"properties": {
|
| 28 |
-
"queries": {
|
| 29 |
-
"type": "string",
|
| 30 |
-
"description": "The query to use in the search. Infer this from the user's message. It should be a question or a statement."
|
| 31 |
-
}
|
| 32 |
-
},
|
| 33 |
-
"required": ["queries"]
|
| 34 |
-
},
|
| 35 |
-
},
|
| 36 |
-
'sql': {
|
| 37 |
-
"name": "query_func",
|
| 38 |
-
"description": f"""This is a tool useful to query a PostgreSQL database with the following tables, {titles_string}.
|
| 39 |
-
There may also be more tables in the database if the number of tables is too large to process.
|
| 40 |
-
This function also saves the results of the query to csv file called query.csv.""",
|
| 41 |
-
"parameters": {
|
| 42 |
-
"type": "object",
|
| 43 |
-
"properties": {
|
| 44 |
-
"queries": {
|
| 45 |
-
"type": "string",
|
| 46 |
-
"description": "The PostgreSQL query to use in the search. Infer this from the user's message. It should be a question or a statement."
|
| 47 |
-
}
|
| 48 |
-
},
|
| 49 |
-
"required": ["queries"]
|
| 50 |
-
},
|
| 51 |
-
},
|
| 52 |
-
'doc_db': {
|
| 53 |
-
"name": "query_func",
|
| 54 |
-
"description": f"""This is a tool useful to build an aggregation pipeline to query a MongoDB NoSQL document database with the following collections, {titles_string}.
|
| 55 |
-
There may also be more collections in the database if the number of collections is too large to process.
|
| 56 |
-
This function also saves the results of the query to a csv file called query.csv.""",
|
| 57 |
-
"parameters": {
|
| 58 |
-
"type": "object",
|
| 59 |
-
"properties": {
|
| 60 |
-
"queries": {
|
| 61 |
-
"type": "string",
|
| 62 |
-
"description": "The MongoDB aggregation pipeline to use in the search. Infer this from the user's message. It should be a question or a statement."
|
| 63 |
-
},
|
| 64 |
-
"db_collection": {
|
| 65 |
-
"type": "string",
|
| 66 |
-
"description": "The MongoDB collection to use in the search. Infer this from the user's message. It should be a question or a statement."
|
| 67 |
-
}
|
| 68 |
-
},
|
| 69 |
-
"required": ["queries", "db_collection"]
|
| 70 |
-
},
|
| 71 |
-
},
|
| 72 |
-
'graphql': [
|
| 73 |
-
{
|
| 74 |
-
"name": "query_func",
|
| 75 |
-
"description": f"""This is a tool useful to build a GraphQL query for a GraphQL API endpoint with the following types, {titles_string}.
|
| 76 |
-
There may also be more types in the GraphQL endpoint if the number of types is too large to process.
|
| 77 |
-
This function also saves the results of the query to a csv file called query.csv.""",
|
| 78 |
-
"parameters": {
|
| 79 |
-
"type": "object",
|
| 80 |
-
"properties": {
|
| 81 |
-
"queries": {
|
| 82 |
-
"type": "string",
|
| 83 |
-
"description": "The GraphQL query to use in the search. Infer this from the user's message. It should be a question or a statement."
|
| 84 |
-
}
|
| 85 |
-
},
|
| 86 |
-
"required": ["queries"]
|
| 87 |
-
},
|
| 88 |
-
},
|
| 89 |
-
{
|
| 90 |
-
"name": "graphql_schema_query",
|
| 91 |
-
"description": f"""This is a tool useful to query a GraphQL type and receive back information about its schema. This is useful because
|
| 92 |
-
the GraphQL introspection query is too large to be ingested all at once and this allows us to query the schema one type at a time to
|
| 93 |
-
view it in manageable bites. You may realize after viewing the schema, that the type you selected was not appropriate for the question
|
| 94 |
-
you are attempting answer. You may then query additional types to find the appropriate types to use for your GraphQL API query.""",
|
| 95 |
-
"parameters": {
|
| 96 |
-
"type": "object",
|
| 97 |
-
"properties": {
|
| 98 |
-
"graphql_type": {
|
| 99 |
-
"type": "string",
|
| 100 |
-
"description": "The GraphQL type that we want to view the schema of in order to make the proper query with our graphql_query_func. Infer this from the user's message. It should be a question or a statement."
|
| 101 |
-
}
|
| 102 |
-
},
|
| 103 |
-
"required": ["graphql_type"]
|
| 104 |
-
},
|
| 105 |
-
},
|
| 106 |
-
{
|
| 107 |
-
"name": "graphql_csv_query",
|
| 108 |
-
"description": f"""This is a tool useful to SQL query our query.csv file that is generated from our GraphQL query. This is useful in a situation
|
| 109 |
-
where the results of the GraphQL query need additional querying to answer the user question. The query.csv file is converted to a Pandas dataframe
|
| 110 |
-
and we query that dataframe with SQL on a table called 'query' before converting it back to a csv file.""",
|
| 111 |
-
"parameters": {
|
| 112 |
-
"type": "object",
|
| 113 |
-
"properties": {
|
| 114 |
-
"csv_query": {
|
| 115 |
-
"type": "string",
|
| 116 |
-
"description": "The pandas dataframe SQL query to use in the search. The table that we query is named 'query'. Infer this from the user's message. It should be a question or a statement."
|
| 117 |
-
}
|
| 118 |
-
},
|
| 119 |
-
"required": ["csv_query"]
|
| 120 |
-
},
|
| 121 |
-
},
|
| 122 |
-
]
|
| 123 |
-
}
|
| 124 |
-
|
| 125 |
-
source_schemas = query_tool_schemas[data_source]
|
| 126 |
-
source_tools = [make_tool(s) for s in (source_schemas if isinstance(source_schemas, list) else [source_schemas])]
|
| 127 |
-
chart_tools = [make_tool(s) for s in chart_tool_schemas]
|
| 128 |
-
stats_tools = [make_tool(s) for s in stats_tool_schemas]
|
| 129 |
-
|
| 130 |
-
return source_tools + chart_tools + stats_tools
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
utils.py
DELETED
|
@@ -1,9 +0,0 @@
|
|
| 1 |
-
from pathlib import Path
|
| 2 |
-
|
| 3 |
-
current_dir = Path(__file__).parent
|
| 4 |
-
|
| 5 |
-
TEMP_DIR = current_dir / 'temp'
|
| 6 |
-
|
| 7 |
-
message_dict = {}
|
| 8 |
-
api_key_store = {}
|
| 9 |
-
model_store = {}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|