shubham142000 commited on
Commit
6f9cc79
·
verified ·
1 Parent(s): e467151

Update src/streamlit_app.py

Browse files
Files changed (1) hide show
  1. src/streamlit_app.py +473 -202
src/streamlit_app.py CHANGED
@@ -1,3 +1,164 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  # import os
2
  # import json
3
  # import streamlit as st
@@ -19,33 +180,59 @@
19
  # # -------------------#
20
  # st.set_page_config(page_title="FutureScope: Research Direction Explorer", layout="wide")
21
 
22
- # st.markdown("""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
23
  # <style>
24
- # body {
25
- # background: linear-gradient(135deg, #0f2027, #203a43, #2c5364);
26
- # color: #FFFFFF;
27
- # }
28
- # h1, h2, h3 {
29
- # text-align: center;
30
- # color: #FFD700;
31
  # font-family: 'Poppins', sans-serif;
32
- # }
33
- # .footer {
 
 
 
 
34
  # position: fixed;
35
  # left: 0;
36
  # bottom: 0;
37
  # width: 100%;
38
- # color: white;
39
  # text-align: center;
40
  # padding: 10px;
41
- # background-color: rgba(0,0,0,0.4);
42
- # }
43
- # .stButton > button {
44
- # background-color: #FFD700 !important;
45
  # color: black !important;
46
  # font-weight: bold;
47
  # border-radius: 10px;
48
- # }
 
 
 
49
  # </style>
50
  # """, unsafe_allow_html=True)
51
 
@@ -96,13 +283,10 @@
96
  # # JSON Cleaning & Parsing
97
  # # -------------------#
98
  # def extract_json(text):
99
- # """Extract valid JSON portion from the model response."""
100
  # text = text.strip()
101
- # text = re.sub(r"^```json|```$", "", text).strip() # remove code fences
102
  # match = re.search(r'\{.*\}', text, re.DOTALL)
103
- # if match:
104
- # return match.group(0)
105
- # return text
106
 
107
  # cleaned = extract_json(raw_content)
108
  # try:
@@ -116,7 +300,7 @@
116
  # # Display Results
117
  # # -------------------#
118
  # st.markdown("## 🧩 Evolution Summary")
119
- # st.markdown(f"<div style='background:#1e2a38;padding:15px;border-radius:10px;'>{data['evolution_summary']}</div>", unsafe_allow_html=True)
120
 
121
  # # Timeline Chart
122
  # if "timeline" in data and len(data["timeline"]) > 0:
@@ -125,7 +309,7 @@
125
  # fig = px.scatter(df, x="year", y="trend", title="📈 Topic Evolution Over Time",
126
  # size=[10]*len(df), text="trend", color_discrete_sequence=["gold"])
127
  # fig.update_traces(textposition='top center', marker=dict(symbol="circle"))
128
- # fig.update_layout(template="plotly_dark", height=500)
129
  # st.plotly_chart(fig, use_container_width=True)
130
  # else:
131
  # st.warning("Timeline data invalid — showing raw table:")
@@ -135,7 +319,7 @@
135
  # st.markdown("## 🔮 Predicted Future Directions")
136
  # for item in data.get("future_directions", []):
137
  # st.markdown(f"""
138
- # <div style='background:#142733;padding:15px;margin:10px;border-radius:10px;'>
139
  # <h4>🧠 {item['title']}</h4>
140
  # <p>{item['reason']}</p>
141
  # </div>
@@ -157,189 +341,276 @@
157
  # # -------------------#
158
  # # Footer
159
  # # -------------------#
160
- # st.markdown("<div class='footer'>© Group 6 ILP TCS Research ", unsafe_allow_html=True)
161
 
162
- import os
163
- import json
164
- import streamlit as st
165
  import pandas as pd
166
- import plotly.express as px
167
  from together import Together
168
- from dotenv import load_dotenv
169
- import re
170
 
171
- # -------------------#
172
- # Secure API key load
173
- # -------------------#
174
- load_dotenv()
 
 
 
 
175
  TOGETHER_API_KEY = os.getenv("TOGETHER_API_KEY", "987adcf573b9658c775b671270aef959b3d38793771932f372f9f2a9ed5b78bf")
 
176
  client = Together(api_key=TOGETHER_API_KEY)
177
 
