mozzic commited on
Commit
3784462
·
verified ·
1 Parent(s): b17926a

Upload ui\app_old.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. ui//app_old.py +336 -0
ui//app_old.py ADDED
@@ -0,0 +1,336 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Gradio UI for Context Thread Agent.
3
+ Interactive interface for uploading notebooks and asking questions.
4
+ """
5
+
6
+ import gradio as gr
7
+ import json
8
+ import tempfile
9
+ from pathlib import Path
10
+ from typing import Tuple, List, Dict
11
+ from src.models import Cell
12
+
13
+ from src.parser import NotebookParser
14
+ from src.dependencies import ContextThreadBuilder
15
+ from src.indexing import FAISSIndexer
16
+ from src.retrieval import RetrievalEngine, ContextBuilder
17
+ from src.reasoning import ContextualAnsweringSystem
18
+ from src.intent import ContextThreadEnricher
19
+ from src.groq_integration import GroqReasoningEngine
20
+ import pandas as pd
21
+
22
+
23
+ class NotebookAgentUI:
24
+ """Gradio UI for the Context Thread Agent."""
25
+
26
+ def __init__(self):
27
+ self.current_thread = None
28
+ self.current_indexer = None
29
+ self.current_engine = None
30
+ self.answering_system = None
31
+ self.conversation_history = [] # To maintain context across questions
32
+
33
+ def load_notebook(self, notebook_file) -> str:
34
+ """Load and index a notebook or Excel file."""
35
+ try:
36
+ if notebook_file is None:
37
+ return "❌ No file provided"
38
+
39
+ # Save uploaded file temporarily
40
+ with tempfile.NamedTemporaryFile(suffix=Path(notebook_file).suffix if isinstance(notebook_file, str) else ".ipynb", delete=False) as f:
41
+ if isinstance(notebook_file, str):
42
+ # File path
43
+ f.write(open(notebook_file, 'rb').read())
44
+ else:
45
+ # Uploaded file
46
+ f.write(notebook_file.read())
47
+ temp_path = f.name
48
+
49
+ file_ext = Path(temp_path).suffix.lower()
50
+
51
+ if file_ext == '.ipynb':
52
+ # Parse notebook
53
+ parser = NotebookParser()
54
+ result = parser.parse_file(temp_path)
55
+ cells = result['cells']
56
+ elif file_ext in ['.xlsx', '.xls']:
57
+ # Convert Excel to pseudo-cells
58
+ cells = self._excel_to_cells(temp_path)
59
+ else:
60
+ return "❌ Unsupported file type. Please upload .ipynb or .xlsx/.xls"
61
+
62
+ # Build context thread
63
+ builder = ContextThreadBuilder(
64
+ notebook_name=Path(temp_path).stem,
65
+ thread_id=f"thread_{id(self)}"
66
+ )
67
+ builder.add_cells(cells)
68
+ self.current_thread = builder.build()
69
+
70
+ # Enrich with intents (heuristic, not LLM for speed)
71
+ enricher = ContextThreadEnricher(infer_intents=True)
72
+ self.current_thread = enricher.enrich(self.current_thread)
73
+
74
+ # Index
75
+ self.current_indexer = FAISSIndexer()
76
+ self.current_indexer.add_multiple(self.current_thread.units)
77
+
78
+ # Setup retrieval and reasoning
79
+ self.current_engine = RetrievalEngine(self.current_thread, self.current_indexer)
80
+ self.answering_system = ContextualAnsweringSystem(self.current_engine)
81
+
82
+ # Cleanup
83
+ Path(temp_path).unlink()
84
+
85
+ return f"""
86
+ ✅ **File Loaded Successfully!**
87
+
88
+ **Stats:**
89
+ - Total cells/sections: {len(cells)}
90
+ - Code cells: {sum(1 for c in cells if c.cell_type == 'code')}
91
+ - Markdown cells: {sum(1 for c in cells if c.cell_type == 'markdown')}
92
+ - Indexed: ✓
93
+
94
+ Ready to ask questions! 🎯
95
+ """
96
+
97
+ except Exception as e:
98
+ return f"❌ Error loading file: {str(e)}"
99
+
100
+ def generate_keypoints(self) -> str:
101
+ """Generate key points summary of the notebook."""
102
+ if not self.answering_system:
103
+ return "❌ No notebook loaded."
104
+
105
+ try:
106
+ # Use the reasoning engine to summarize keypoints
107
+ query = "Summarize the key points and main insights from this notebook."
108
+ response = self.answering_system.answer_question(query, top_k=10) # More context for summary
109
+
110
+ keypoints = f"**Key Points Summary:**\n\n{response.answer}"
111
+ return keypoints
112
+ except Exception as e:
113
+ return f"❌ Error generating keypoints: {str(e)}"
114
+
115
+ def get_notebook_display(self) -> str:
116
+ """Get formatted notebook content for display."""
117
+ if not self.current_thread:
118
+ return "No notebook loaded."
119
+
120
+ display = ""
121
+ for i, unit in enumerate(self.current_thread.units, 1):
122
+ display += f"### Cell {i}: {unit.cell.cell_id} [{unit.cell.cell_type}]\n"
123
+ if unit.intent and unit.intent != "[Pending intent inference]":
124
+ display += f"**Intent:** {unit.intent}\n\n"
125
+ display += f"```\n{unit.cell.source}\n```\n\n"
126
+ if unit.cell.outputs:
127
+ display += "**Output:**\n"
128
+ for output in unit.cell.outputs:
129
+ if 'text' in output:
130
+ display += f"```\n{output['text']}\n```\n"
131
+ elif 'data' in output and 'text/plain' in output['data']:
132
+ display += f"```\n{output['data']['text/plain']}\n```\n"
133
+ display += "\n"
134
+
135
+ return display
136
+
137
+ def ask_question(self, query: str) -> Tuple[str, str, str]:
138
+ """Answer a question about the notebook."""
139
+ if not self.answering_system:
140
+ return (
141
+ "❌ No notebook loaded. Please upload a notebook first.",
142
+ "",
143
+ ""
144
+ )
145
+
146
+ try:
147
+ # Add to conversation history
148
+ self.conversation_history.append({"role": "user", "content": query})
149
+
150
+ # Get answer
151
+ response = self.answering_system.answer_question(query, top_k=5)
152
+
153
+ # Add assistant response to history
154
+ self.conversation_history.append({"role": "assistant", "content": response.answer})
155
+
156
+ # Format answer
157
+ answer_text = f"**Answer:**\n\n{response.answer}"
158
+
159
+ # Format citations
160
+ if response.citations:
161
+ citations_text = "**Citations:**\n\n"
162
+ for i, citation in enumerate(response.citations, 1):
163
+ citations_text += f"{i}. **{citation.cell_id}** [{citation.cell_type}]\n"
164
+ if citation.intent:
165
+ citations_text += f" *Intent: {citation.intent}*\n"
166
+ citations_text += f" ```\n{citation.content_snippet}\n ```\n\n"
167
+ else:
168
+ citations_text = "*No specific cells cited*"
169
+
170
+ # Format context
171
+ context_text = "**Retrieved Context:**\n\n"
172
+ for unit in response.retrieved_units:
173
+ context_text += f"### {unit.cell.cell_id} [{unit.cell.cell_type}]\n"
174
+ if unit.intent != "[Pending intent inference]":
175
+ context_text += f"**Intent:** {unit.intent}\n\n"
176
+ if unit.dependencies:
177
+ context_text += f"**Depends on:** {', '.join(unit.dependencies)}\n\n"
178
+ context_text += f"```python\n{unit.cell.source[:300]}\n```\n\n"
179
+
180
+ context_text += f"\n**Confidence:** {response.confidence:.2%}\n"
181
+ context_text += f"**Hallucination Risk:** {'⚠️ Yes' if response.has_hallucination_risk else '✅ No'}"
182
+
183
+ return (answer_text, citations_text, context_text)
184
+
185
+ except Exception as e:
186
+ return (f"❌ Error: {str(e)}", "", "")
187
+
188
+ def _excel_to_cells(self, excel_path: str) -> List[Cell]:
189
+ """Convert Excel file to notebook-like cells."""
190
+ from src.models import Cell, CellType
191
+
192
+ cells = []
193
+
194
+ # Read all sheets
195
+ xl = pd.ExcelFile(excel_path)
196
+
197
+ for sheet_name in xl.sheet_names:
198
+ df = xl.parse(sheet_name)
199
+
200
+ # Create a markdown cell for sheet name
201
+ cells.append(Cell(
202
+ cell_id=f"sheet_{sheet_name}",
203
+ cell_type=CellType.MARKDOWN,
204
+ source=f"# Sheet: {sheet_name}",
205
+ outputs=[]
206
+ ))
207
+
208
+ # Create a code cell for data loading
209
+ cells.append(Cell(
210
+ cell_id=f"data_{sheet_name}",
211
+ cell_type=CellType.CODE,
212
+ source=f"# Data from {sheet_name}\ndf_{sheet_name} = pd.read_excel('{excel_path}', sheet_name='{sheet_name}')",
213
+ outputs=[{"data": {"text/plain": str(df.head())}}]
214
+ ))
215
+
216
+ # Create cells for basic analysis
217
+ cells.append(Cell(
218
+ cell_id=f"shape_{sheet_name}",
219
+ cell_type=CellType.CODE,
220
+ source=f"print(f'Shape: {df.shape}')",
221
+ outputs=[{"text": f"Shape: {df.shape}"}]
222
+ ))
223
+
224
+ cells.append(Cell(
225
+ cell_id=f"info_{sheet_name}",
226
+ cell_type=CellType.CODE,
227
+ source=f"df_{sheet_name}.info()",
228
+ outputs=[{"text": str(df.dtypes)}]
229
+ ))
230
+
231
+ return cells
232
+
233
+
234
+ def create_gradio_app():
235
+ """Create and return the Gradio interface."""
236
+ agent = NotebookAgentUI()
237
+
238
+ with gr.Blocks(title="Context Thread Agent", theme=gr.themes.Soft()) as demo:
239
+ gr.Markdown("# 🧵 Context Thread Agent")
240
+ gr.Markdown("""
241
+ **An AI-powered notebook copilot for analytical workflows**
242
+
243
+ Upload your Jupyter notebook (.ipynb) or Excel file (.xlsx/.xls) and ask questions about your data analysis.
244
+ The agent understands your notebook's context, dependencies, and reasoning—providing grounded answers with citations.
245
+
246
+ ### Key Features:
247
+ - ✅ **Context-Aware**: Answers based only on your notebook content
248
+ - ✅ **Citation-Based**: Every claim references specific cells
249
+ - ✅ **Dependency-Aware**: Understands how cells relate
250
+ - ✅ **No Hallucinations**: Grounded in your actual analysis
251
+ - ✅ **Fast & Free**: Powered by Groq AI
252
+
253
+ ### Major Uses:
254
+ - **Audit Analysis**: Verify assumptions and decisions in complex notebooks
255
+ - **Code Review**: Understand data transformations and logic flows
256
+ - **Documentation**: Generate insights summaries with evidence
257
+ - **Debugging**: Trace errors through dependent cells
258
+ - **Collaboration**: Share verifiable insights from your work
259
+ """)
260
+
261
+ # Upload section
262
+ with gr.Row():
263
+ with gr.Column(scale=1):
264
+ file_input = gr.File(
265
+ label="Upload Notebook or Excel",
266
+ file_types=[".ipynb", ".xlsx", ".xls"],
267
+ type="filepath"
268
+ )
269
+ upload_btn = gr.Button("📤 Upload & Analyze", variant="primary", size="lg")
270
+ with gr.Column(scale=1):
271
+ upload_status = gr.Markdown("### Status\n\nReady to upload...")
272
+
273
+ # After upload, show the main interface
274
+ with gr.Row(visible=False) as main_interface:
275
+ # Left side: Notebook viewer
276
+ with gr.Column(scale=1):
277
+ gr.Markdown("### 📓 Notebook Viewer")
278
+ notebook_display = gr.Markdown("")
279
+
280
+ keypoints_btn = gr.Button("🔑 Generate Key Points", variant="secondary")
281
+ keypoints_display = gr.Markdown("")
282
+
283
+ # Right side: Question answering
284
+ with gr.Column(scale=1):
285
+ gr.Markdown("### ❓ Ask Questions")
286
+ query_input = gr.Textbox(
287
+ label="Your Question",
288
+ placeholder="e.g., 'Why did we remove Q4 data?' or 'What are the key findings?'",
289
+ lines=3
290
+ )
291
+ ask_btn = gr.Button("🤖 Get Answer", variant="primary")
292
+
293
+ answer_display = gr.Markdown("")
294
+ citations_display = gr.Markdown("")
295
+ context_display = gr.Markdown("")
296
+
297
+ # Event handlers
298
+ def on_upload(file):
299
+ status = agent.load_notebook(file)
300
+ if "✅" in status:
301
+ return status, gr.update(visible=True), agent.get_notebook_display(), ""
302
+ else:
303
+ return status, gr.update(visible=False), "", ""
304
+
305
+ upload_btn.click(
306
+ fn=on_upload,
307
+ inputs=[file_input],
308
+ outputs=[upload_status, main_interface, notebook_display, keypoints_display]
309
+ )
310
+
311
+ keypoints_btn.click(
312
+ fn=lambda: agent.generate_keypoints(),
313
+ inputs=[],
314
+ outputs=[keypoints_display]
315
+ ).then(
316
+ fn=lambda: gr.update(visible=True),
317
+ inputs=[],
318
+ outputs=[keypoints_display]
319
+ )
320
+
321
+ ask_btn.click(
322
+ fn=agent.ask_question,
323
+ inputs=[query_input],
324
+ outputs=[answer_display, citations_display, context_display]
325
+ )
326
+
327
+ return demo
328
+
329
+
330
+ if __name__ == "__main__":
331
+ demo = create_gradio_app()
332
+ demo.launch(
333
+ server_name="0.0.0.0",
334
+ server_port=7860,
335
+ share=True
336
+ )