CosmickVisions commited on
Commit
8025520
·
verified ·
1 Parent(s): 63b2a0e

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +203 -329
app.py CHANGED
@@ -1,344 +1,218 @@
1
- import streamlit as st
2
- import pandas as pd
3
- import plotly.express as px
4
- import numpy as np
5
- from sklearn.model_selection import train_test_split
6
- from sklearn.neural_network import MLPClassifier, MLPRegressor
7
- from sklearn.cluster import KMeans
8
- from sklearn.metrics import accuracy_score, r2_score, silhouette_score, confusion_matrix, classification_report, mean_squared_error
9
- from sklearn.preprocessing import StandardScaler
10
- from ydata_profiling import ProfileReport
11
- from streamlit_pandas_profiling import st_profile_report
12
- from groq import Groq
13
- from langchain_community.vectorstores import FAISS
14
- from langchain.text_splitter import RecursiveCharacterTextSplitter
15
- from langchain.embeddings import HuggingFaceEmbeddings
16
- from langchain_community.document_loaders import TextLoader
17
- from langchain_community.tools.tavily_search import TavilySearchResults
18
  import os
19
- from dotenv import load_dotenv
20
  import tempfile
 
 
 
 
 
 
 
 
 
21
 
22
  # Load environment variables
23
  load_dotenv()
24
-
25
- # Initialize Groq client
26
- client = Groq(api_key=os.getenv("GROQ_API_KEY"))
27
-
28
- # Initialize embeddings for FAISS
29
  embeddings = HuggingFaceEmbeddings(model_name="sentence-transformers/all-MiniLM-L6-v2")
30
 