178
- # -------------------#
179
- # Streamlit UI setup
180
- # -------------------#
181
- st.set_page_config(page_title="FutureScope: Research Direction Explorer", layout="wide")
182
-
183
- # Detect Streamlit theme
184
- try:
185
- theme = st.get_option("theme.base")
186
- except:
187
- theme = "dark"
188
-
189
- # Define theme colors
190
- if theme == "light":
191
- BACKGROUND_GRADIENT = "linear-gradient(135deg, #f9f9f9, #eaeaea, #dddddd)"
192
- TEXT_COLOR = "#000000"
193
- TITLE_COLOR = "#DAA520"
194
- CARD_BG = "#ffffff"
195
- FOOTER_BG = "rgba(0, 0, 0, 0.1)"
196
- else:
197
- BACKGROUND_GRADIENT = "linear-gradient(135deg, #0f2027, #203a43, #2c5364)"
198
- TEXT_COLOR = "#FFFFFF"
199
- TITLE_COLOR = "#FFD700"
200
- CARD_BG = "#1e2a38"
201
- FOOTER_BG = "rgba(0, 0, 0, 0.4)"
202
-
203
- # -------------------#
204
- # Dynamic CSS Styling
205
- # -------------------#
206
- st.markdown(f"""
207
- <style>
208
- body {{
209
- background: {BACKGROUND_GRADIENT};
210
- color: {TEXT_COLOR};
211
- font-family: 'Poppins', sans-serif;
212
- }}
213
- h1, h2, h3 {{
214
- text-align: center;
215
- color: {TITLE_COLOR};
216
- }}
217
- .footer {{
218
- position: fixed;
219
- left: 0;
220
- bottom: 0;
221
- width: 100%;
222
- color: {TEXT_COLOR};
223
- text-align: center;
224
- padding: 10px;
225
- background-color: {FOOTER_BG};
226
- }}
227
- .stButton > button {{
228
- background-color: {TITLE_COLOR} !important;
229
- color: black !important;
230
- font-weight: bold;
231
- border-radius: 10px;
232
- }}
233
- div[data-testid="stMarkdownContainer"] p {{
234
- color: {TEXT_COLOR};
235
- }}
236
- </style>
237
- """, unsafe_allow_html=True)
238
-
239
- # -------------------#
240
- # App Title
241
- # -------------------#
242
- st.markdown("<h1>🧭 FutureScope: Research Direction Explorer</h1>", unsafe_allow_html=True)
243
- st.markdown("<p style='text-align:center;'>Discover how your research area evolved and where it's heading next 🚀</p>", unsafe_allow_html=True)
244
-
245
- # -------------------#
246
- # User Input
247
- # -------------------#
248
- user_topic = st.text_input("🔍 Enter your research topic", placeholder="e.g. Graph Neural Networks for Drug Discovery")
249
-
250
- # -------------------#
251
- # Main Logic
252
- # -------------------#
253
- if st.button("Generate Research Insights"):
254
- if not user_topic.strip():
255
- st.warning("⚠️ Please enter a valid research topic.")
256
- else:
257
- with st.spinner("Analyzing topic evolution and forecasting future directions... ⏳"):
258
-
259
- # Prompt Design
260
- prompt = f"""
261
- You are a world-class AI research assistant specialized in analyzing research trends.
262
- Given the topic: "{user_topic}", perform the following:
263
- 1. Summarize how this research area has evolved in the past 10–15 years.
264
- 2. Identify key milestones and subfields in a timeline format.
265
- 3. Predict 3–5 future research directions and explain why each matters.
266
- Return the output strictly in JSON format like this:
267
- {{
268
- "evolution_summary": "...",
269
- "timeline": [{{"year": ..., "trend": "..."}}, ...],
270
- "future_directions": [{{"title": "...", "reason": "..."}}, ...]
271
- }}
272
- """
273
-
274
- # Call Together API
275
- response = client.chat.completions.create(
276
- model="meta-llama/Llama-3.3-70B-Instruct-Turbo-Free",
277
- messages=[{"role": "user", "content": prompt}]
278
  )
