Reality123b commited on
Commit
9b0505d
·
verified ·
1 Parent(s): 993882c

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +750 -46
app.py CHANGED
@@ -1,64 +1,768 @@
1
  import gradio as gr
2
- from huggingface_hub import InferenceClient
 
 
 
 
 
 
 
 
 
 
 
 
 
3
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4
  """
5
- For more information on `huggingface_hub` Inference API support, please check the docs: https://huggingface.co/docs/huggingface_hub/v0.22.2/en/guides/inference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6
  """
7
- client = InferenceClient("HuggingFaceH4/zephyr-7b-beta")
8
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9
 
10
- def respond(
11
- message,
12
- history: list[tuple[str, str]],
13
- system_message,
14
- max_tokens,
15
- temperature,
16
- top_p,
17
- ):
18
- messages = [{"role": "system", "content": system_message}]
19
 
20
- for val in history:
21
- if val[0]:
22
- messages.append({"role": "user", "content": val[0]})
23
- if val[1]:
24
- messages.append({"role": "assistant", "content": val[1]})
25
 
26
- messages.append({"role": "user", "content": message})
 
 
 
 
 
27
 
28
- response = ""
 
 
 
29
 
30
- for message in client.chat_completion(
31
- messages,
32
- max_tokens=max_tokens,
33
- stream=True,
34
- temperature=temperature,
35
- top_p=top_p,
36
- ):
37
- token = message.choices[0].delta.content
38
 
39
- response += token
40
- yield response
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
41
 
 
 
 
 
 
 
 
 
 
 
 
 
42
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
43
  """
44
- For information on how to customize the ChatInterface, peruse the gradio docs: https://www.gradio.app/docs/chatinterface
45
- """
46
- demo = gr.ChatInterface(
47
- respond,
48
- additional_inputs=[
49
- gr.Textbox(value="You are a friendly Chatbot.", label="System message"),
50
- gr.Slider(minimum=1, maximum=2048, value=512, step=1, label="Max new tokens"),
51
- gr.Slider(minimum=0.1, maximum=4.0, value=0.7, step=0.1, label="Temperature"),
52
- gr.Slider(
53
- minimum=0.1,
54
- maximum=1.0,
55
- value=0.95,
56
- step=0.05,
57
- label="Top-p (nucleus sampling)",
58
- ),
59
  ],
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
60
  )
61
 
 
 
 
 
 
 
 
62
 
63
- if __name__ == "__main__":
64
- demo.launch()
 
1
  import gradio as gr
2
+ import requests
3
+ import os
4
+ import time
5
+ import json
6
+ import re
7
+ from uuid import uuid4
8
+ from datetime import datetime
9
+ from duckduckgo_search import ddg
10
+ from sentence_transformers import SentenceTransformer, util
11
+ from typing import List, Dict, Any, Optional, Union, Tuple
12
+ import logging
13
+ import pandas as pd
14
+ import numpy as np
15
+ from collections import deque
16
 
