Hamdy005 commited on
Commit
50fc5bd
Β·
1 Parent(s): ca22acf

refactor: replace agent-based web search with parallelized retrieval using Wikipedia and DuckDuckGo

Browse files
quiz_generator/constants.py CHANGED
@@ -9,11 +9,11 @@ MAX_SAMPLE_CHUNKS = 10
9
  RETRIEVER_K = 5
10
 
11
  # Web search configuration β€” Wikipedia is the primary educational source
12
- WIKI_TOP_K_RESULTS = 3
13
- WIKI_DOC_CONTENT_CHARS_MAX = 40000 # 3 Γ— 40k = 120k (broad topic coverage)
14
  # arXiv adds technical depth as supplementary source
15
  ARXIV_TOP_K_RESULTS = 1
16
- ARXIV_DOC_CONTENT_CHARS_MAX = 30000 # 1 Γ— 30k = 30k β†’ total β‰ˆ 150k
17
 
18
  QUIZ_PROMPT_TEMPLATE = PromptTemplate(
19
  input_variables=[
 
9
  RETRIEVER_K = 5
10
 
11
  # Web search configuration β€” Wikipedia is the primary educational source
12
+ WIKI_TOP_K_RESULTS = 2
13
+ WIKI_DOC_CONTENT_CHARS_MAX = 60000
14
  # arXiv adds technical depth as supplementary source
15
  ARXIV_TOP_K_RESULTS = 1
16
+ ARXIV_DOC_CONTENT_CHARS_MAX = 30000
17
 
18
  QUIZ_PROMPT_TEMPLATE = PromptTemplate(
19
  input_variables=[
quiz_generator/quiz.py CHANGED
@@ -126,38 +126,61 @@ def _contextual_quiz(difficulty, mcq_count, tf_count, context, material_id):
126
  def _web_quiz(difficulty, mcq_count, tf_count, topic_title):
127
  logger.info(f"Web Quiz started (topic={topic_title}, diff={difficulty})")
128
  try:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
129
  prompt = WEB_QUIZ_PROMPT_TEMPLATE
130
  llm = get_quiz_llm()
131
- tools = web_search_tools(
132
- wiki_k=WIKI_TOP_K_RESULTS,
133
- wiki_chars=WIKI_DOC_CONTENT_CHARS_MAX,
134
- arxiv_k=ARXIV_TOP_K_RESULTS,
135
- arxiv_chars=ARXIV_DOC_CONTENT_CHARS_MAX,
136
- )
137
- agent = create_tool_calling_agent(llm, tools, prompt)
138
 
139
- executor = AgentExecutor(
140
- agent=agent,
141
- tools=tools,
142
- verbose=False,
143
- return_intermediate_steps=False,
144
- handle_parsing_errors=True,
145
- max_iterations=80,
146
- max_execution_time=300,
147
- )
148
-
149
- safe_context = topic_title
150
- response = executor.invoke({
151
  "topic": topic_title,
152
- "context": safe_context,
153
  "difficulty": difficulty,
154
  "mcq_count": mcq_count,
155
  "tf_count": tf_count,
156
  "source_type": "Web Search",
157
  "agent_scratchpad": "",
158
  })
159
- logger.info("Web Quiz agent finished successfully")
160
- return _parse_quiz(response)
 
 
161
  except Exception as e:
162
  logger.error(f"Web Quiz failed: {str(e)}", exc_info=True)
163
  raise
 
126
  def _web_quiz(difficulty, mcq_count, tf_count, topic_title):
127
  logger.info(f"Web Quiz started (topic={topic_title}, diff={difficulty})")
128
  try:
129
+ import concurrent.futures
130
+ from langchain_community.utilities import WikipediaAPIWrapper, DuckDuckGoSearchAPIWrapper
131
+
132
+ def fetch_wikipedia():
133
+ try:
134
+ wiki_api = WikipediaAPIWrapper(
135
+ top_k_results=WIKI_TOP_K_RESULTS,
136
+ doc_content_chars_max=WIKI_DOC_CONTENT_CHARS_MAX,
137
+ )
138
+ return wiki_api.run(topic_title)
139
+ except Exception as e:
140
+ logger.warning(f"Wikipedia search for '{topic_title}' failed: {e}")
141
+ return ""
142
+
143
+ def fetch_duckduckgo():
144
+ try:
145
+ duck_api = DuckDuckGoSearchAPIWrapper()
146
+ return duck_api.run(topic_title)
147
+ except Exception as e:
148
+ logger.warning(f"DuckDuckGo search for '{topic_title}' failed: {e}")
149
+ return ""
150
+
151
+ # Fetch in parallel
152
+ with concurrent.futures.ThreadPoolExecutor(max_workers=2) as executor:
153
+ wiki_future = executor.submit(fetch_wikipedia)
154
+ duck_future = executor.submit(fetch_duckduckgo)
155
+
156
+ wiki_content = wiki_future.result()
157
+ duck_content = duck_future.result()
158
+
159
+ all_content = []
160
+ if wiki_content and wiki_content.strip():
161
+ all_content.append(f"--- Wikipedia ---\n{wiki_content}")
162
+ if duck_content and duck_content.strip():
163
+ all_content.append(f"--- Web Search ---\n{duck_content}")
164
+
165
+ combined_context = "\n\n".join(all_content) if all_content else f"No web content found for: {topic_title}"
166
+
167
  prompt = WEB_QUIZ_PROMPT_TEMPLATE
168
  llm = get_quiz_llm()
 
 
 
 
 
 
 
169
 
170
+ chain = prompt | llm
171
+ response = chain.invoke({
 
 
 
 
 
 
 
 
 
 
172
  "topic": topic_title,
173
+ "context": combined_context,
174
  "difficulty": difficulty,
175
  "mcq_count": mcq_count,
176
  "tf_count": tf_count,
177
  "source_type": "Web Search",
178
  "agent_scratchpad": "",
179
  })
180
+
181
+ raw_content = response.content
182
+ logger.info("Web Quiz chain finished successfully")
183
+ return _parse_quiz({"output": raw_content})
184
  except Exception as e:
185
  logger.error(f"Web Quiz failed: {str(e)}", exc_info=True)
186
  raise
summary_generator/constants.py CHANGED
@@ -4,12 +4,12 @@ MAX_INPUT_CHARS = 150000
4
  MAX_COMBINED_TEXT_LEN = 160000
5
 
6
  # Web search configuration β€” targets ~150k total (β‰ˆ MAX_INPUT_CHARS)
7
- WIKI_TOP_K_RESULTS = 3
8
- WIKI_DOC_CONTENT_CHARS_MAX = 40000 # 3 Γ— 40k = 120k (80% of budget β€” foundational content)
9
 
10
  # arXiv is supplementary β€” adds depth for technical/research topics
11
  ARXIV_TOP_K_RESULTS = 1
12
- ARXIV_DOC_CONTENT_CHARS_MAX = 30000 # 1 Γ— 30k = 30k β†’ total β‰ˆ 150k
13
 
14
  SUMMARIZER_PROMPT_TEMPLATE = PromptTemplate(
15
  input_variables=["input"],
 
4
  MAX_COMBINED_TEXT_LEN = 160000
5
 
6
  # Web search configuration β€” targets ~150k total (β‰ˆ MAX_INPUT_CHARS)
7
+ WIKI_TOP_K_RESULTS = 2
8
+ WIKI_DOC_CONTENT_CHARS_MAX = 60000
9
 
10
  # arXiv is supplementary β€” adds depth for technical/research topics
11
  ARXIV_TOP_K_RESULTS = 1
12
+ ARXIV_DOC_CONTENT_CHARS_MAX = 30000
13
 
14
  SUMMARIZER_PROMPT_TEMPLATE = PromptTemplate(
15
  input_variables=["input"],
summary_generator/summary.py CHANGED
@@ -1,6 +1,7 @@
1
  import re
2
  import logging
3
- from langchain_community.utilities import ArxivAPIWrapper, WikipediaAPIWrapper
 
4
  from src.rag.rag import get_llm
5
  from .constants import (
6
  SUMMARIZER_PROMPT_TEMPLATE,
@@ -8,8 +9,6 @@ from .constants import (
8
  MAX_INPUT_CHARS,
9
  WIKI_TOP_K_RESULTS,
10
  WIKI_DOC_CONTENT_CHARS_MAX,
11
- ARXIV_TOP_K_RESULTS,
12
- ARXIV_DOC_CONTENT_CHARS_MAX,
13
  )
14
 
15
  logger = logging.getLogger(__name__)
@@ -74,27 +73,37 @@ def web_summarizer(topic: str) -> str:
74
 
75
  all_content = []
76
 
77
- try:
78
- wiki_api = WikipediaAPIWrapper(
79
- top_k_results=WIKI_TOP_K_RESULTS,
80
- doc_content_chars_max=WIKI_DOC_CONTENT_CHARS_MAX,
81
- )
82
- wiki_content = wiki_api.run(topic)
83
- if wiki_content and wiki_content.strip():
84
- all_content.append(f"--- Wikipedia ---\n{wiki_content}")
85
- except Exception as e:
86
- logger.warning(f"Wikipedia search for '{topic}' failed: {e}")
87
-
88
- try:
89
- arxiv_api = ArxivAPIWrapper(
90
- top_k_results=ARXIV_TOP_K_RESULTS,
91
- doc_content_chars_max=ARXIV_DOC_CONTENT_CHARS_MAX,
92
- )
93
- arxiv_content = arxiv_api.run(topic)
94
- if arxiv_content and arxiv_content.strip():
95
- all_content.append(f"--- Arxiv ---\n{arxiv_content}")
96
- except Exception as e:
97
- logger.warning(f"Arxiv search for '{topic}' failed: {e}")
 
 
 
 
 
 
 
 
 
 
98
 
99
  if not all_content:
100
  logger.warning(f"No content found for topic: {topic}. Falling back to general knowledge.")
 
1
  import re
2
  import logging
3
+ import concurrent.futures
4
+ from langchain_community.utilities import WikipediaAPIWrapper, DuckDuckGoSearchAPIWrapper
5
  from src.rag.rag import get_llm
6
  from .constants import (
7
  SUMMARIZER_PROMPT_TEMPLATE,
 
9
  MAX_INPUT_CHARS,
10
  WIKI_TOP_K_RESULTS,
11
  WIKI_DOC_CONTENT_CHARS_MAX,
 
 
12
  )
13
 
14
  logger = logging.getLogger(__name__)
 
73
 
74
  all_content = []
75
 
76
+ def fetch_wikipedia():
77
+ try:
78
+ wiki_api = WikipediaAPIWrapper(
79
+ top_k_results=WIKI_TOP_K_RESULTS,
80
+ doc_content_chars_max=WIKI_DOC_CONTENT_CHARS_MAX,
81
+ )
82
+ return wiki_api.run(topic)
83
+ except Exception as e:
84
+ logger.warning(f"Wikipedia search for '{topic}' failed: {e}")
85
+ return ""
86
+
87
+ def fetch_duckduckgo():
88
+ try:
89
+ duck_api = DuckDuckGoSearchAPIWrapper()
90
+ return duck_api.run(topic)
91
+ except Exception as e:
92
+ logger.warning(f"DuckDuckGo search for '{topic}' failed: {e}")
93
+ return ""
94
+
95
+ # Execute searches in parallel to minimize latency
96
+ with concurrent.futures.ThreadPoolExecutor(max_workers=2) as executor:
97
+ wiki_future = executor.submit(fetch_wikipedia)
98
+ duck_future = executor.submit(fetch_duckduckgo)
99
+
100
+ wiki_content = wiki_future.result()
101
+ duck_content = duck_future.result()
102
+
103
+ if wiki_content and wiki_content.strip():
104
+ all_content.append(f"--- Wikipedia ---\n{wiki_content}")
105
+ if duck_content and duck_content.strip():
106
+ all_content.append(f"--- Web Search ---\n{duck_content}")
107
 
108
  if not all_content:
109
  logger.warning(f"No content found for topic: {topic}. Falling back to general knowledge.")