279
-
280
- raw_content = response.choices[0].message.content
281
-
282
- # -------------------#
283
- # JSON Cleaning & Parsing
284
- # -------------------#
285
- def extract_json(text):
286
- text = text.strip()
287
- text = re.sub(r"^```json|```$", "", text).strip()
288
- match = re.search(r'\{.*\}', text, re.DOTALL)
289
- return match.group(0) if match else text
290
-
291
- cleaned = extract_json(raw_content)
292
- try:
293
- data = json.loads(cleaned)
294
- except Exception as e:
295
- st.error(f"⚠️ Failed to parse JSON: {e}")
296
- st.text_area("Raw Response", raw_content, height=300)
297
- st.stop()
298
-
299
- # -------------------#
300
- # Display Results
301
- # -------------------#
302
- st.markdown("## 🧩 Evolution Summary")
303
- st.markdown(f"<div style='background:{CARD_BG};padding:15px;border-radius:10px;color:{TEXT_COLOR};'>{data['evolution_summary']}</div>", unsafe_allow_html=True)
304
-
305
- # Timeline Chart
306
- if "timeline" in data and len(data["timeline"]) > 0:
307
- df = pd.DataFrame(data["timeline"])
308
- if "year" in df.columns and "trend" in df.columns:
309
- fig = px.scatter(df, x="year", y="trend", title="📈 Topic Evolution Over Time",
310
- size=[10]*len(df), text="trend", color_discrete_sequence=["gold"])
311
- fig.update_traces(textposition='top center', marker=dict(symbol="circle"))
312
- fig.update_layout(template="plotly_dark" if theme == "dark" else "plotly_white", height=500)
313
- st.plotly_chart(fig, use_container_width=True)
314
- else:
315
- st.warning("Timeline data invalid — showing raw table:")
316
- st.dataframe(df)
317
-
318
- # Future Directions
319
- st.markdown("## 🔮 Predicted Future Directions")
320
- for item in data.get("future_directions", []):
321
- st.markdown(f"""
322
- <div style='background:{CARD_BG};padding:15px;margin:10px;border-radius:10px;color:{TEXT_COLOR};'>
323
- <h4>🧠 {item['title']}</h4>
324
- <p>{item['reason']}</p>
325
- </div>
326
- """, unsafe_allow_html=True)
327
-
328
- # Tools: Copy / Download
329
- col1, col2 = st.columns(2)
330
- with col1:
331
- if st.button("📋 Copy Insights"):
332
- st.write("Copied to clipboard! (Use Ctrl+C manually to copy)")
333
- with col2:
334
- st.download_button(
335
- label="💾 Download JSON",
336
- data=json.dumps(data, indent=2),
337
- file_name=f"{user_topic.replace(' ','_')}_future_directions.json",
338
- mime="application/json"
339
- )
340
-
341
- # -------------------#
342
- # Footer
343
- # -------------------#
344
- st.markdown(f"<div class='footer'>© Group 6 ILP TCS Research</div>", unsafe_allow_html=True)
345
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # # import os
2
+ # # import json
3
+ # # import streamlit as st
4
+ # # import pandas as pd
5
+ # # import plotly.express as px
6
+ # # from together import Together
7
+ # # from dotenv import load_dotenv
8
+ # # import re
9
+
10
+ # # # -------------------#
11
+ # # # Secure API key load
12
+ # # # -------------------#
13
+ # # load_dotenv()
14
+ # # TOGETHER_API_KEY = os.getenv("TOGETHER_API_KEY", "987adcf573b9658c775b671270aef959b3d38793771932f372f9f2a9ed5b78bf")
15
+ # # client = Together(api_key=TOGETHER_API_KEY)
16
+
17
+ # # # -------------------#
18
+ # # # Streamlit UI setup
19
+ # # # -------------------#
20
+ # # st.set_page_config(page_title="FutureScope: Research Direction Explorer", layout="wide")
21
+
22
+ # # st.markdown("""
23
+ # # <style>
24
+ # # body {
25
+ # # background: linear-gradient(135deg, #0f2027, #203a43, #2c5364);
26
+ # # color: #FFFFFF;
27
+ # # }
28
+ # # h1, h2, h3 {
29
+ # # text-align: center;
30
+ # # color: #FFD700;
31
+ # # font-family: 'Poppins', sans-serif;
32
+ # # }
33
+ # # .footer {
34
+ # # position: fixed;
35
+ # # left: 0;
36
+ # # bottom: 0;
37
+ # # width: 100%;
38
+ # # color: white;
39
+ # # text-align: center;
40
+ # # padding: 10px;
41
+ # # background-color: rgba(0,0,0,0.4);
42
+ # # }
43
+ # # .stButton > button {
44
+ # # background-color: #FFD700 !important;
45
+ # # color: black !important;
46
+ # # font-weight: bold;
47
+ # # border-radius: 10px;
48
+ # # }
49
+ # # </style>
50
+ # # """, unsafe_allow_html=True)
51
+
52
+ # # # -------------------#
53
+ # # # App Title
54
+ # # # -------------------#
55
+ # # st.markdown("<h1>🧭 FutureScope: Research Direction Explorer</h1>", unsafe_allow_html=True)
56
+ # # st.markdown("<p style='text-align:center;'>Discover how your research area evolved and where it's heading next 🚀</p>", unsafe_allow_html=True)
57
+
58
+ # # # -------------------#
59
+ # # # User Input
60
+ # # # -------------------#
61
+ # # user_topic = st.text_input("🔍 Enter your research topic", placeholder="e.g. Graph Neural Networks for Drug Discovery")
62
+
63
+ # # # -------------------#
64
+ # # # Main Logic
65
+ # # # -------------------#
66
+ # # if st.button("Generate Research Insights"):
67
+ # # if not user_topic.strip():
68
+ # # st.warning("⚠️ Please enter a valid research topic.")
69
+ # # else:
70
+ # # with st.spinner("Analyzing topic evolution and forecasting future directions... ⏳"):
71
+
72
+ # # # Prompt Design
73
+ # # prompt = f"""
74
+ # # You are a world-class AI research assistant specialized in analyzing research trends.
75
+ # # Given the topic: "{user_topic}", perform the following:
76
+ # # 1. Summarize how this research area has evolved in the past 10–15 years.
77
+ # # 2. Identify key milestones and subfields in a timeline format.
78
+ # # 3. Predict 3–5 future research directions and explain why each matters.
79
+ # # Return the output strictly in JSON format like this:
80
+ # # {{
81
+ # # "evolution_summary": "...",
82
+ # # "timeline": [{{"year": ..., "trend": "..."}}, ...],
83
+ # # "future_directions": [{{"title": "...", "reason": "..."}}, ...]
84
+ # # }}
85
+ # # """
86
+
87
+ # # # Call Together API
88
+ # # response = client.chat.completions.create(
89
+ # # model="meta-llama/Llama-3.3-70B-Instruct-Turbo-Free",
90
+ # # messages=[{"role": "user", "content": prompt}]
91
+ # # )
92
+
93
+ # # raw_content = response.choices[0].message.content
94
+
95
+ # # # -------------------#
96
+ # # # JSON Cleaning & Parsing
97
+ # # # -------------------#
98
+ # # def extract_json(text):
99
+ # # """Extract valid JSON portion from the model response."""
100
+ # # text = text.strip()
101
+ # # text = re.sub(r"^```json|```$", "", text).strip() # remove code fences
102
+ # # match = re.search(r'\{.*\}', text, re.DOTALL)
103
+ # # if match:
104
+ # # return match.group(0)
105
+ # # return text
106
+
107
+ # # cleaned = extract_json(raw_content)
108
+ # # try:
109
+ # # data = json.loads(cleaned)
110
+ # # except Exception as e:
111
+ # # st.error(f"⚠️ Failed to parse JSON: {e}")
112
+ # # st.text_area("Raw Response", raw_content, height=300)
113
+ # # st.stop()
114
+
115
+ # # # -------------------#
116
+ # # # Display Results
117
+ # # # -------------------#
118
+ # # st.markdown("## 🧩 Evolution Summary")
119
+ # # st.markdown(f"<div style='background:#1e2a38;padding:15px;border-radius:10px;'>{data['evolution_summary']}</div>", unsafe_allow_html=True)
120
+
121
+ # # # Timeline Chart
122
+ # # if "timeline" in data and len(data["timeline"]) > 0:
123
+ # # df = pd.DataFrame(data["timeline"])
124
+ # # if "year" in df.columns and "trend" in df.columns:
125
+ # # fig = px.scatter(df, x="year", y="trend", title="📈 Topic Evolution Over Time",
126
+ # # size=[10]*len(df), text="trend", color_discrete_sequence=["gold"])
127
+ # # fig.update_traces(textposition='top center', marker=dict(symbol="circle"))
128
+ # # fig.update_layout(template="plotly_dark", height=500)
129
+ # # st.plotly_chart(fig, use_container_width=True)
130
+ # # else:
131
+ # # st.warning("Timeline data invalid — showing raw table:")
132
+ # # st.dataframe(df)
133
+
134
+ # # # Future Directions
135
+ # # st.markdown("## 🔮 Predicted Future Directions")
136
+ # # for item in data.get("future_directions", []):
137
+ # # st.markdown(f"""
138
+ # # <div style='background:#142733;padding:15px;margin:10px;border-radius:10px;'>
139
+ # # <h4>🧠 {item['title']}</h4>
140
+ # # <p>{item['reason']}</p>
141
+ # # </div>
142
+ # # """, unsafe_allow_html=True)
143
+
144
+ # # # Tools: Copy / Download
145
+ # # col1, col2 = st.columns(2)
146
+ # # with col1:
147
+ # # if st.button("📋 Copy Insights"):
148
+ # # st.write("Copied to clipboard! (Use Ctrl+C manually to copy)")
149
+ # # with col2:
150
+ # # st.download_button(
151
+ # # label="💾 Download JSON",
152
+ # # data=json.dumps(data, indent=2),
153
+ # # file_name=f"{user_topic.replace(' ','_')}_future_directions.json",
154
+ # # mime="application/json"
155
+ # # )
156
+
157
+ # # # -------------------#
158
+ # # # Footer
159
+ # # # -------------------#
160
+ # # st.markdown("<div class='footer'>© Group 6 ILP TCS Research ", unsafe_allow_html=True)
161
+
162
  # import os