17
+ # Set up logging
18
+ logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
19
+ logger = logging.getLogger(__name__)
20
+
21
+ # Configuration
22
+ HF_API_KEY = os.environ.get("HF_API_KEY")
23
+ if not HF_API_KEY:
24
+ raise ValueError("Please set the HF_API_KEY environment variable.")
25
+
26
+ # You can use different models for different tasks
27
+ MAIN_LLM_ENDPOINT = "your-main-llm-endpoint"
28
+ REASONING_LLM_ENDPOINT = "your-reasoning-llm-endpoint" # Can be the same as main if needed
29
+ CRITIC_LLM_ENDPOINT = "your-critic-llm-endpoint" # Can be the same as main if needed
30
+
31
+ MAX_ITERATIONS = 12 # Increased from 7
32
+ TIMEOUT = 60
33
+ RETRY_DELAY = 5
34
+ NUM_RESULTS = 10 # Increased from 7
35
+ SIMILARITY_THRESHOLD = 0.15 # Lowered from 0.2 to get more potentially relevant results
36
+ MAX_CONTEXT_ITEMS = 20 # Prevent context from growing too large
37
+ MAX_HISTORY_ITEMS = 5 # For keeping track of previous queries/reasoning
38
+
39
+ # Load multiple embedding models for different purposes
40
+ try:
41
+ main_similarity_model = SentenceTransformer('all-mpnet-base-v2')
42
+ concept_similarity_model = SentenceTransformer('sentence-transformers/all-MiniLM-L6-v2') # Faster, lighter model for concept matching
43
+ except Exception as e:
44
+ logger.error(f"Failed to load SentenceTransformer models: {e}")
45
+ main_similarity_model = None
46
+ concept_similarity_model = None
47
+
48
+ def hf_inference(endpoint, inputs, parameters=None, retries=5):
49
+ headers = {"Authorization": f"Bearer {HF_API_KEY}"}
50
+ payload = {"inputs": inputs, "parameters": parameters or {}}
51
+
52
+ for attempt in range(retries):
53
+ try:
54
+ response = requests.post(endpoint, headers=headers, json=payload, timeout=TIMEOUT)
55
+ response.raise_for_status()
56
+ return response.json()
57
+ except requests.exceptions.RequestException as e:
58
+ if attempt == retries - 1:
59
+ logger.error(f"Request failed after {retries} retries: {e}")
60
+ return {"error": f"Request failed after {retries} retries: {e}"}
61
+ time.sleep(RETRY_DELAY * (1 + attempt)) # Exponential backoff
62
+ return {"error": "Request failed after multiple retries."}
63
+
64
+ def tool_search_web(query: str, num_results: int = NUM_RESULTS, safesearch: str = "moderate",
65
+ time_filter: str = "", region: str = "wt-wt", language: str = "en-us") -> list:
66
+ try:
67
+ results = ddg(query, max_results=num_results, safesearch=safesearch,
68
+ time=time_filter, region=region, language=language)
69
+ if results:
70
+ return [{"title": r["title"], "snippet": r["snippet"], "url": r["href"]} for r in results]
71
+ else:
72
+ return []
73
+ except Exception as e:
74
+ logger.error(f"DuckDuckGo search error: {e}")
75
+ return []
76
+
77
+ def tool_reason(prompt: str, search_results: list, reasoning_context: list = [],
78
+ critique: str = "", focus_areas: list = []) -> str:
79
+ if not search_results:
80
+ return "No search results to reason about."
81
+
82
+ reasoning_input = "Reason about the following search results in relation to the prompt:\n\n"
83
+ reasoning_input += f"Prompt: {prompt}\n\n"
84
+
85
+ if focus_areas:
86
+ reasoning_input += f"Focus particularly on these aspects: {', '.join(focus_areas)}\n\n"
87
+
88
+ for i, result in enumerate(search_results):
89
+ reasoning_input += f"- Result {i + 1}: Title: {result['title']}, Snippet: {result['snippet']}\n"
90
+
91
+ if reasoning_context:
92
+ recent_context = reasoning_context[-MAX_HISTORY_ITEMS:]
93
+ reasoning_input += "\nPrevious Reasoning Context:\n" + "\n".join(recent_context)
94
+
95
+ if critique:
96
+ reasoning_input += f"\n\nRecent critique to address: {critique}\n"
97
+
98
+ reasoning_input += "\nProvide a thorough, nuanced analysis that builds upon previous reasoning if applicable. Consider multiple perspectives and potential contradictions in the search results."
99
+
100
+ reasoning_output = hf_inference(REASONING_LLM_ENDPOINT, reasoning_input)
101
+
102
+ if isinstance(reasoning_output, dict) and "generated_text" in reasoning_output:
103
+ return reasoning_output["generated_text"].strip()
104
+ else:
105
+ logger.error(f"Failed to generate reasoning: {reasoning_output}")
106
+ return "Could not generate reasoning due to an error."
107
+
108
+ def tool_summarize(insights: list, prompt: str, contradictions: list = []) -> str:
109
+ if not insights:
110
+ return "No insights to summarize."
111
+
112
+ summarization_input = f"Synthesize the following insights into a cohesive and comprehensive summary regarding: '{prompt}'\n\n"
113
+ summarization_input += "\n\n".join(insights[-MAX_HISTORY_ITEMS:]) # Only use most recent insights
114
+
115
+ if contradictions:
116
+ summarization_input += "\n\nAddress these specific contradictions:\n" + "\n".join(contradictions)
117
+
118
+ summarization_input += "\n\nProvide a well-structured summary that:\n1. Presents the main findings\n2. Acknowledges limitations and uncertainties\n3. Highlights areas of consensus and disagreement\n4. Suggests potential directions for further inquiry"
119
+
120
+ summarization_output = hf_inference(MAIN_LLM_ENDPOINT, summarization_input)
121
+
122
+ if isinstance(summarization_output, dict) and "generated_text" in summarization_output:
123
+ return summarization_output["generated_text"].strip()
124
+ else:
125
+ logger.error(f"Failed to generate summary: {summarization_output}")
126
+ return "Could not generate a summary due to an error."
127
+
128
+ def tool_generate_search_query(prompt: str, previous_queries: list = [],
129
+ failed_queries: list = [], focus_areas: list = []) -> str:
130
+ query_gen_input = f"Generate an effective search query for the following prompt: {prompt}\n"
131
+
132
+ if previous_queries:
133
+ recent_queries = previous_queries[-MAX_HISTORY_ITEMS:]
134
+ query_gen_input += "Previous search queries:\n" + "\n".join(recent_queries) + "\n"
135
+
136
+ if failed_queries:
137
+ query_gen_input += "These queries didn't yield useful results:\n" + "\n".join(failed_queries) + "\n"
138
+
139
+ if focus_areas:
140
+ query_gen_input += f"Focus particularly on these aspects: {', '.join(focus_areas)}\n"
141
+
142
+ query_gen_input += "Refine the search query based on previous queries, aiming for more precise results.\n"
143
+ query_gen_input += "Search Query:"
144
+
145
+ query_gen_output = hf_inference(MAIN_LLM_ENDPOINT, query_gen_input)
146
+
147
+ if isinstance(query_gen_output, dict) and 'generated_text' in query_gen_output:
148
+ return query_gen_output['generated_text'].strip()
149
+
150
+ logger.error(f"Failed to generate search query: {query_gen_output}")
151
+ return ""
152
+
153
+ def tool_critique_reasoning(reasoning_output: str, prompt: str,
154
+ previous_critiques: list = []) -> str:
155
+ critique_input = f"Critically evaluate the following reasoning output in relation to the prompt:\n\nPrompt: {prompt}\n\nReasoning: {reasoning_output}\n\n"
156
+
157
+ if previous_critiques:
158
+ critique_input += "Previous critiques that should be addressed:\n" + "\n".join(previous_critiques[-MAX_HISTORY_ITEMS:]) + "\n\n"
159
+
160
+ critique_input += "Identify any flaws, biases, logical fallacies, unsupported claims, or areas for improvement. Be specific and constructive. Suggest concrete ways to enhance the reasoning."
161
+
162
+ critique_output = hf_inference(CRITIC_LLM_ENDPOINT, critique_input)
163
+
164
+ if isinstance(critique_output, dict) and "generated_text" in critique_output:
165
+ return critique_output["generated_text"].strip()
166
+
167
+ logger.error(f"Failed to generate critique: {critique_output}")
168
+ return "Could not generate a critique due to an error."
169
+
170
+ def tool_identify_contradictions(insights: list) -> list:
171
+ if len(insights) < 2:
172
+ return []
173
+
174
+ contradiction_input = "Identify specific contradictions in these insights:\n\n" + "\n\n".join(insights[-MAX_HISTORY_ITEMS:])
175
+ contradiction_input += "\n\nList each contradiction as a separate numbered point. If no contradictions exist, respond with 'No contradictions found.'"
176
+
177
+ contradiction_output = hf_inference(CRITIC_LLM_ENDPOINT, contradiction_input)
178
+
179
+ if isinstance(contradiction_output, dict) and "generated_text" in contradiction_output:
180
+ result = contradiction_output["generated_text"].strip()
181
+ if result == "No contradictions found.":
182
+ return []
183
+
184
+ # Extract numbered contradictions
185
+ contradictions = re.findall(r'\d+\.\s+(.*?)(?=\d+\.|$)', result, re.DOTALL)
186
+ return [c.strip() for c in contradictions if c.strip()]
187
+
188
+ logger.error(f"Failed to identify contradictions: {contradiction_output}")
189
+ return []
190
+
191
+ def tool_identify_focus_areas(prompt: str, insights: list = [],
192
+ failed_areas: list = []) -> list:
193
+ focus_input = f"Based on this research prompt: '{prompt}'\n\n"
194
+
195
+ if insights:
196
+ focus_input += "And these existing insights:\n" + "\n".join(insights[-3:]) + "\n\n" # Last 3 insights
197
+
198
+ if failed_areas:
199
+ focus_input += f"These focus areas didn't yield useful results: {', '.join(failed_areas)}\n\n"
200
+
201
+ focus_input += "Identify 2-3 specific aspects that should be investigated further to get a complete understanding. Be precise and prioritize underexplored areas."
202
+
203
+ focus_output = hf_inference(MAIN_LLM_ENDPOINT, focus_input)
204
+
205
+ if isinstance(focus_output, dict) and "generated_text" in focus_output:
206
+ result = focus_output["generated_text"].strip()
207
+ # Extract areas, assuming they're listed with numbers, bullets, or in separate lines
208
+ areas = re.findall(r'(?:^|\n)(?:\d+\.|\*|\-)\s*(.*?)(?=(?:\n(?:\d+\.|\*|\-|$))|$)', result)
209
+ return [area.strip() for area in areas if area.strip()][:3] # Limit to top 3
210
+
211
+ logger.error(f"Failed to identify focus areas: {focus_output}")
212
+ return []
213
+
214
+ def filter_results(search_results, prompt, previous_snippets=None):
215
+ if not main_similarity_model or not search_results:
216
+ return search_results
217
+
218
+ try:
219
+ prompt_embedding = main_similarity_model.encode(prompt, convert_to_tensor=True)
220
+ filtered_results = []
221
+
222
+ # Keep track of snippets we've already seen
223
+ seen_snippets = set()
224
+ if previous_snippets:
225
+ seen_snippets.update(previous_snippets)
226
+
227
+ for result in search_results:
228
+ combined_text = result['title'] + " " + result['snippet']
229
+
230
+ # Skip if we've seen this exact snippet before
231
+ if result['snippet'] in seen_snippets:
232
+ continue
233
+
234
+ result_embedding = main_similarity_model.encode(combined_text, convert_to_tensor=True)
235
+ cosine_score = util.pytorch_cos_sim(prompt_embedding, result_embedding)[0][0].item()
236
+
237
+ if cosine_score >= SIMILARITY_THRESHOLD:
238
+ result['relevance_score'] = cosine_score
239
+ filtered_results.append(result)
240
+ seen_snippets.add(result['snippet'])
241
+
242
+ # Sort by relevance score
243
+ filtered_results.sort(key=lambda x: x.get('relevance_score', 0), reverse=True)
244
+ return filtered_results
245
+
246
+ except Exception as e:
247
+ logger.error(f"Error during filtering: {e}")
248
+ return search_results
249
+
250
+ # New tool: Extract entities for focused research
251
+ def tool_extract_key_entities(prompt: str) -> list:
252
+ entity_input = f"Extract the key entities (people, organizations, concepts, technologies, etc.) from this research prompt that should be investigated individually:\n\n{prompt}\n\nList only the most important 3-5 entities, one per line."
253
+
254
+ entity_output = hf_inference(MAIN_LLM_ENDPOINT, entity_input)
255
+
256
+ if isinstance(entity_output, dict) and "generated_text" in entity_output:
257
+ result = entity_output["generated_text"].strip()
258
+ # Split by lines and clean up
259
+ entities = [e.strip() for e in result.split('\n') if e.strip()]
260
+ return entities[:5] # Limit to 5 entities
261
+
262
+ logger.error(f"Failed to extract key entities: {entity_output}")
263
+ return []
264
+
265
+ # New tool: Meta-analyze across entities
266
+ def tool_meta_analyze(entity_insights: Dict[str, list], prompt: str) -> str:
267
+ if not entity_insights:
268
+ return "No entity insights to analyze."
269
+
270
+ meta_input = f"Perform a meta-analysis across these different entities related to the prompt: '{prompt}'\n\n"
271
+
272
+ for entity, insights in entity_insights.items():
273
+ if insights:
274
+ meta_input += f"\n--- {entity} ---\n" + insights[-1] + "\n" # Just use the latest insight for each entity
275
+
276
+ meta_input += "\nProvide a high-level synthesis that identifies:\n1. Common themes across entities\n2. Important differences\n3. How these entities interact or influence each other\n4. The broader implications for the original research question"
277
+
278
+ meta_output = hf_inference(MAIN_LLM_ENDPOINT, meta_input)
279
+
280
+ if isinstance(meta_output, dict) and "generated_text" in meta_output:
281
+ return meta_output["generated_text"].strip()
282
+
283
+ logger.error(f"Failed to perform meta-analysis: {meta_output}")
284
+ return "Could not generate a meta-analysis due to an error."
285
+
286
+ # Update tools dictionary with enhanced functionality
287
+ tools = {
288
+ "search_web": {
289
+ "function": tool_search_web,
290
+ "description": "Searches the web for information.",
291
+ "parameters": {
292
+ "query": {"type": "string", "description": "The search query."},
293
+ "num_results": {"type": "integer", "description": "Number of results to return."},
294
+ "time_filter": {"type": "string", "description": "Optional time filter (d, w, m, y)."},
295
+ "region": {"type": "string", "description": "Optional region code."},
296
+ "language": {"type": "string", "description": "Optional language code."}
297
+ },
298
+ },
299
+ "reason": {
300
+ "function": tool_reason,
301
+ "description": "Analyzes and reasons about information.",
302
+ "parameters": {
303
+ "prompt": {"type": "string", "description": "The original prompt."},
304
+ "search_results": {"type": "array", "description": "Search results to analyze."},
305
+ "reasoning_context": {"type": "array", "description": "Previous reasoning outputs."},
306
+ "critique": {"type": "string", "description": "Recent critique to address."},
307
+ "focus_areas": {"type": "array", "description": "Specific aspects to focus on."}
308
+ },
309
+ },
310
+ "summarize": {
311
+ "function": tool_summarize,
312
+ "description": "Synthesizes insights into a cohesive summary.",
313
+ "parameters": {
314
+ "insights": {"type": "array", "description": "Insights to summarize."},
315
+ "prompt": {"type": "string", "description": "The original research prompt."},
316
+ "contradictions": {"type": "array", "description": "Specific contradictions to address."}
317
+ },
318
+ },
319
+ "generate_search_query": {
320
+ "function": tool_generate_search_query,
321
+ "description": "Generates an optimized search query",
322
+ "parameters":{
323
+ "prompt": {"type": "string", "description": "The original user prompt."},
324
+ "previous_queries": {"type": "array", "description": "Previously used search queries."},
325
+ "failed_queries": {"type": "array", "description": "Queries that didn't yield good results."},
326
+ "focus_areas": {"type": "array", "description": "Specific aspects to focus on."}
327
+ }
328
+ },
329
+ "critique_reasoning": {
330
+ "function": tool_critique_reasoning,
331
+ "description": "Critically evaluates reasoning output.",
332
+ "parameters": {
333
+ "reasoning_output": {"type": "string", "description": "The reasoning output to critique."},
334
+ "prompt": {"type": "string", "description": "The original prompt."},
335
+ "previous_critiques": {"type": "array", "description": "Previous critique outputs."}
336
+ },
337
+ },
338
+ "identify_contradictions": {
339
+ "function": tool_identify_contradictions,
340
+ "description": "Identifies contradictions across multiple insights.",
341
+ "parameters": {
342
+ "insights": {"type": "array", "description": "Collection of insights to analyze for contradictions."},
343
+ },
344
+ },
345
+ "identify_focus_areas": {
346
+ "function": tool_identify_focus_areas,
347
+ "description": "Identifies specific aspects that need further investigation.",
348
+ "parameters": {
349
+ "prompt": {"type": "string", "description": "The original research prompt."},
350
+ "insights": {"type": "array", "description": "Existing insights to build upon."},
351
+ "failed_areas": {"type": "array", "description": "Previously tried areas that yielded poor results."}
352
+ },
353
+ },
354
+ "extract_key_entities": {
355
+ "function": tool_extract_key_entities,
356
+ "description": "Extracts key entities from the prompt for focused research.",
357
+ "parameters": {
358
+ "prompt": {"type": "string", "description": "The original research prompt."}
359
+ },
360
+ },
361
+ "meta_analyze": {
362
+ "function": tool_meta_analyze,
363
+ "description": "Performs meta-analysis across entity-specific insights.",
364
+ "parameters": {
365
+ "entity_insights": {"type": "object", "description": "Dictionary mapping entities to their insights."},
366
+ "prompt": {"type": "string", "description": "The original research prompt."}
367
+ },
368
+ }
369
+ }
370
+
371
+ def create_prompt(task_description, user_input, available_tools, context):
372
+ prompt = f"""{task_description}
373
+
374
+ User Input:
375
+ {user_input}
376
+
377
+ Available Tools:
378
  """
