Spaces:
Running
Running
Create app.py
Browse files
app.py
ADDED
@@ -0,0 +1,271 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from __future__ import annotations
|
2 |
+
import gradio as gr
|
3 |
+
import pandas as pd
|
4 |
+
import os
|
5 |
+
from typing import Optional, Iterable
|
6 |
+
import sys
|
7 |
+
from pathlib import Path
|
8 |
+
from gradio.themes.base import Base
|
9 |
+
from gradio.themes.utils import colors, fonts, sizes
|
10 |
+
from augini import Augini
|
11 |
+
|
12 |
+
# Create custom dark theme
|
13 |
+
class AuginiDarkTheme(Base):
|
14 |
+
def __init__(
|
15 |
+
self,
|
16 |
+
*,
|
17 |
+
primary_hue: colors.Color | str = colors.indigo,
|
18 |
+
secondary_hue: colors.Color | str = colors.indigo,
|
19 |
+
neutral_hue: colors.Color | str = colors.gray,
|
20 |
+
spacing_size: sizes.Size | str = sizes.spacing_md,
|
21 |
+
radius_size: sizes.Size | str = sizes.radius_lg,
|
22 |
+
text_size: sizes.Size | str = sizes.text_md,
|
23 |
+
font: fonts.Font | str | Iterable[fonts.Font | str] = (
|
24 |
+
fonts.GoogleFont("Inter"),
|
25 |
+
"ui-sans-serif",
|
26 |
+
"sans-serif",
|
27 |
+
),
|
28 |
+
):
|
29 |
+
super().__init__(
|
30 |
+
primary_hue=primary_hue,
|
31 |
+
secondary_hue=secondary_hue,
|
32 |
+
neutral_hue=neutral_hue,
|
33 |
+
spacing_size=spacing_size,
|
34 |
+
radius_size=radius_size,
|
35 |
+
text_size=text_size,
|
36 |
+
font=font,
|
37 |
+
)
|
38 |
+
self.name = "augini_dark"
|
39 |
+
self.set(
|
40 |
+
# Dark theme colors
|
41 |
+
body_background_fill="*neutral_950",
|
42 |
+
body_text_color="*neutral_200",
|
43 |
+
background_fill_primary="*neutral_900",
|
44 |
+
background_fill_secondary="*neutral_800",
|
45 |
+
border_color_primary="*neutral_700",
|
46 |
+
|
47 |
+
# Components
|
48 |
+
block_background_fill="*neutral_900",
|
49 |
+
block_border_color="*neutral_700",
|
50 |
+
block_border_width="1px",
|
51 |
+
block_label_background_fill="*neutral_900",
|
52 |
+
block_label_text_color="*neutral_200",
|
53 |
+
block_title_text_color="*neutral_200",
|
54 |
+
|
55 |
+
# Buttons
|
56 |
+
button_primary_background_fill="*primary_600",
|
57 |
+
button_primary_background_fill_hover="*primary_500",
|
58 |
+
button_primary_text_color="white",
|
59 |
+
button_secondary_background_fill="*neutral_700",
|
60 |
+
button_secondary_background_fill_hover="*neutral_600",
|
61 |
+
button_secondary_text_color="*neutral_200",
|
62 |
+
|
63 |
+
# Inputs
|
64 |
+
input_background_fill="*neutral_800",
|
65 |
+
input_background_fill_focus="*neutral_800",
|
66 |
+
input_border_color="*neutral_700",
|
67 |
+
input_border_color_focus="*primary_500",
|
68 |
+
input_placeholder_color="*neutral_500",
|
69 |
+
|
70 |
+
# Shadows and effects
|
71 |
+
shadow_spread="1px",
|
72 |
+
block_shadow="0 1px 2px 0 rgb(0 0 0 / 0.05)",
|
73 |
+
button_shadow="0 1px 2px 0 rgb(0 0 0 / 0.05)",
|
74 |
+
)
|
75 |
+
|
76 |
+
class AuginiChat:
|
77 |
+
def __init__(self):
|
78 |
+
self.df: Optional[pd.DataFrame] = None
|
79 |
+
# Initialize Augini with the API key directly
|
80 |
+
self.augini = Augini(
|
81 |
+
api_key=os.environ.get('OPENROUTER_TOKEN'),
|
82 |
+
use_openrouter=True,
|
83 |
+
model='gpt-4o-mini',
|
84 |
+
temperature=0.7
|
85 |
+
)
|
86 |
+
|
87 |
+
def upload_file(self, file) -> str:
|
88 |
+
"""Handle file upload and return preview"""
|
89 |
+
try:
|
90 |
+
if file is None:
|
91 |
+
return "Please upload a file"
|
92 |
+
|
93 |
+
file_path = file.name
|
94 |
+
file_extension = os.path.splitext(file_path)[1].lower()
|
95 |
+
|
96 |
+
# Read the file based on its extension
|
97 |
+
if file_extension == '.csv':
|
98 |
+
self.df = pd.read_csv(file_path)
|
99 |
+
elif file_extension in ['.xlsx', '.xls']:
|
100 |
+
self.df = pd.read_excel(file_path)
|
101 |
+
else:
|
102 |
+
return "β Unsupported file format. Please upload a CSV or Excel file."
|
103 |
+
|
104 |
+
return "β
File uploaded successfully!"
|
105 |
+
|
106 |
+
except Exception as e:
|
107 |
+
return f"β Error uploading file: {str(e)}"
|
108 |
+
|
109 |
+
def chat_with_data(self, message: str, history: list) -> tuple[str, list]:
|
110 |
+
"""Process chat messages and return responses"""
|
111 |
+
try:
|
112 |
+
if not message or message.strip() == "":
|
113 |
+
return "", history
|
114 |
+
|
115 |
+
if self.df is None:
|
116 |
+
return "", history + [(message, "β οΈ Please upload a CSV file first.")]
|
117 |
+
|
118 |
+
# Get response from Augini
|
119 |
+
response = self.augini.chat(message, self.df)
|
120 |
+
|
121 |
+
# Update history and clear the message
|
122 |
+
new_history = history + [(message, response)]
|
123 |
+
return "", new_history
|
124 |
+
|
125 |
+
except Exception as e:
|
126 |
+
error_msg = f"β Error processing message: {str(e)}"
|
127 |
+
return "", history + [(message, error_msg)]
|
128 |
+
|
129 |
+
def create_app():
|
130 |
+
# Initialize the chat handler
|
131 |
+
chat_handler = AuginiChat()
|
132 |
+
|
133 |
+
# JavaScript to force dark theme - added to head
|
134 |
+
dark_mode_script = """
|
135 |
+
<script>
|
136 |
+
function setDarkTheme() {
|
137 |
+
const url = new URL(window.location);
|
138 |
+
if (url.searchParams.get('__theme') !== 'dark') {
|
139 |
+
url.searchParams.set('__theme', 'dark');
|
140 |
+
window.location.href = url.href;
|
141 |
+
}
|
142 |
+
}
|
143 |
+
document.addEventListener('DOMContentLoaded', setDarkTheme);
|
144 |
+
window.addEventListener('load', setDarkTheme);
|
145 |
+
// Also try to set it immediately
|
146 |
+
setDarkTheme();
|
147 |
+
</script>
|
148 |
+
"""
|
149 |
+
|
150 |
+
# Create the Gradio interface with dark theme script in head
|
151 |
+
with gr.Blocks(head=dark_mode_script) as app:
|
152 |
+
gr.Markdown("""
|
153 |
+
# π€ **augini** - your ai data analysis assistant
|
154 |
+
|
155 |
+
**augini** is an agentic AI system designed to help you analyze and understand your data through natural conversation.
|
156 |
+
Upload your data file and start chatting to uncover insights!
|
157 |
+
|
158 |
+
> π‘ **Tip**: Ask questions about patterns, relationships, or any aspect of your data. **augini** will provide detailed, evidence-based answers.
|
159 |
+
""", elem_classes=["center-content"])
|
160 |
+
|
161 |
+
with gr.Row(elem_classes=["container"]):
|
162 |
+
# Left sidebar for file upload
|
163 |
+
with gr.Column(scale=1, elem_classes=["sidebar"]):
|
164 |
+
gr.Markdown("### π upload your data")
|
165 |
+
file_upload = gr.File(
|
166 |
+
label="Upload Data File",
|
167 |
+
file_types=[".csv", ".xlsx", ".xls"],
|
168 |
+
elem_classes=["file-upload"]
|
169 |
+
)
|
170 |
+
file_status = gr.Textbox(
|
171 |
+
label="Upload Status",
|
172 |
+
interactive=False,
|
173 |
+
elem_classes=["status-box"]
|
174 |
+
)
|
175 |
+
|
176 |
+
# Main chat area
|
177 |
+
with gr.Column(scale=3, elem_classes=["main-content"]):
|
178 |
+
chatbot = gr.Chatbot(
|
179 |
+
label="Chat History",
|
180 |
+
height=500,
|
181 |
+
elem_classes=["chat-window"]
|
182 |
+
)
|
183 |
+
with gr.Row():
|
184 |
+
msg = gr.Textbox(
|
185 |
+
label="your question",
|
186 |
+
placeholder="ask me anything about your data...",
|
187 |
+
lines=2,
|
188 |
+
scale=4,
|
189 |
+
elem_classes=["question-input"]
|
190 |
+
)
|
191 |
+
submit_btn = gr.Button("send π€", scale=1, elem_classes=["submit-btn"])
|
192 |
+
clear = gr.Button("clear chat ποΈ", elem_classes=["clear-btn"])
|
193 |
+
|
194 |
+
# Examples and Documentation in a collapsible section
|
195 |
+
with gr.Accordion("π examples & features", open=False, elem_classes=["docs-section"]):
|
196 |
+
with gr.Row():
|
197 |
+
with gr.Column(scale=1):
|
198 |
+
gr.Markdown("""
|
199 |
+
### π― example questions
|
200 |
+
|
201 |
+
**data overview**
|
202 |
+
- "what are the key patterns in this dataset?"
|
203 |
+
- "give me a summary of the main statistics"
|
204 |
+
|
205 |
+
**data quality**
|
206 |
+
- "are there any missing values?"
|
207 |
+
- "how clean is this dataset?"
|
208 |
+
|
209 |
+
**relationships**
|
210 |
+
- "show me the correlations between columns"
|
211 |
+
- "what variables are most related?"
|
212 |
+
|
213 |
+
**deep analysis**
|
214 |
+
- "what insights can you find about [column]?"
|
215 |
+
- "is this a synthetic dataset?"
|
216 |
+
""")
|
217 |
+
|
218 |
+
with gr.Column(scale=1):
|
219 |
+
gr.Markdown("""
|
220 |
+
### β¨ features
|
221 |
+
|
222 |
+
**smart analysis**
|
223 |
+
- advanced statistical analysis
|
224 |
+
- pattern recognition
|
225 |
+
- anomaly detection
|
226 |
+
|
227 |
+
**data support**
|
228 |
+
- csv files
|
229 |
+
- excel files (.xlsx, .xls)
|
230 |
+
- automatic type detection
|
231 |
+
|
232 |
+
**ai capabilities**
|
233 |
+
- natural language understanding
|
234 |
+
- context-aware responses
|
235 |
+
- evidence-based insights
|
236 |
+
""")
|
237 |
+
|
238 |
+
# Add powered by link
|
239 |
+
gr.Markdown("""
|
240 |
+
<div class="powered-by">
|
241 |
+
powered by <a href="https://tabularis.ai" target="_blank">tabularis.ai</a>
|
242 |
+
</div>
|
243 |
+
""", elem_classes=["footer"])
|
244 |
+
|
245 |
+
# Set up event handlers
|
246 |
+
file_upload.upload(
|
247 |
+
chat_handler.upload_file,
|
248 |
+
inputs=[file_upload],
|
249 |
+
outputs=[file_status]
|
250 |
+
)
|
251 |
+
|
252 |
+
# Add both message submission methods
|
253 |
+
msg.submit(
|
254 |
+
chat_handler.chat_with_data,
|
255 |
+
inputs=[msg, chatbot],
|
256 |
+
outputs=[msg, chatbot]
|
257 |
+
)
|
258 |
+
|
259 |
+
submit_btn.click(
|
260 |
+
chat_handler.chat_with_data,
|
261 |
+
inputs=[msg, chatbot],
|
262 |
+
outputs=[msg, chatbot]
|
263 |
+
)
|
264 |
+
|
265 |
+
clear.click(lambda: ([], None), None, [chatbot, msg], queue=False)
|
266 |
+
|
267 |
+
return app
|
268 |
+
|
269 |
+
if __name__ == "__main__":
|
270 |
+
app = create_app()
|
271 |
+
app.launch(share=True)
|