163
  # import json
164
  # import streamlit as st
 
180
  # # -------------------#
181
  # st.set_page_config(page_title="FutureScope: Research Direction Explorer", layout="wide")
182
 
183
+ # # Detect Streamlit theme
184
+ # try:
185
+ # theme = st.get_option("theme.base")
186
+ # except:
187
+ # theme = "dark"
188
+
189
+ # # Define theme colors
190
+ # if theme == "light":
191
+ # BACKGROUND_GRADIENT = "linear-gradient(135deg, #f9f9f9, #eaeaea, #dddddd)"
192
+ # TEXT_COLOR = "#000000"
193
+ # TITLE_COLOR = "#DAA520"
194
+ # CARD_BG = "#ffffff"
195
+ # FOOTER_BG = "rgba(0, 0, 0, 0.1)"
196
+ # else:
197
+ # BACKGROUND_GRADIENT = "linear-gradient(135deg, #0f2027, #203a43, #2c5364)"
198
+ # TEXT_COLOR = "#FFFFFF"
199
+ # TITLE_COLOR = "#FFD700"
200
+ # CARD_BG = "#1e2a38"
201
+ # FOOTER_BG = "rgba(0, 0, 0, 0.4)"
202
+
203
+ # # -------------------#
204
+ # # Dynamic CSS Styling
205
+ # # -------------------#
206
+ # st.markdown(f"""
207
  # <style>