379
+ for tool_name, tool_data in available_tools.items():
380
+ prompt += f"- {tool_name}: {tool_data['description']}\n"
381
+ prompt += " Parameters:\n"
382
+ for param_name, param_data in tool_data["parameters"].items():
383
+ prompt += f" - {param_name} ({param_data['type']}): {param_data['description']}\n"
384
+
385
+ # Only include most recent context items to avoid exceeding context limits
386
+ recent_context = context[-MAX_CONTEXT_ITEMS:] if len(context) > MAX_CONTEXT_ITEMS else context
387
+
388
+ prompt += "\nContext (most recent items):\n"
389
+ for item in recent_context:
390
+ prompt += f"- {item}\n"
391
+
392
+ prompt += """
393
+ Instructions:
394
+ Select the BEST tool and parameters for the current research stage. Output valid JSON. If no tool is appropriate, respond with {}.
395
+ Only use provided tools. Be strategic about which tool to use next based on the research progress so far.
396
+
397
+ Example:
398
+ {"tool": "search_web", "parameters": {"query": "Eiffel Tower location"}}
399
+
400
+ Output:
401
  """
402
+ return prompt
403
 
404
+ def deep_research(prompt):
405
+ task_description = "You are an advanced research assistant that can perform deep, multi-stage analysis. Use available tools iteratively, focus on different aspects, follow promising leads, and critically evaluate your findings."
406
+ context = []
407
+ all_insights = []
408
+ entity_specific_insights = {}
409
+ intermediate_output = ""
410
+ previous_queries = []
411
+ failed_queries = []
412
+ reasoning_context = []
413
+ previous_critiques = []
414
+ focus_areas = []
415
+ failed_areas = []
416
+ seen_snippets = set()
417
+ contradictions = []
418
+ research_session_id = str(uuid4())
419
+
420
+ # Start with entity extraction for multi-pronged research
421
+ key_entities = tool_extract_key_entities(prompt=prompt)
422
+ if key_entities:
423
+ context.append(f"Identified key entities: {key_entities}")
424
+ intermediate_output += f"Identified key entities for focused research: {key_entities}\n"
425
+
426
+ # Tracking progress for each entity
427
+ entity_progress = {entity: {'queries': [], 'insights': []} for entity in key_entities}
428
+ entity_progress['general'] = {'queries': [], 'insights': []} # For general research not tied to specific entities
429
+
430
+ for i in range(MAX_ITERATIONS):
431
+ # Decide which entity to focus on this iteration, or general research
432
+ if key_entities and i > 0:
433
+ # Simple round-robin for entities, with general research every few iterations
434
+ entities_to_process = key_entities + ['general']
435
+ current_entity = entities_to_process[i % len(entities_to_process)]
436
+ else:
437
+ current_entity = 'general'
438
+
439
+ context.append(f"Current focus: {current_entity}")
440
+
441
+ # First iteration: general query and initial research
442
+ if i == 0:
443
+ initial_query = tool_generate_search_query(prompt=prompt)
444
+ if initial_query:
445
+ previous_queries.append(initial_query)
446
+ entity_progress['general']['queries'].append(initial_query)
447
+ search_results = tool_search_web(query=initial_query)
448
+ filtered_search_results = filter_results(search_results, prompt)
449
+
450
+ for result in filtered_search_results:
451
+ seen_snippets.add(result['snippet'])
452
+
453
+ if filtered_search_results:
454
+ context.append(f"Initial Search Results: {len(filtered_search_results)} items found")
455
+ reasoning_output = tool_reason(prompt, filtered_search_results)
456
+ if reasoning_output:
457
+ all_insights.append(reasoning_output)
458
+ entity_progress['general']['insights'].append(reasoning_output)
459
+ reasoning_context.append(reasoning_output)
460
+ context.append(f"Initial Reasoning: {reasoning_output[:200]}...")
461
+ else:
462
+ failed_queries.append(initial_query)
463
+ context.append(f"Initial query yielded no relevant results: {initial_query}")
464
+
465
+ # Generate current entity-specific query if applicable
466
+ elif current_entity != 'general':
467
+ entity_query = tool_generate_search_query(
468
+ prompt=f"{prompt} focusing specifically on {current_entity}",
469
+ previous_queries=entity_progress[current_entity]['queries'],
470
+ focus_areas=focus_areas
471
+ )
472
+
473
+ if entity_query:
474
+ previous_queries.append(entity_query)
475
+ entity_progress[current_entity]['queries'].append(entity_query)
476
+
477
+ # Search with entity focus
478
+ search_results = tool_search_web(query=entity_query)
479
+ filtered_search_results = filter_results(search_results,
480
+ f"{prompt} {current_entity}",
481
+ previous_snippets=seen_snippets)
482
+
483
+ # Update seen snippets
484
+ for result in filtered_search_results:
485
+ seen_snippets.add(result['snippet'])
486
+
487
+ if filtered_search_results:
488
+ context.append(f"Entity Search for {current_entity}: {len(filtered_search_results)} results")
489
+
490
+ # Get entity-specific reasoning
491
+ entity_reasoning = tool_reason(
492
+ prompt=f"{prompt} focusing on {current_entity}",
493
+ search_results=filtered_search_results,
494
+ reasoning_context=entity_progress[current_entity]['insights'],
495
+ focus_areas=focus_areas
496
+ )
497
+
498
+ if entity_reasoning:
499
+ all_insights.append(entity_reasoning)
500
+ entity_progress[current_entity]['insights'].append(entity_reasoning)
501
+
502
+ # Store in entity-specific insights dictionary for meta-analysis
503
+ if current_entity not in entity_specific_insights:
504
+ entity_specific_insights[current_entity] = []
505
+ entity_specific_insights[current_entity].append(entity_reasoning)
506
+
507
+ context.append(f"Reasoning about {current_entity}: {entity_reasoning[:200]}...")
508
+ else:
509
+ failed_queries.append(entity_query)
510
+ context.append(f"Entity query for {current_entity} yielded no relevant results")
511
+
512
+ # Generate LLM decision for next tool
513
+ llm_prompt = create_prompt(task_description, prompt, tools, context)
514
+ llm_response = hf_inference(MAIN_LLM_ENDPOINT, llm_prompt)
515
+
516
+ if isinstance(llm_response, dict) and "error" in llm_response:
517
+ intermediate_output += f"LLM Error: {llm_response['error']}\n"
518
+ continue
519
+
520
+ if not isinstance(llm_response, dict) or "generated_text" not in llm_response:
521
+ intermediate_output += "Error: Invalid LLM response.\n"
522
+ continue
523
 