31
- # Set page config
32
- st.set_page_config(page_title="Neural-Vision Enhanced", layout="wide")
33
-
34
- # Custom CSS matching previous theme
35
- st.markdown("""
36
- <style>
37
- :root {
38
- --primary-blue: #3B82F6;
39
- --dark-blue: #1E40AF;
40
- --light-blue: #DBEAFE;
41
- --medium-grey: #6B7280;
42
- --light-grey: #F3F4F6;
43
- --white: #FFFFFF;
44
- --border-grey: #E5E7EB;
45
- }
46
- .stApp {
47
- background-color: var(--light-grey);
48
- font-family: 'Inter', sans-serif;
49
- max-width: 1200px;
50
- margin: 0 auto;
51
- }
52
- .header {
53
- background-color: var(--white);
54
- border-bottom: 2px solid var(--border-grey);
55
- padding: 15px;
56
- border-radius: 12px 12px 0 0;
57
- box-shadow: 0 2px 4px rgba(0,0,0,0.05);
58
- text-align: center;
59
- }
60
- .header-title {
61
- color: var(--dark-blue);
62
- font-size: 1.8rem;
63
- font-weight: 700;
64
- margin: 0;
65
- }
66
- .header-subtitle {
67
- color: var(--medium-grey);
68
- font-size: 1rem;
69
- margin-top: 5px;
70
- }
71
- .sidebar .sidebar-content {
72
- background-color: var(--white);
73
- border-radius: 12px;
74
- box-shadow: 0 4px 6px rgba(0,0,0,0.1);
75
- padding: 15px;
76
- }
77
- .chat-container {
78
- background-color: var(--white);
79
- border-radius: 12px;
80
- box-shadow: 0 4px 6px rgba(0,0,0,0.1);
81
- padding: 15px;
82
- margin-top: 20px;
83
- }
84
- .user-message {
85
- background-color: var(--primary-blue);
86
- color: var(--white);
87
- border-radius: 18px 18px 4px 18px;
88
- padding: 12px 16px;
89
- margin-left: auto;
90
- max-width: 80%;
91
- margin-bottom: 10px;
92
- }
93
- .bot-message {
94
- background-color: var(--light-grey);
95
- color: var(--medium-grey);
96
- border-radius: 18px 18px 18px 4px;
97
- padding: 12px 16px;
98
- margin-right: auto;
99
- max-width: 80%;
100
- margin-bottom: 10px;
101
- }
102
- </style>
103
- """, unsafe_allow_html=True)
104
-
105
- # Initialize session state
106
- if 'metrics' not in st.session_state:
107
- st.session_state.metrics = {}
108
- if 'chat_history' not in st.session_state:
109
- st.session_state.chat_history = []
110
- if 'vector_store' not in st.session_state:
111
- st.session_state.vector_store = None
112
-
113
- # Helper Functions
114
- def convert_df_to_text(df):
115
- text = f"Dataset Summary: {df.shape[0]} rows, {df.shape[1]} columns\n"
116
- text += f"Missing Values: {df.isna().sum().sum()}\n"
117
- text += "Columns:\n"
118
- for col in df.columns:
119
- text += f"- {col} ({df[col].dtype}): "
120
- if pd.api.types.is_numeric_dtype(df[col]):
121
- text += f"Mean={df[col].mean():.2f}, Min={df[col].min()}, Max={df[col].max()}"
122
- else:
123
- text += f"Unique={df[col].nunique()}, Top={df[col].mode()[0] if not df[col].mode().empty else 'N/A'}"
124
- text += f", Missing={df[col].isna().sum()}\n"
125
- return text
126
-
127
- def create_vector_store(df_text):
128
- with tempfile.NamedTemporaryFile(mode='w', suffix='.txt', delete=False) as temp_file:
129
- temp_file.write(df_text)
130
- temp_path = temp_file.name
131
- loader = TextLoader(temp_path)
132
- documents = loader.load()
133
- text_splitter = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=100)
134
- texts = text_splitter.split_documents(documents)
135
- vector_store = FAISS.from_documents(texts, embeddings)
136
- os.unlink(temp_path)
137
- return vector_store
138
-
139
- def get_groq_response(prompt, mode, use_web_search=False):
140
- context = ""
141
- if st.session_state.vector_store:
142
- docs = st.session_state.vector_store.similarity_search(prompt, k=3)
143
- context = "\n\nDataset Context:\n" + "\n".join([f"- {doc.page_content}" for doc in docs])
144
-
145
- if use_web_search:
146
- tavily = TavilySearchResults(max_results=3)
147
- web_results = tavily.invoke(prompt)
148
- context += "\n\nWeb Search Results:\n" + "\n".join([f"- {res['content'][:200]}..." for res in web_results])
149
-
150
- prompts = {
151
- "Legal": "You are a neural network expert specializing in legal data analysis.",
152
- "Financial": "You are a neural network expert specializing in financial data analysis.",
153
- "Academic": "You are a neural network expert specializing in academic data analysis.",
154
- "Technical": "You are a neural network expert specializing in technical data analysis."
155
- }
156
- system_prompt = prompts.get(mode, "You are a neural network development assistant.") + "\n" + context
157
-
158
- response = client.chat.completions.create(
159
- model="llama3-70b-8192",
160
- messages=[
161
- {"role": "system", "content": system_prompt},
162
- {"role": "user", "content": prompt}
163
- ],
164
- temperature=0.7,
165
- max_tokens=1024
166
- )
167
- return response.choices[0].message.content
168
-
169
- # Visualization Functions
170
- def plot_confusion_matrix(y_true, y_pred):
171
- cm = confusion_matrix(y_true, y_pred)
172
- fig = px.imshow(cm, text_auto=True, color_continuous_scale='Blues', title="Confusion Matrix")
173
- return fig
174
-
175
- def plot_feature_importance(model, X):
176
- if hasattr(model, 'feature_importances_'):
177
- importance = model.feature_importances_
178
- else:
179
- importance = np.abs(model.coef_) if hasattr(model, 'coef_') else np.ones(X.shape[1])
180
- fig = px.bar(x=X.columns, y=importance, title="Feature Importance")
181
- return fig
182
-
183
- def plot_residuals(y_true, y_pred):
184
- residuals = y_true - y_pred
185
- fig = px.scatter(x=y_pred, y=residuals, title="Residual Plot", labels={"x": "Predicted", "y": "Residuals"})
186
- return fig
187
-
188
- def plot_clusters(X, labels):
189
- fig = px.scatter(X, x=X.columns[0], y=X.columns[1], color=labels, title="Cluster Visualization")
190
- return fig
191
-
192
- # Pages
193
- def data_upload_page():
194
- st.header("📤 Data Upload & Analysis")
195
- uploaded_file = st.file_uploader("Upload Dataset", type=["csv"])
196
-
197
- if uploaded_file:
198
- df = pd.read_csv(uploaded_file)
199
- st.session_state.df = df
200
- st.session_state.vector_store = create_vector_store(convert_df_to_text(df))
201
- st.session_state.metrics = {}
202
-
203
- st.subheader("Dataset Health Check")
204
- col1, col2, col3 = st.columns(3)
205
- col1.metric("Total Samples", df.shape[0])
206
- col2.metric("Features", df.shape[1])
207
- col3.metric("Missing Values", df.isna().sum().sum())
208
 