208
+ # body {{
209
+ # background: {BACKGROUND_GRADIENT};
210
+ # color: {TEXT_COLOR};
 
 
 
 
211
  # font-family: 'Poppins', sans-serif;
212
+ # }}
213
+ # h1, h2, h3 {{
214
+ # text-align: center;
215
+ # color: {TITLE_COLOR};
216
+ # }}
217
+ # .footer {{
218
  # position: fixed;
219
  # left: 0;
220
  # bottom: 0;
221
  # width: 100%;
222
+ # color: {TEXT_COLOR};
223
  # text-align: center;
224
  # padding: 10px;
225
+ # background-color: {FOOTER_BG};
226
+ # }}
227
+ # .stButton > button {{
228
+ # background-color: {TITLE_COLOR} !important;
229
  # color: black !important;
230
  # font-weight: bold;
231
  # border-radius: 10px;
232
+ # }}
233
+ # div[data-testid="stMarkdownContainer"] p {{
234
+ # color: {TEXT_COLOR};
235
+ # }}
236
  # </style>
237
  # """, unsafe_allow_html=True)
238
 
 
283
  # # JSON Cleaning & Parsing
284
  # # -------------------#
285
  # def extract_json(text):
 
286
  # text = text.strip()
287
+ # text = re.sub(r"^```json|```$", "", text).strip()
288
  # match = re.search(r'\{.*\}', text, re.DOTALL)
289
+ # return match.group(0) if match else text
 
 
290
 
291
  # cleaned = extract_json(raw_content)
292
  # try:
 
300
  # # Display Results
301
  # # -------------------#
302
  # st.markdown("## 🧩 Evolution Summary")
303
+ # st.markdown(f"<div style='background:{CARD_BG};padding:15px;border-radius:10px;color:{TEXT_COLOR};'>{data['evolution_summary']}</div>", unsafe_allow_html=True)
304
 
305
  # # Timeline Chart
306
  # if "timeline" in data and len(data["timeline"]) > 0:
 
309
  # fig = px.scatter(df, x="year", y="trend", title="📈 Topic Evolution Over Time",
310
  # size=[10]*len(df), text="trend", color_discrete_sequence=["gold"])