524
+ try:
525
+ response_text = llm_response["generated_text"].strip()
526
+ response_json = json.loads(response_text)
527
+ intermediate_output += f"Iteration {i+1} - Focus: {current_entity} - Action: {response_text}\n"
528
+ except json.JSONDecodeError:
529
+ intermediate_output += f"Iteration {i+1} - LLM Response (Invalid JSON): {llm_response['generated_text'][:100]}...\n"
530
+ context.append(f"Invalid JSON: {llm_response['generated_text'][:100]}...")
531
+ continue
 
532
 
533
+ tool_name = response_json.get("tool")
534
+ parameters = response_json.get("parameters", {})
 
 
 
535
 
536
+ if not tool_name:
537
+ if all_insights:
538
+ # If we have insights but no tool selected, maybe we're done
539
+ if i > MAX_ITERATIONS // 2: # Only consider ending early after half the iterations
540
+ break
541
+ continue
542
 
543
+ if tool_name not in tools:
544
+ context.append(f"Invalid tool: {tool_name}")
545
+ intermediate_output += f"Iteration {i + 1} - Invalid tool chosen: {tool_name}\n"
546
+ continue
547
 
548
+ tool = tools[tool_name]
549
+ try:
550
+ intermediate_output += f"Iteration {i+1} - Executing: {tool_name}, Key params: {str(parameters)[:100]}...\n"
 
 
 
 
 