209
- if st.button("Generate Full EDA Report"):
210
- with st.spinner("Generating comprehensive analysis..."):
211
- profile = ProfileReport(df, explorative=True)
212
- st_profile_report(profile)
213
-
214
- def model_training_page():
215
- st.header("🧠 Neural Network Training Studio")
216
-
217
- if 'df' not in st.session_state:
218
- st.warning("Upload data first!")
219
- return
220
-
221
- df = st.session_state.df
222
- problem_type = st.selectbox("Select Problem Type", ["Classification", "Regression", "Clustering"])
223
- mode = st.selectbox("Domain Specialization", ["Legal", "Financial", "Academic", "Technical"])
224
-
225
- if problem_type != "Clustering":
226
- target = st.selectbox("Select Target Variable", df.columns)
227
- X = df.drop(columns=[target])
228
- y = df[target]
229
- else:
230
- X = df
231
- y = None
232
-
233
- if st.button("Train Neural Network"):
234
- with st.spinner("Training in progress..."):
235
- X_scaled = StandardScaler().fit_transform(X)
236
- X_train, X_test, y_train, y_test = train_test_split(X_scaled, y, test_size=0.2, random_state=42) if y is not None else (X_scaled, None, None, None)
237
-
238
- if problem_type == "Classification":
239
- model = MLPClassifier(hidden_layer_sizes=(100, 50), max_iter=500, random_state=42)
240
- model.fit(X_train, y_train)
241
- y_pred = model.predict(X_test)
242
- st.session_state.metrics = {
243
- "Accuracy": accuracy_score(y_test, y_pred),
244
- "Classification Report": classification_report(y_test, y_pred, output_dict=True)
245
- }
246
- elif problem_type == "Regression":
247
- model = MLPRegressor(hidden_layer_sizes=(100, 50), max_iter=500, random_state=42)
248
- model.fit(X_train, y_train)
249
- y_pred = model.predict(X_test)
250
- st.session_state.metrics = {
251
- "R2 Score": r2_score(y_test, y_pred),
252
- "Mean Squared Error": mean_squared_error(y_test, y_pred)
253
- }
254
- else: # Clustering
255
- model = KMeans(n_clusters=3, random_state=42)
256
- labels = model.fit_predict(X_scaled)
257
- st.session_state.metrics = {
258
- "Silhouette Score": silhouette_score(X_scaled, labels)
259
- }
260
-
261
- st.session_state.best_model = model
262
- st.session_state.X_test = X_test
263
- st.session_state.y_test = y_test
264
- st.session_state.y_pred = y_pred if y is not None else labels
265
- st.session_state.problem_type = problem_type
266
- st.success(f"Model trained successfully in {mode} mode!")
267
-
268
- def visualization_page():
269
- st.header("🔍 Neural Network Evaluation Center")
270
-
271
- if 'best_model' not in st.session_state:
272
- st.warning("Train a model first!")
273
- return
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
274
 