311
  # fig.update_traces(textposition='top center', marker=dict(symbol="circle"))
312
+ # fig.update_layout(template="plotly_dark" if theme == "dark" else "plotly_white", height=500)
313
  # st.plotly_chart(fig, use_container_width=True)
314
  # else:
315
  # st.warning("Timeline data invalid — showing raw table:")
 
319
  # st.markdown("## 🔮 Predicted Future Directions")
320
  # for item in data.get("future_directions", []):
321
  # st.markdown(f"""
322
+ # <div style='background:{CARD_BG};padding:15px;margin:10px;border-radius:10px;color:{TEXT_COLOR};'>
323
  # <h4>🧠 {item['title']}</h4>
324
  # <p>{item['reason']}</p>
325
  # </div>
 
341
  # # -------------------#
342
  # # Footer
343
  # # -------------------#
344
+ # st.markdown(f"<div class='footer'>© Group 6 ILP TCS Research</div>", unsafe_allow_html=True)
345
 
346
+
347
+
348
+ import os, json, time, re, requests, random
349
  import pandas as pd
350
+ import streamlit as st
351
  from together import Together
 
 
352
 
353
+ # =========================
354
+ # 0️⃣ Configuration & Setup
355
+ # =========================
356
+ st.set_page_config(page_title="📚 pResearch Retrieval", layout="wide", page_icon=":books:")
357
+ st.title("🤖 **pResearch: Multi-Agent Research Retrieval System**")
358
+ st.caption("Built with LLM-based reasoning, multi-agent intelligence, and human-in-loop control.")
359
+ st.markdown("---")
360
+
361
  TOGETHER_API_KEY = os.getenv("TOGETHER_API_KEY", "987adcf573b9658c775b671270aef959b3d38793771932f372f9f2a9ed5b78bf")
362
+ SEMANTIC_API_KEY = os.getenv("SEMANTIC_API_KEY", "b2EsaPVVN1890PXdCeum37K9zKq4AYY46n8QyLvp")
363
  client = Together(api_key=TOGETHER_API_KEY)
364
 
365
+ # =========================
366
+ # Unified LLM Call
367
+ # =========================
368
+ @st.cache_data(show_spinner=False)
369
+ def llm_call(prompt: str, temperature=0.2, max_retries=3):
370
+ for attempt in range(max_retries):
371
+ try:
372
+ resp = client.chat.completions.create(
373
+ model="meta-llama/Llama-3.3-70B-Instruct-Turbo",
374
+ messages=[{"role": "user", "content": prompt}],
375
+ temperature=temperature
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
376
  )