551
 
552
+ if tool_name == "generate_search_query":
553
+ parameters['previous_queries'] = previous_queries
554
+ parameters['failed_queries'] = failed_queries
555
+ parameters['focus_areas'] = focus_areas
556
+ result = tool["function"](**parameters)
557
+
558
+ if current_entity != 'general':
559
+ entity_progress[current_entity]['queries'].append(result)
560
+
561
+ previous_queries.append(result)
562
+
563
+ elif tool_name == "reason":
564
+ if current_entity != 'general' and 'reasoning_context' not in parameters:
565
+ parameters['reasoning_context'] = entity_progress[current_entity]['insights']
566
+ elif 'reasoning_context' not in parameters:
567
+ parameters['reasoning_context'] = reasoning_context[:]
568
+
569
+ if 'prompt' not in parameters:
570
+ if current_entity != 'general':
571
+ parameters['prompt'] = f"{prompt} focusing on {current_entity}"
572
+ else:
573
+ parameters['prompt'] = prompt
574
+
575
+ if 'search_results' not in parameters:
576
+ parameters['search_results'] = []
577
+
578
+ if 'focus_areas' not in parameters and focus_areas:
579
+ parameters['focus_areas'] = focus_areas
580
+
581
+ result = tool["function"](**parameters)
582
+
583
+ if current_entity != 'general':
584
+ entity_progress[current_entity]['insights'].append(result)
585
+ if current_entity not in entity_specific_insights:
586
+ entity_specific_insights[current_entity] = []
587
+ entity_specific_insights[current_entity].append(result)
588
+ else:
589
+ reasoning_context.append(result)
590
+
591
+ all_insights.append(result)
592
+
593
+ elif tool_name == "search_web":
594
+ result = tool_search_web(**parameters)
595
+ filtered_result = filter_results(result,
596
+ prompt if current_entity == 'general' else f"{prompt} {current_entity}",
597
+ previous_snippets=seen_snippets)
598
+
599
+ # Update seen snippets
600
+ for r in filtered_result:
601
+ seen_snippets.add(r['snippet'])
602
+
603
+ result = filtered_result
604
+
605
+ if not result:
606
+ query = parameters.get('query', '')
607
+ if query:
608
+ failed_queries.append(query)
609
+
610
+ elif tool_name == "critique_reasoning":
611
+ if 'previous_critiques' not in parameters:
612
+ parameters['previous_critiques'] = previous_critiques
613
+
614
+ if all_insights:
615
+ if 'reasoning_output' not in parameters:
616
+ parameters['reasoning_output'] = all_insights[-1]
617
+ if 'prompt' not in parameters:
618
+ parameters['prompt'] = prompt
619
+
620
+ result = tool["function"](**parameters)
621
+ previous_critiques.append(result)
622
+ context.append(f"Critique: {result[:200]}...")
623
+ else:
624
+ result = "No reasoning to critique yet."
625
+
626
+ elif tool_name == "identify_contradictions":
627
+ result = tool["function"](**parameters)
628
+ if result:
629
+ contradictions = result # Store for later use in summarization
630
+ context.append(f"Identified contradictions: {result}")
631
+
632
+ elif tool_name == "identify_focus_areas":
633
+ if 'failed_areas' not in parameters:
634
+ parameters['failed_areas'] = failed_areas
635
+ result = tool["function"](**parameters)
636
+ if result:
637
+ # Update focus areas, but keep track of ones that didn't yield results
638
+ old_focus = set(focus_areas)
639
+ focus_areas = result
640
+ failed_areas.extend([area for area in old_focus if area not in result])
641
+ context.append(f"New focus areas: {result}")
642
+
643
+ elif tool_name == "meta_analyze":
644
+ if 'entity_insights' not in parameters:
645
+ parameters['entity_insights'] = entity_specific_insights
646
+ if 'prompt' not in parameters:
647
+ parameters['prompt'] = prompt
648
+ result = tool["function"](**parameters)
649
+ if result:
650
+ all_insights.append(result) # Add meta-analysis to insights
651
+ context.append(f"Meta-analysis across entities: {result[:200]}...")
652
+
653
+ else:
654
+ result = tool["function"](**parameters)
655
 