275
- st.subheader("Performance Analysis")
276
- if st.session_state.problem_type == "Classification":
277
- st.plotly_chart(plot_confusion_matrix(st.session_state.y_test, st.session_state.y_pred))
278
- st.plotly_chart(plot_feature_importance(st.session_state.best_model, pd.DataFrame(st.session_state.X_test, columns=st.session_state.df.columns[:-1])))
279
- elif st.session_state.problem_type == "Regression":
280
- st.plotly_chart(plot_residuals(st.session_state.y_test, st.session_state.y_pred))
281
- st.plotly_chart(plot_feature_importance(st.session_state.best_model, pd.DataFrame(st.session_state.X_test, columns=st.session_state.df.columns[:-1])))
282
- else: # Clustering
283
- st.plotly_chart(plot_clusters(pd.DataFrame(st.session_state.X_test, columns=st.session_state.df.columns), st.session_state.y_pred))
284
 
285
- st.subheader("Metrics")
286
- st.write(st.session_state.metrics)
287
-
288
- # Chatbot Interface
289
- def ai_assistant():
290
- st.markdown('<div class="chat-container">', unsafe_allow_html=True)
291
- st.subheader("🧠 Neural Insight Assistant (RAG + Web Search)")
 
 
 
292
 
293
- use_web_search = st.checkbox("Enable Tavily Web Search", value=False)
294
- mode = st.selectbox("Domain Mode", ["Legal", "Financial", "Academic", "Technical"], key="chat_mode")
 
 
 
295
 
296
- for msg in st.session_state.chat_history:
297
- with st.chat_message(msg["role"]):
298
- st.markdown(f'<div class="{msg["role"]}-message">{msg["content"]}</div>', unsafe_allow_html=True)
 
 
299
 
300
- if prompt := st.chat_input("Ask about data, models, or web insights..."):
301
- st.session_state.chat_history.append({"role": "user", "content": prompt})
302
- with st.chat_message("user"):
303
- st.markdown(f'<div class="user-message">{prompt}</div>', unsafe_allow_html=True)
304
-
305
- with st.spinner("Processing..."):
306
- response = get_groq_response(prompt, mode, use_web_search)
307
- st.session_state.chat_history.append({"role": "assistant", "content": response})
308
-
309
- with st.chat_message("assistant"):
310
- st.markdown(f'<div class="bot-message">{response}</div>', unsafe_allow_html=True)
311
 
312
- st.markdown('</div>', unsafe_allow_html=True)
313
-
314
- # Main App Layout
315
- st.markdown("""
316
- <div class="header">
317
- <h1 class="header-title">Neural-Vision Enhanced</h1>
318
- <div class="header-subtitle">Neural Network Development for Domain-Specialized Analysis</div>
319
- </div>
320
- """, unsafe_allow_html=True)
321
-
322
- with st.sidebar:
323
- st.title("🔮 Neural-Vision Enhanced")
324
- page = st.selectbox("Navigation", [
325
- "Data Upload & Analysis",
326
- "Neural Network Training Studio",
327
- "Neural Network Evaluation Center"
328
- ])
329
- st.session_state.active_page = page
330
- st.markdown("---")
331
- st.markdown("**Environment Setup**")
332
- os.environ["TAVILY_API_KEY"] = st.text_input("Tavily API Key", type="password", help="For web search functionality")
333
- st.markdown("---")
334
- st.markdown("v5.0 | © 2025 Neural-Vision")
335
-
336
- # Page Routing
337
- if "Data Upload & Analysis" in page:
338
- data_upload_page()
339
- elif "Neural Network Training Studio" in page:
340
- model_training_page()
341
- else:
342
- visualization_page()
343
 
344
- ai_assistant()
 
 
 
1
+ import gradio as gr
2
+ import groq
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3
  import os
 
4
  import tempfile
5
+ import uuid
6
+ from dotenv import load_dotenv
7
+ from langchain.text_splitter import RecursiveCharacterTextSplitter
8
+ from langchain.vectorstores import FAISS
9
+ from langchain.embeddings import HuggingFaceEmbeddings
10
+ import fitz # PyMuPDF
11
+ import base64
12
+ from PIL import Image
13
+ import io
14
 
