Tirath5504 commited on
Commit
8973e7a
Β·
verified Β·
1 Parent(s): 596aa39

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +71 -129
app.py CHANGED
@@ -6,6 +6,7 @@ from datetime import datetime
6
  import asyncio
7
  from functools import wraps
8
 
 
9
  from pipeline.critique_extraction import extract_critiques
10
  from pipeline.disagreement_detection import detect_disagreements
11
  from pipeline.search_retrieval import search_and_retrieve
@@ -17,7 +18,6 @@ from utils.validators import validate_paper_input
17
 
18
  from dotenv import load_dotenv
19
  load_dotenv()
20
- print(os.getenv("GEMINI_API_KEY"))
21
 
22
  # Initialize rate limiter and queue manager
23
  rate_limiter = RateLimiter(max_requests_per_minute=10)
@@ -43,15 +43,6 @@ async def full_pipeline(
43
  ) -> Dict:
44
  """
45
  Run the complete consensus analysis pipeline
46
-
47
- Args:
48
- paper_title: Title of the paper
49
- paper_abstract: Abstract of the paper
50
- reviews: List of review texts
51
- request_id: Optional request ID for progress tracking
52
-
53
- Returns:
54
- Complete pipeline results
55
  """
56
  if not request_id:
57
  request_id = f"req_{datetime.now().timestamp()}"