656
+ # Truncate very long results for the intermediate output
657
+ result_str = str(result)
658
+ if len(result_str) > 500:
659
+ result_str = result_str[:500] + "..."
660
+
661
+ intermediate_output += f"Iteration {i+1} - Result: {result_str}\n"
662
+
663
+ # Add truncated result to context
664
+ result_context = result_str
665
+ if len(result_str) > 300: # Even shorter for context
666
+ result_context = result_str[:300] + "..."
667
+ context.append(f"Used: {tool_name}, Result: {result_context}")
668
 
669
+ except Exception as e:
670
+ logger.error(f"Error with {tool_name}: {str(e)}")
671
+ context.append(f"Error with {tool_name}: {str(e)}")
672
+ intermediate_output += f"Iteration {i+1} - Error: {str(e)}\n"
673
+ continue
674
+
675
+ # Perform final meta-analysis if we have entity-specific insights
676
+ if len(entity_specific_insights) > 1 and len(all_insights) > 2:
677
+ meta_analysis = tool_meta_analyze(entity_insights=entity_specific_insights, prompt=prompt)
678
+ if meta_analysis:
679
+ all_insights.append(meta_analysis)
680
+ intermediate_output += f"Final Meta-Analysis: {meta_analysis[:500]}...\n"
681
+
682
+ # Generate the final summary
683
+ if all_insights:
684
+ final_result = tool_summarize(all_insights, prompt, contradictions)
685
+ else:
686
+ final_result = "Could not find meaningful information despite multiple attempts."
687
+
688
+ # Prepare the full output with detailed tracking
689
+ full_output = f"**Research Prompt:** {prompt}\n\n"
690
+
691
+ if key_entities:
692
+ full_output += f"**Key Entities Identified:** {', '.join(key_entities)}\n\n"
693
+
694
+ full_output += "**Research Process:**\n" + intermediate_output + "\n"
695
+
696
+ if contradictions:
697
+ full_output += "**Contradictions Identified:**\n"
698
+ for i, contradiction in enumerate(contradictions, 1):
699
+ full_output += f"{i}. {contradiction}\n"
700
+ full_output += "\n"
701
+
702
+ full_output += f"**Final Analysis:**\n{final_result}\n\n"
703
+
704
+ # Add session info for potential follow-up
705
+ full_output += f"Research Session ID: {research_session_id}\n"
706
+ full_output += f"Completed at: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n"
707
+ full_output += f"Total iterations: {i+1}\n"
708
+ full_output += f"Total insights generated: {len(all_insights)}\n"
709
+
710
+ return full_output
711
+
712
+ # Create CSS for a more professional look
713
+ custom_css = """
714
+ .gradio-container {
715
+ background-color: #f7f9fc;
716
+ }
717
+ .output-box {
718
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
719
+ line-height: 1.5;
720
+ }
721
+ h3 {
722
+ color: #2c3e50;
723
+ font-weight: 600;
724
+ }
725
+ .footer {
726
+ text-align: center;
727
+ margin-top: 20px;
728
+ color: #7f8c8d;
729
+ font-size: 0.9em;
730
+ }
731
  """