15
  # Load environment variables
16
  load_dotenv()
17
+ client = groq.Client(api_key=os.getenv("GROQ_LEGAL_API_KEY"))
 
 
 
 
18
  embeddings = HuggingFaceEmbeddings(model_name="sentence-transformers/all-MiniLM-L6-v2")
19
 
20
+ # Directory to store FAISS indexes
21
+ FAISS_INDEX_DIR = "faiss_indexes_academic"
22
+ if not os.path.exists(FAISS_INDEX_DIR):
23
+ os.makedirs(FAISS_INDEX_DIR)
24
+
25
+ # Dictionary to store user-specific vectorstores
26
+ user_vectorstores = {}
27
+
28
+ # Custom CSS for Academic theme
29
+ custom_css = """
30
+ :root {
31
+ --primary-color: #003366; /* Deep Blue */
32
+ --secondary-color: #000080; /* Navy */
33
+ --light-background: #F5F5F5; /* Light Gray */
34
+ --dark-text: #333333;
35
+ --white: #FFFFFF;
36
+ --border-color: #E5E7EB;
37
+ }
38
+ body { background-color: var(--light-background); font-family: 'Inter', sans-serif; }
39
+ .container { max-width: 1200px !important; margin: 0 auto !important; padding: 10px; }
40
+ .header { background-color: var(--white); border-bottom: 2px solid var(--border-color); padding: 15px 0; margin-bottom: 20px; border-radius: 12px 12px 0 0; box-shadow: 0 2px 4px rgba(0,0,0,0.05); }
41
+ .header-title { color: var(--secondary-color); font-size: 1.8rem; font-weight: 700; text-align: center; }
42
+ .header-subtitle { color: var(--dark-text); font-size: 1rem; text-align: center; margin-top: 5px; }
43
+ .chat-container { border-radius: 12px !important; box-shadow: 0 4px 6px rgba(0,0,0,0.1) !important; background-color: var(--white) !important; border: 1px solid var(--border-color) !important; min-height: 500px; }
44
+ .message-user { background-color: var(--primary-color) !important; color: var(--white) !important; border-radius: 18px 18px 4px 18px !important; padding: 12px 16px !important; margin-left: auto !important; max-width: 80% !important; }
45
+ .message-bot { background-color: #F0F0F0 !important; color: var(--dark-text) !important; border-radius: 18px 18px 18px 4px !important; padding: 12px 16px !important; margin-right: auto !important; max-width: 80% !important; }
46
+ .input-area { background-color: var(--white) !important; border-top: 1px solid var(--border-color) !important; padding: 12px !important; border-radius: 0 0 12px 12px !important; }
47
+ .input-box { border: 1px solid var(--border-color) !important; border-radius: 24px !important; padding: 12px 16px !important; box-shadow: 0 2px 4px rgba(0,0,0,0.05) !important; }
48
+ .send-btn { background-color: var(--secondary-color) !important; border-radius: 24px !important; color: var(--white) !important; padding: 10px 20px !important; font-weight: 500 !important; }
49
+ .clear-btn { background-color: #F0F0F0 !important; border: 1px solid var(--border-color) !important; border-radius: 24px !important; color: var(--dark-text) !important; padding: 8px 16px !important; font-weight: 500 !important; }
50
+ .pdf-viewer-container { border-radius: 12px !important; box-shadow: 0 4px 6px rgba(0,0,0,0.1) !important; background-color: var(--white) !important; border: 1px solid var(--border-color) !important; padding: 20px; }
51
+ .pdf-viewer-image { max-width: 100%; height: auto; border: 1px solid var(--border-color); border-radius: 12px; box-shadow: 0 2px 4px rgba(0,0,0,0.05); }
52
+ .stats-box { background-color: #E6E6FA; padding: 10px; border-radius: 8px; margin-top: 10px; }
53
+ """
54
+
55
+ # Function to process PDF files (unchanged)
56
+ def process_pdf(pdf_file):
57
+ if pdf_file is None:
58
+ return None, "No file uploaded", {"page_images": [], "total_pages": 0, "total_words": 0}
59
+ try:
60
+ session_id = str(uuid.uuid4())
61
+ with tempfile.NamedTemporaryFile(suffix=".pdf", delete=False) as temp_file:
62
+ temp_file.write(pdf_file)
63
+ pdf_path = temp_file.name
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
64
 