377
+ return resp.choices[0].message.content.strip()
378
+ except Exception as e:
379
+ time.sleep(1 + attempt)
380
+ return "LLM error (see logs)"
381
+
382
+
383
+ # ============================================================
384
+ # 1️⃣ Query Reformulator Agent
385
+ # ============================================================
386
+ def agent_query_reformulator(query: str):
387
+ prompt = f"""
388
+ You are an expert academic assistant.
389
+ Reformulate the query below into 5 semantically diverse and rich alternatives
390
+ that explore different perspectives (methods, datasets, applications, etc.)
391
+
392
+ Query: "{query}"
393
+
394
+ Respond in JSON format:
395
+ {{
396
+ "reformulated_queries": [
397
+ {{ "id": 1, "query": "..." }},
398
+ {{ "id": 2, "query": "..." }},
399
+ {{ "id": 3, "query": "..." }},
400
+ {{ "id": 4, "query": "..." }},
401
+ {{ "id": 5, "query": "..." }}
402
+ ]
403
+ }}
404
+ """
405
+ output = llm_call(prompt)
406
+ cleaned = re.sub(r"```json|```", "", output).strip()
407
+
408
+ try:
409
+ data = json.loads(cleaned)
410
+ queries = [q["query"] for q in data.get("reformulated_queries", []) if "query" in q]
411
+ except Exception:
412
+ queries = []
413
+
414
+ # fallback diversity
415
+ while len(queries) < 5:
416
+ alt = llm_call(f"Generate a diverse reformulation of: {query}")
417
+ queries.append(alt[:300])
418
+ queries = list(dict.fromkeys(queries))[:5]
419
+ return {"original_query": query, "reformulated_queries": [{"id": i+1, "query": q} for i, q in enumerate(queries)]}
420
+
421
+
422
+ # ============================================================
423
+ # 2️⃣ Retriever Agent (Semantic Scholar)
424
+ # ============================================================
425
+ def agent_retriever(query, top_k=20):
426
+ url = "https://api.semanticscholar.org/graph/v1/paper/search"
427
+ headers = {"x-api-key": SEMANTIC_API_KEY}
428
+ params = {
429
+ "query": query, "limit": top_k,
430
+ "fields": "paperId,title,abstract,year,authors,url,venue,citationCount"
431
+ }
432
+ resp = requests.get(url, headers=headers, params=params)
433
+ if resp.status_code != 200:
434
+ return []
435
+ return resp.json().get("data", [])
436
+
437
+
438
+ # ============================================================
439
+ # 3️⃣ Reranker Agent
440
+ # ============================================================
441
+ def agent_reranker(query, papers):
442
+ cleaned_papers = [p for p in papers if p.get("abstract")]
443
+ random.shuffle(cleaned_papers)
444
+ for batch_start in range(0, len(cleaned_papers), 5):
445
+ batch = cleaned_papers[batch_start:batch_start+5]
446
+ papers_str = "\n\n".join([
447
+ f"[{i+1}] Title: {p.get('title','N/A')}\nAbstract: {p.get('abstract','')[:500]}"
448
+ for i,p in enumerate(batch)
449
+ ])
450
+ prompt = f"""
451
+ You are a relevance scoring agent.
452
+ Given a research query and 5 papers, assign a score (0–1) to each paper for its relevance.
453
+
454
+ Query: {query}
455
+ Papers:
456
+ {papers_str}
457
+
458
+ Respond strictly in JSON:
459
+ {{ "results": [{{"id": 1, "score": 0.85}}, ...] }}
460
+ """
461
+ output = llm_call(prompt)
462
+ cleaned = re.sub(r"```json|```", "", output).strip()
463
+ try:
464
+ results = json.loads(cleaned)["results"]
465
+ for i,r in enumerate(results):
466
+ cleaned_papers[batch_start+i]["semantic_score"] = r.get("score",0)
467
+ except:
468
+ for i in range(len(batch)):
469
+ cleaned_papers[batch_start+i]["semantic_score"] = 0.0
470
+ return sorted(cleaned_papers, key=lambda x: x.get("semantic_score",0), reverse=True)
471
+
472
+
473
+ # ============================================================
474
+ # 4️⃣ Weighting Agent (Meta Scorer)
475
+ # ============================================================
476
+ def agent_weighting(papers):
477
+ prompt = """
478
+ You are an expert in bibliometrics.
479
+ Assign importance weights (sum=1.0) for how papers should be ranked based on:
480
+ - semantic_score (LLM relevance)
481
+ - citationCount
482
+ - recency
483
+ - venue quality
484
+
485
+ Return JSON:
486
+ {"weights":{"semantic_score":0.55,"citations":0.25,"recency":0.15,"venue":0.05}}
487
+ """
488
+ output = llm_call(prompt)
489
+ cleaned = re.sub(r"```json|```", "", output).strip()
490
+ try:
491
+ weights = json.loads(cleaned)["weights"]
492
+ except:
493
+ weights = {"semantic_score":0.55,"citations":0.25,"recency":0.15,"venue":0.05}
494
+ total = sum(weights.values())
495
+ return {k:v/total for k,v in weights.items()}
496
+
497
+
498
+ # ============================================================
499
+ # 5️⃣ Meta-Scoring and Ranking
500
+ # ============================================================
501
+ def agent_meta_scorer(papers, weights):
502
+ current_year = 2025
503
+ prestige = {"CVPR":1.0,"ICCV":0.95,"ECCV":0.9,"NEURIPS":0.9,"ICML":0.85,"AAAI":0.8,"IJCAI":0.8,"ARXIV":0.4}
504
+ for p in papers:
505
+ sem = p.get("semantic_score",0)
506
+ cit = min(p.get("citationCount",0)/1000,1.0)
507
+ rec = max(0, 1 - (current_year - p.get("year",2000))/10)
508
+ venue_name = (p.get("venue") or "").upper()
509
+ ven = next((v for k,v in prestige.items() if k in venue_name), 0.3)
510
+ p["final_score"] = (
511
+ weights["semantic_score"]*sem +
512
+ weights["citations"]*cit +
513
+ weights["recency"]*rec +
514
+ weights["venue"]*ven
515
+ )
516
+ return sorted(papers, key=lambda x: x["final_score"], reverse=True)
517
+
518
+
519
+ # ============================================================
520
+ # 6️⃣ Critique Agent
521
+ # ============================================================
522
+ def agent_critique(papers, query):
523
+ top_titles = [p["title"] for p in papers[:5]]
524
+ prompt = f"""
525
+ As a research critic, evaluate whether these top papers are relevant to:
526
+ "{query}"
527
+
528
+ Papers: {json.dumps(top_titles, indent=2)}
529
+
530
+ Respond as JSON:
531
+ {{ "critique": "...", "relevance_score": 0–1 }}
532
+ """
533
+ output = llm_call(prompt)
534
+ cleaned = re.sub(r"```json|```", "", output).strip()
535
+ try:
536
+ return json.loads(cleaned)
537
+ except:
538
+ return {"critique":"Automatic check fallback.","relevance_score":0.7}
539
+
540
+
541
+ # ============================================================
542
+ # 7️⃣ Human-in-Loop Fallback
543
+ # ============================================================
544
+ def human_feedback_loop(papers):
545
+ st.warning("⚠️ Low relevance detected — human feedback required.")
546
+ for i,p in enumerate(papers[:3]):
547
+ st.markdown(f"**{i+1}. {p['title']}** *(Score: {p['final_score']:.3f})*")
548
+ st.caption(f"{p.get('abstract','')[:250]}...")
549
+ choice = st.radio("Approve ranking?", ["Yes","No"], index=0)
550
+ if choice == "No":
551
+ st.info("🔄 Re-ranking by citation count.")
552
+ papers = sorted(papers, key=lambda x: x.get("citationCount",0), reverse=True)
553
+ return papers
554
+
555
+
556
+ # ============================================================
557
+ # 8️⃣ Streamlit Master Orchestrator
558
+ # ============================================================
559
+ def run_pipeline(query, top_k=10):
560
+ st.markdown("## 🧩 Stage 1: Query Reformulation")
561
+ with st.spinner("Generating diverse reformulations..."):
562
+ q_data = agent_query_reformulator(query)
563
+ queries = [query] + [q["query"] for q in q_data["reformulated_queries"]]
564
+ for q in queries[1:]:
565
+ st.markdown(f"🔹 *{q}*")
566
+
567
+ st.markdown("## 🔍 Stage 2: Retrieval")
568
+ all_papers = []
569
+ progress = st.progress(0)
570
+ for i, q in enumerate(queries):
571
+ new_papers = agent_retriever(q, top_k)
572
+ all_papers.extend(new_papers)
573
+ progress.progress((i+1)/len(queries))
574
+ time.sleep(0.3)
575
+ progress.empty()
576
+ st.success(f"✅ Retrieved {len(all_papers)} papers in total.")
577
+
578
+ st.markdown("## 🧠 Stage 3: Semantic Reranking")
579
+ with st.spinner("Reranking papers semantically..."):
580
+ reranked = agent_reranker(query, all_papers)
581
+ st.info(f"Top paper after rerank: **{reranked[0]['title']}**")
582
+
583
+ st.markdown("## ⚖️ Stage 4: Weighting Agent & Meta-Scoring")
584
+ weights = agent_weighting(reranked)
585
+ st.json(weights)
586
+ meta_ranked = agent_meta_scorer(reranked, weights)
587
+
588
+ st.markdown("## 🔎 Stage 5: Critique Agent")
589
+ critique = agent_critique(meta_ranked, query)
590
+ st.info(f"**Critique:** {critique['critique']} | Relevance: {critique['relevance_score']:.2f}")
591
+
592
+ if critique["relevance_score"] < 0.6:
593
+ meta_ranked = human_feedback_loop(meta_ranked)
594
+
595
+ df = pd.DataFrame(meta_ranked)
596
+ st.download_button(
597
+ label="💾 Download Results as CSV",
598
+ data=df.to_csv(index=False),
599
+ file_name="final_ranked_papers.csv",
600
+ mime="text/csv"
601
+ )
602
+
603
+ st.dataframe(df.head(20))
604
+ st.success("🎯 Pipeline completed successfully!")
605
+
606
+
607
+ # ============================================================
608
+ # 9️⃣ Streamlit UI
609
+ # ============================================================
610
+ with st.form("research_form"):
611
+ query = st.text_input("Enter your research query:", "spatio-temporal action detection and localization")
612
+ top_k = st.slider("Number of papers per reformulation", 5, 50, 10)
613
+ run = st.form_submit_button("🚀 Run Multi-Agent Retrieval")
614
+
615
+ if run:
616
+ run_pipeline(query, top_k)