732
+
733
+ # Create the Gradio interface with enhanced UI
734
+ iface = gr.Interface(
735
+ fn=deep_research,
736
+ inputs=[
737
+ gr.Textbox(lines=5, placeholder="Enter your research question...", label="Research Question")
 
 
 
 
 
 
 
 
 
738
  ],
739
+ outputs=gr.Textbox(lines=30, placeholder="Research results will appear here...", label="Research Results", elem_classes=["output-box"]),
740
+ title="Advanced Multi-Stage Research Assistant",
741
+ description="""This tool performs deep, multi-faceted research by:
742
+ 1. Breaking down complex topics into key entities and aspects
743
+ 2. Iteratively searching, reasoning, and critiquing findings
744
+ 3. Exploring different perspectives and addressing contradictions
745
+ 4. Synthesizing insights across multiple information sources""",
746
+ examples=[
747
+ ["What are the key factors affecting urban tree survival and how do they vary between developing and developed countries?"],
748
+ ["Compare and contrast the economic policies of China and the United States over the past two decades, analyzing their impacts on global trade."],
749
+ ["What are the most promising approaches to quantum computing and what are their respective advantages and limitations?"],
750
+ ["Analyze the environmental and social impacts of lithium mining for electric vehicle batteries."],
751
+ ["How has artificial intelligence influenced medical diagnostics in the past five years, and what are the ethical considerations?"]
752
+ ],
753
+ theme="default",
754
+ css=custom_css,
755
+ allow_flagging=False,
756
+ analytics_enabled=False,
757
  )
758
 
759
+ # Add footer with additional information
760
+ footer_html = """
761
+ <div class="footer">
762
+ <p>This research assistant performs advanced multi-stage analysis using natural language processing and web search.</p>
763
+ <p>Results should be verified with additional sources. Not suitable for medical, legal, or emergency use.</p>
764
+ </div>
765
+ """
766
 
767
+ # Launch the interface
768
+ iface.launch(share=False)