65
+ doc = fitz.open(pdf_path)
66
+ texts = [page.get_text() for page in doc]
67
+ page_images = []
68
+ for page in doc:
69
+ pix = page.get_pixmap()
70
+ img_bytes = pix.tobytes("png")
71
+ img_base64 = base64.b64encode(img_bytes).decode("utf-8")
72
+ page_images.append(img_base64)
73
+ total_pages = len(doc)
74
+ total_words = sum(len(text.split()) for text in texts)
75
+ doc.close()
76
+
77
+ text_splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=200)
78
+ chunks = text_splitter.create_documents(texts)
79
+ vectorstore = FAISS.from_documents(chunks, embeddings)
80
+ index_path = os.path.join(FAISS_INDEX_DIR, session_id)
81
+ vectorstore.save_local(index_path)
82
+ user_vectorstores[session_id] = vectorstore
83
+
84
+ os.unlink(pdf_path)
85
+ pdf_state = {"page_images": page_images, "total_pages": total_pages, "total_words": total_words}
86
+ return session_id, f"✅ Successfully processed {len(chunks)} text chunks from your PDF", pdf_state
87
+ except Exception as e:
88
+ if "pdf_path" in locals() and os.path.exists(pdf_path):
89
+ os.unlink(pdf_path)
90
+ return None, f"Error processing PDF: {str(e)}", {"page_images": [], "total_pages": 0, "total_words": 0}
91
+
92
+ # Function to generate chatbot responses with Academic theme
93
+ def generate_response(message, session_id, model_name, history):
94
+ if not message:
95
+ return history
96
+ try:
97
+ context = ""
98
+ if session_id and session_id in user_vectorstores:
99
+ vectorstore = user_vectorstores[session_id]
100
+ docs = vectorstore.similarity_search(message, k=3)
101
+ if docs:
102
+ context = "\n\nRelevant information from uploaded PDF:\n" + "\n".join(f"- {doc.page_content}" for doc in docs)
103
+ system_prompt = "You are an academic assistant specializing in analyzing research papers, theses, and scholarly articles."
104
+ if context:
105
+ system_prompt += " Use the following context to answer the question if relevant: " + context
106
+ completion = client.chat.completions.create(
107
+ model=model_name,
108
+ messages=[
109
+ {"role": "system", "content": system_prompt},
110
+ {"role": "user", "content": message}
111
+ ],
112
+ temperature=0.7,
113
+ max_tokens=1024
114
+ )
115
+ response = completion.choices[0].message.content
116
+ history.append((message, response))
117
+ return history
118
+ except Exception as e:
119
+ history.append((message, f"Error generating response: {str(e)}"))
120
+ return history
121
+
122
+ # Functions to update PDF viewer (unchanged)
123
+ def update_pdf_viewer(pdf_state):
124
+ if not pdf_state["total_pages"]:
125
+ return 0, None, "No PDF uploaded yet"
126
+ try:
127
+ img_data = base64.b64decode(pdf_state["page_images"][0])
128
+ img = Image.open(io.BytesIO(img_data))
129
+ return pdf_state["total_pages"], img, f"**Total Pages:** {pdf_state['total_pages']}\n**Total Words:** {pdf_state['total_words']}"
130
+ except Exception as e:
131
+ print(f"Error decoding image: {e}")
132
+ return 0, None, "Error displaying PDF"
133
+
134
+ def update_image(page_num, pdf_state):
135
+ if not pdf_state["total_pages"] or page_num < 1 or page_num > pdf_state["total_pages"]:
136
+ return None
137
+ try:
138
+ img_data = base64.b64decode(pdf_state["page_images"][page_num - 1])
139
+ img = Image.open(io.BytesIO(img_data))
140
+ return img
141
+ except Exception as e:
142
+ print(f"Error decoding image: {e}")
143
+ return None
144
+
145
+ # Gradio interface
146
+ with gr.Blocks(css=custom_css, theme=gr.themes.Soft()) as demo:
147
+ current_session_id = gr.State(None)
148
+ pdf_state = gr.State({"page_images": [], "total_pages": 0, "total_words": 0})
149
+ gr.HTML("""
150
+ <div class="header">
151
+ <div class="header-title">Scholar-Vision</div>
152
+ <div class="header-subtitle">Analyze academic papers with Groq's LLM API.</div>
153
+ </div>
154
+ """)
155
+ with gr.Row(elem_classes="container"):
156
+ with gr.Column(scale=1, min_width=300):
157
+ pdf_file = gr.File(label="Upload PDF Document", file_types=[".pdf"], type="binary")
158
+ upload_button = gr.Button("Process PDF", variant="primary")
159
+ pdf_status = gr.Markdown("No PDF uploaded yet")
160
+ model_dropdown = gr.Dropdown(
161
+ choices=["llama3-70b-8192", "llama3-8b-8192", "mixtral-8x7b-32768", "gemma-7b-it"],
162
+ value="llama3-70b-8192",
163
+ label="Select Groq Model"
164
+ )
165
+ with gr.Column(scale=2, min_width=600):
166
+ with gr.Tabs():
167
+ with gr.TabItem("PDF Viewer"):
168
+ with gr.Column(elem_classes="pdf-viewer-container"):
169
+ page_slider = gr.Slider(minimum=1, maximum=1, step=1, label="Page Number", value=1)
170
+ pdf_image = gr.Image(label="PDF Page", type="pil", elem_classes="pdf-viewer-image")
171
+ stats_display = gr.Markdown("No PDF uploaded yet", elem_classes="stats-box")
172
 