@@ -64,7 +55,7 @@ async def full_pipeline(
64
 
65
  try:
66
  # Stage 1: Critique Extraction
67
- update_progress(request_id, "critique_extraction", 0.1, "Extracting critique points...")
68
  critique_results = await extract_critiques(reviews)
69
  results["critique_points"] = critique_results
70
 
@@ -74,12 +65,12 @@ async def full_pipeline(
74
  results["disagreements"] = disagreement_results
75
 
76
  # Stage 3: Search & Retrieval
77
- update_progress(request_id, "search_retrieval", 0.5, "Searching for relevant research...")
78
  search_results = await search_and_retrieve(paper_title, paper_abstract, critique_results)
79
  results["search_results"] = search_results
80
 
81
  # Stage 4: Disagreement Resolution
82
- update_progress(request_id, "disagreement_resolution", 0.7, "Resolving disagreements...")
83
  resolution_results = await resolve_disagreements(
84
  paper_title,
85
  paper_abstract,
@@ -90,7 +81,7 @@ async def full_pipeline(
90
  results["resolution"] = resolution_results
91
 
92
  # Stage 5: Meta-Review Generation
93
- update_progress(request_id, "meta_review", 0.9, "Generating meta-review...")
94
  meta_review = await generate_meta_review(
95
  paper_title,
96
  paper_abstract,
@@ -103,73 +94,106 @@ async def full_pipeline(
103
  return results
104
 
105
  except Exception as e:
106
- update_progress(request_id, "error", 0.0, f"Error: {str(e)}")
107
- raise
 
 
 
 
108
 
109
- # Gradio Interface Functions
110
- def run_full_pipeline_ui(title: str, abstract: str, reviews_json: str) -> str:
111
- """UI wrapper for full pipeline"""
 
 
112
  try:
113
- # Validate and parse input
114
  reviews = json.loads(reviews_json)
115
  if not isinstance(reviews, list):
116
- return json.dumps({"error": "Reviews must be a list of strings"}, indent=2)
 
117
 
118
- # Check rate limit
119
  if not rate_limiter.allow_request():
120
- return json.dumps({"error": "Rate limit exceeded. Please try again later."}, indent=2)
 
121
 
122
- # Add to queue and run
123
  request_id = f"ui_{datetime.now().timestamp()}"
124
- result = asyncio.run(queue_manager.add_task(
125
- full_pipeline(title, abstract, reviews, request_id)
126
- ))
127
 
128
- return json.dumps(result, indent=2)
 
 
 
 
 
 
 
129
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
130
  except json.JSONDecodeError:
131
- return json.dumps({"error": "Invalid JSON format for reviews"}, indent=2)
132
  except Exception as e:
133
- return json.dumps({"error": str(e)}, indent=2)
134
 
135
- def run_critique_extraction_ui(reviews_json: str) -> str:
136
- """UI wrapper for critique extraction"""
137
  try:
138
  reviews = json.loads(reviews_json)
139
  if not rate_limiter.allow_request():
140
  return json.dumps({"error": "Rate limit exceeded"}, indent=2)
141
 
142
- result = asyncio.run(extract_critiques(reviews))
143
  return json.dumps(result, indent=2)
144
  except Exception as e:
145
  return json.dumps({"error": str(e)}, indent=2)
146
 
147
- def run_disagreement_detection_ui(critiques_json: str) -> str:
148
- """UI wrapper for disagreement detection"""
149
  try:
150
  critiques = json.loads(critiques_json)
151
  if not rate_limiter.allow_request():
152
  return json.dumps({"error": "Rate limit exceeded"}, indent=2)
153
 
154
- result = asyncio.run(detect_disagreements(critiques))
155
  return json.dumps(result, indent=2)
156
  except Exception as e:
157
  return json.dumps({"error": str(e)}, indent=2)
158
 
159
- def run_search_retrieval_ui(title: str, abstract: str, critiques_json: str) -> str:
160
- """UI wrapper for search retrieval"""
161
  try:
162
  critiques = json.loads(critiques_json)
163
  if not rate_limiter.allow_request():
164
  return json.dumps({"error": "Rate limit exceeded"}, indent=2)
165
 
166
- result = asyncio.run(search_and_retrieve(title, abstract, critiques))
167
  return json.dumps(result, indent=2)
168
  except Exception as e:
169
  return json.dumps({"error": str(e)}, indent=2)
170
 
171
  def check_progress_ui(request_id: str) -> str:
172
- """Check progress of a request"""
173
  if request_id in progress_store:
174
  return json.dumps(progress_store[request_id], indent=2)
175
  return json.dumps({"error": "Request ID not found"}, indent=2)
@@ -180,13 +204,6 @@ with gr.Blocks(title="Automated Consensus Analysis API", theme=gr.themes.Soft())
180
  # πŸ”¬ Automated Consensus Analysis API
181
 
182
  This API provides automated peer review consensus analysis using LLMs and search-augmented verification.
183
-
184
- ## Features:
185
- - **Critique Extraction**: Extract structured critique points from reviews
186
- - **Disagreement Detection**: Identify conflicts between reviewers
187
- - **Search Retrieval**: Find supporting/contradicting evidence
188
- - **Resolution**: Resolve disagreements with evidence
189
- - **Meta-Review**: Generate comprehensive meta-reviews
190
  """)
191
 
192
  with gr.Tabs():
@@ -215,11 +232,7 @@ with gr.Blocks(title="Automated Consensus Analysis API", theme=gr.themes.Soft())
215
  # Individual Stages
216
  with gr.Tab("πŸ” Critique Extraction"):
217
  gr.Markdown("### Extract critique points from reviews")
218
- critique_reviews = gr.Code(
219
- label="Reviews (JSON Array)",
220
- language="json",
221
- value='["Review 1...", "Review 2..."]'
222
- )
223
  critique_submit = gr.Button("Extract Critiques")
224
  critique_output = gr.Code(label="Extracted Critiques", language="json")
225
 
@@ -230,11 +243,8 @@ with gr.Blocks(title="Automated Consensus Analysis API", theme=gr.themes.Soft())
230
  )
231
 
232
  with gr.Tab("⚑ Disagreement Detection"):
233
- gr.Markdown("### Detect disagreements between reviews")
234
- disagree_critiques = gr.Code(
235
- label="Critique Points (JSON)",
236
- language="json"
237
- )
238
  disagree_submit = gr.Button("Detect Disagreements")
239
  disagree_output = gr.Code(label="Disagreement Analysis", language="json")
240
 
@@ -245,7 +255,7 @@ with gr.Blocks(title="Automated Consensus Analysis API", theme=gr.themes.Soft())
245
  )
246
 
247
  with gr.Tab("πŸ”Ž Search & Retrieval"):
248
- gr.Markdown("### Search for supporting evidence")
249
  with gr.Row():
250
  with gr.Column():
251
  search_title = gr.Textbox(label="Paper Title")
@@ -260,81 +270,13 @@ with gr.Blocks(title="Automated Consensus Analysis API", theme=gr.themes.Soft())
260
  inputs=[search_title, search_abstract, search_critiques],
261
  outputs=search_output
262
  )
263
-
264
- with gr.Tab("πŸ“Š Progress Tracking"):
265
- gr.Markdown("### Check pipeline progress")
266
- progress_id = gr.Textbox(label="Request ID", placeholder="Enter request ID...")
267
- progress_check = gr.Button("Check Progress")
268
- progress_output = gr.Code(label="Progress Status", language="json")
269
-
270
- progress_check.click(
271
- fn=check_progress_ui,
272
- inputs=progress_id,
273
- outputs=progress_output
274
- )
275
-
276
  with gr.Tab("πŸ“– API Documentation"):
277
- gr.Markdown("""
278
- ## API Endpoints
279
-
280
- ### POST /api/full_pipeline
281
- Run the complete consensus analysis pipeline.
282
-
283
- **Request Body:**
284
- ```json
285
- {
286
- "paper_title": "string",
287
- "paper_abstract": "string",
288
- "reviews": ["review1", "review2", ...]
289
- }
290
- ```
291
-
292
- ### POST /api/critique_extraction
293
- Extract critique points from reviews.
294
-
295
- **Request Body:**
296
- ```json
297
- {
298
- "reviews": ["review1", "review2", ...]
299
- }
300
- ```
301
-
302
- ### POST /api/disagreement_detection
303
- Detect disagreements in critique points.
304
-
305
- **Request Body:**
306
- ```json
307
- {
308
- "critiques": [{"Methodology": [...], ...}, ...]
309
- }
310
- ```
311
-
312
- ### POST /api/search_retrieval
313
- Search for supporting evidence.
314
-
315
- **Request Body:**
316
- ```json
317
- {
318
- "paper_title": "string",
319
- "paper_abstract": "string",
320
- "critiques": [...]
321
- }
322
- ```
323
-
324
- ### GET /api/progress/{request_id}
325
- Check progress of a pipeline execution.
326
-
327
- ## Rate Limits
328
- - 10 requests per minute per IP
329
- - Maximum 3 concurrent pipeline executions
330
-
331
- ## Authentication
332
- API keys are managed through HuggingFace Spaces secrets.
333
- """)
334
 
335
  # Launch the app
336
  if __name__ == "__main__":
337
- demo.queue(max_size=20) # Enable queuing
338
  demo.launch(
339
  server_name="0.0.0.0",
340
  server_port=7860,
 
6
  import asyncio
7
  from functools import wraps
8
 
9
+ # Keep your existing imports
10
  from pipeline.critique_extraction import extract_critiques
11
  from pipeline.disagreement_detection import detect_disagreements
12
  from pipeline.search_retrieval import search_and_retrieve
 
18
 
19
  from dotenv import load_dotenv
20
  load_dotenv()
 
21
 
22
  # Initialize rate limiter and queue manager
23
  rate_limiter = RateLimiter(max_requests_per_minute=10)
 
43
  ) -> Dict:
44
  """
45
  Run the complete consensus analysis pipeline
 
 
 
 
 
 
 
 
 
46
  """
47
  if not request_id:
48
  request_id = f"req_{datetime.now().timestamp()}"
 
55
 
56
  try:
57
  # Stage 1: Critique Extraction
58
+ update_progress(request_id, "critique_extraction", 0.1, "Extracting critique points (Gemini)...")
59
  critique_results = await extract_critiques(reviews)
60
  results["critique_points"] = critique_results
61
 
 
65
  results["disagreements"] = disagreement_results
66
 
67
  # Stage 3: Search & Retrieval
68
+ update_progress(request_id, "search_retrieval", 0.5, "Searching arXiv/Scholar for evidence...")
69
  search_results = await search_and_retrieve(paper_title, paper_abstract, critique_results)
70
  results["search_results"] = search_results
71
 
72
  # Stage 4: Disagreement Resolution
73
+ update_progress(request_id, "disagreement_resolution", 0.7, "Reasoning through disagreements (DeepSeek)...")
74
  resolution_results = await resolve_disagreements(
75
  paper_title,
76
  paper_abstract,
 
81
  results["resolution"] = resolution_results
82
 
83
  # Stage 5: Meta-Review Generation
84
+ update_progress(request_id, "meta_review", 0.9, "Writing final Meta-Review (DeepSeek)...")
85
  meta_review = await generate_meta_review(
86
  paper_title,
87
  paper_abstract,
 
94
  return results
95
 
96
  except Exception as e:
97
+ error_msg = f"Error: {str(e)}"
98
+ print(error_msg)
99
+ update_progress(request_id, "error", 0.0, error_msg)
100
+ raise e
101
+
102
+ # --- UI Functions (Converted to Async + Generator for Keep-Alive) ---
103
 
104
+ async def run_full_pipeline_ui(title: str, abstract: str, reviews_json: str):
105
+ """
106
+ UI wrapper for full pipeline with STREAMING updates.
107
+ This prevents the 'Connection Timeout' on HF Spaces.
108
+ """
109
  try:
110
+ # Validate input
111
  reviews = json.loads(reviews_json)
112
  if not isinstance(reviews, list):
113
+ yield json.dumps({"error": "Reviews must be a list of strings"}, indent=2)
114
+ return
115
 
 
116
  if not rate_limiter.allow_request():
117
+ yield json.dumps({"error": "Rate limit exceeded. Please try again later."}, indent=2)
118
+ return
119
 
 
120
  request_id = f"ui_{datetime.now().timestamp()}"
 
 
 
121
 
122
+ # Initialize progress for this ID
123
+ update_progress(request_id, "queued", 0.0, "Request queued...")
124
+
125
+ # Create the task (wrapped in queue manager)
126
+ # We assume queue_manager.add_task is an async function
127
+ pipeline_task = asyncio.create_task(
128
+ queue_manager.add_task(full_pipeline(title, abstract, reviews, request_id))
129
+ )
130
 
131
+ # Loop while the task is running to keep the connection alive
132
+ while not pipeline_task.done():
133
+ # fetch current status
134
+ current_status = progress_store.get(request_id, {})
135
+
136
+ # Create a temporary status object to show in the UI
137
+ status_display = {
138
+ "status": "processing",
139
+ "current_stage": current_status.get("stage", "initializing"),
140
+ "message": current_status.get("message", "Waiting..."),
141
+ "progress": current_status.get("progress", 0)
142
+ }
143
+
144
+ # Yield the status (Keep-Alive!)
145
+ yield json.dumps(status_display, indent=2)
146
+
147
+ # Wait 1 second before next update
148
+ await asyncio.sleep(1)
149
+
150
+ # Get final result or exception
151
+ try:
152
+ result = await pipeline_task
153
+ yield json.dumps(result, indent=2)
154
+ except Exception as e:
155
+ yield json.dumps({"error": str(e), "last_stage_log": progress_store.get(request_id)}, indent=2)
156
+
157
  except json.JSONDecodeError:
158
+ yield json.dumps({"error": "Invalid JSON format for reviews"}, indent=2)
159
  except Exception as e:
160
+ yield json.dumps({"error": f"Unexpected error: {str(e)}"}, indent=2)
161
 
162
+ # Wrapper for smaller tasks (Convert to async, remove asyncio.run)
163
+ async def run_critique_extraction_ui(reviews_json: str) -> str:
164
  try:
165
  reviews = json.loads(reviews_json)
166
  if not rate_limiter.allow_request():
167
  return json.dumps({"error": "Rate limit exceeded"}, indent=2)
168
 
169
+ result = await extract_critiques(reviews)
170
  return json.dumps(result, indent=2)
171
  except Exception as e:
172
  return json.dumps({"error": str(e)}, indent=2)
173
 
174
+ async def run_disagreement_detection_ui(critiques_json: str) -> str:
 
175
  try:
176
  critiques = json.loads(critiques_json)
177
  if not rate_limiter.allow_request():
178
  return json.dumps({"error": "Rate limit exceeded"}, indent=2)
179
 
180
+ result = await detect_disagreements(critiques)
181
  return json.dumps(result, indent=2)
182
  except Exception as e:
183
  return json.dumps({"error": str(e)}, indent=2)
184
 
185
+ async def run_search_retrieval_ui(title: str, abstract: str, critiques_json: str) -> str:
 
186
  try:
187
  critiques = json.loads(critiques_json)
188
  if not rate_limiter.allow_request():
189
  return json.dumps({"error": "Rate limit exceeded"}, indent=2)
190
 
191
+ result = await search_and_retrieve(title, abstract, critiques)
192
  return json.dumps(result, indent=2)
193
  except Exception as e:
194
  return json.dumps({"error": str(e)}, indent=2)
195
 
196
  def check_progress_ui(request_id: str) -> str:
 
197
  if request_id in progress_store:
198
  return json.dumps(progress_store[request_id], indent=2)
199
  return json.dumps({"error": "Request ID not found"}, indent=2)
 
204
  # πŸ”¬ Automated Consensus Analysis API
205
 
206
  This API provides automated peer review consensus analysis using LLMs and search-augmented verification.
 
 
 
 
 
 
 
207
  """)
208
 
209
  with gr.Tabs():
 
232
  # Individual Stages
233
  with gr.Tab("πŸ” Critique Extraction"):
234
  gr.Markdown("### Extract critique points from reviews")
235
+ critique_reviews = gr.Code(label="Reviews (JSON Array)", language="json")
 
 
 
 
236
  critique_submit = gr.Button("Extract Critiques")
237
  critique_output = gr.Code(label="Extracted Critiques", language="json")
238
 
 
243
  )
244
 
245
  with gr.Tab("⚑ Disagreement Detection"):
246
+ gr.Markdown("### Detect disagreements")
247
+ disagree_critiques = gr.Code(label="Critique Points (JSON)", language="json")
 
 
 
248
  disagree_submit = gr.Button("Detect Disagreements")
249
  disagree_output = gr.Code(label="Disagreement Analysis", language="json")
250
 
 
255
  )
256
 
257
  with gr.Tab("πŸ”Ž Search & Retrieval"):
258
+ gr.Markdown("### Search for evidence")
259
  with gr.Row():
260
  with gr.Column():
261
  search_title = gr.Textbox(label="Paper Title")
 
270
  inputs=[search_title, search_abstract, search_critiques],
271
  outputs=search_output
272
  )
273
+
 
 
 
 
 
 
 
 
 
 
 
 
274
  with gr.Tab("πŸ“– API Documentation"):
275
+ gr.Markdown("## API Documentation...")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
276
 
277
  # Launch the app
278
  if __name__ == "__main__":
279
+ demo.queue(max_size=20)
280
  demo.launch(
281
  server_name="0.0.0.0",
282
  server_port=7860,