173
+ with gr.Row(elem_classes="container"):
174
+ with gr.Column(scale=2, min_width=600):
175
+ chatbot = gr.Chatbot(height=500, bubble_full_width=False, show_copy_button=True, elem_classes="chat-container")
176
+ with gr.Row():
177
+ msg = gr.Textbox(show_label=False, placeholder="Ask about your academic document...", scale=5)
178
+ send_btn = gr.Button("Send", scale=1)
179
+ clear_btn = gr.Button("Clear Conversation")
 
 
180
 
181
+ # Event Handlers (unchanged)
182
+ upload_button.click(
183
+ process_pdf,
184
+ inputs=[pdf_file],
185
+ outputs=[current_session_id, pdf_status, pdf_state]
186
+ ).then(
187
+ update_pdf_viewer,
188
+ inputs=[pdf_state],
189
+ outputs=[page_slider, pdf_image, stats_display]
190
+ )
191
 
192
+ msg.submit(
193
+ generate_response,
194
+ inputs=[msg, current_session_id, model_dropdown, chatbot],
195
+ outputs=[chatbot]
196
+ ).then(lambda: "", None, [msg])
197
 
198
+ send_btn.click(
199
+ generate_response,
200
+ inputs=[msg, current_session_id, model_dropdown, chatbot],
201
+ outputs=[chatbot]
202
+ ).then(lambda: "", None, [msg])
203
 
204
+ clear_btn.click(
205
+ lambda: ([], None, "No PDF uploaded yet", {"page_images": [], "total_pages": 0, "total_words": 0}, 0, None, "No PDF uploaded yet"),
206
+ None,
207
+ [chatbot, current_session_id, pdf_status, pdf_state, page_slider, pdf_image, stats_display]
208
+ )
 
 
 
 
 
 
209
 
210
+ page_slider.change(
211
+ update_image,
212
+ inputs=[page_slider, pdf_state],
213
+ outputs=[pdf_image]
214
+ )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
215
 
216
+ # Launch the app
217
+ if __name__ == "__main__":
218
+ demo.launch()