Prof-Reza commited on
Commit
a24abb9
·
verified ·
1 Parent(s): 617daa2

Upload app.py

Browse files

Improve chat UI and implement search result caching.

This commit fixes the chat display by switching to the default Gradio Chatbot (tuple-based) with a fixed height and returning chat pairs so conversations are visible. It also introduces a per-session resource cache to store web search results keyed by the query, preventing repeated searches and ensuring all resources shown come from real results. When searching, previously seen URLs are filtered out before adding to sources. State management now includes the resource cache alongside chat history, pairs, sources, and plan.

Files changed (1) hide show
  1. app.py +44 -15
app.py CHANGED
@@ -14,13 +14,15 @@ SYSTEM_PROMPT = (
14
  "conversation and gathered resources."
15
  )
16
 
17
- def chat(user_message, chat_history, chat_pairs, sources, plan):
18
  """Handle a user chat message and return updated chat state."""
19
- # Ensure lists are initialised
20
  if chat_history is None:
21
  chat_history = []
22
  if chat_pairs is None:
23
  chat_pairs = []
 
 
24
  # Append the user's message to the conversation history (list of dictionaries for Chatbot)
25
  chat_history.append({"role": "user", "content": user_message})
26
  # Build messages including system prompt for API call
@@ -131,8 +133,14 @@ def chat(user_message, chat_history, chat_pairs, sources, plan):
131
  lower_msg = user_message.lower()
132
  if any(trig in lower_msg for trig in search_triggers):
133
  try:
134
- # Perform web search using the entire user message as the query
135
- results = run_web_search(user_message, num_results=5, domain_filter="")
 
 
 
 
 
 
136
  # Normalize results:
137
  # Tavily may return a dictionary with a "results" key containing
138
  # the list of search results. If so, extract that list. If it's a
@@ -146,10 +154,24 @@ def chat(user_message, chat_history, chat_pairs, sources, plan):
146
  # Ensure the sources list is initialised
147
  if sources is None:
148
  sources = []
149
- sources.extend(normalized_results)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
150
  # Summarise results into a simple string with title and URL
151
  summary_lines = []
152
- for r in normalized_results:
153
  # Defensive: ensure r is a dict
154
  if isinstance(r, dict):
155
  title = r.get("title", "")
@@ -159,7 +181,10 @@ def chat(user_message, chat_history, chat_pairs, sources, plan):
159
  if summary_lines:
160
  assistant_reply = "Here are some resources I found:\n" + "\n".join(summary_lines)
161
  else:
162
- assistant_reply = "I couldn't find any results for that query."
 
 
 
163
  except Exception as e:
164
  assistant_reply = (
165
  "An error occurred during web search. Please ensure your search API key is configured.\n"
@@ -228,8 +253,8 @@ def chat(user_message, chat_history, chat_pairs, sources, plan):
228
  chat_history.append({"role": "assistant", "content": assistant_reply})
229
  # Append pair to display history for any other uses (kept for compatibility)
230
  chat_pairs.append((user_message, assistant_reply))
231
- # For Chatbot with type="messages", return the chat_history as the first output
232
- return chat_history, chat_history, chat_pairs, sources, plan
233
 
234
  def run_search(query, chat_history, chat_pairs, sources, plan, num_results=5, domain_filter=""):
235
  """Execute a web search and update sources list."""
@@ -309,9 +334,10 @@ with gr.Blocks() as demo:
309
  Chat with the assistant to brainstorm your course idea. You can ask the assistant to search the internet directly in the chat. When you're ready, click **Finalize Outline** to generate a course plan. Then generate the final course package (ZIP)."""
310
  )
311
  # Chat interface components
312
- # Use 'messages' type for Chatbot. The Chatbot expects a list of message dictionaries
313
- # (e.g. {"role": "user", "content": "..."}) when type="messages".
314
- chatbot = gr.Chatbot(label="Conversation", type="messages")
 
315
  msg_input = gr.Textbox(label="Your message", placeholder="Type your message and press Enter", lines=1)
316
  # Buttons and outputs (search is triggered via chat; no separate search controls)
317
  finalize_btn = gr.Button("Finalize Outline")
@@ -323,12 +349,15 @@ Chat with the assistant to brainstorm your course idea. You can ask the assistan
323
  state_chat_pairs = gr.State([])
324
  state_sources = gr.State([])
325
  state_plan = gr.State("")
 
 
 
326
  # Handle chat submission: update chatbot display and states
327
  msg_input.submit(
328
  chat,
329
- inputs=[msg_input, state_chat_history, state_chat_pairs, state_sources, state_plan],
330
- # First output goes to the Chatbot; we return the updated chat history (list of messages)
331
- outputs=[chatbot, state_chat_history, state_chat_pairs, state_sources, state_plan],
332
  )
333
  # Finalize outline button: generate course plan and store it
334
  finalize_btn.click(
 
14
  "conversation and gathered resources."
15
  )
16
 
17
+ def chat(user_message, chat_history, chat_pairs, sources, plan, resource_cache):
18
  """Handle a user chat message and return updated chat state."""
19
+ # Ensure lists/dicts are initialised
20
  if chat_history is None:
21
  chat_history = []
22
  if chat_pairs is None:
23
  chat_pairs = []
24
+ if resource_cache is None:
25
+ resource_cache = {}
26
  # Append the user's message to the conversation history (list of dictionaries for Chatbot)
27
  chat_history.append({"role": "user", "content": user_message})
28
  # Build messages including system prompt for API call
 
133
  lower_msg = user_message.lower()
134
  if any(trig in lower_msg for trig in search_triggers):
135
  try:
136
+ # Perform web search using the entire user message as the query. Use cached results if available
137
+ query_key = user_message.strip().lower()
138
+ if query_key in resource_cache:
139
+ results = resource_cache[query_key]
140
+ else:
141
+ results = run_web_search(user_message, num_results=5, domain_filter="")
142
+ # store results in cache for future queries
143
+ resource_cache[query_key] = results
144
  # Normalize results:
145
  # Tavily may return a dictionary with a "results" key containing
146
  # the list of search results. If so, extract that list. If it's a
 
154
  # Ensure the sources list is initialised
155
  if sources is None:
156
  sources = []
157
+ # Filter out duplicate URLs already in sources
158
+ existing_urls = set()
159
+ for src in sources:
160
+ if isinstance(src, dict):
161
+ url = src.get("url")
162
+ if url:
163
+ existing_urls.add(url)
164
+ new_results = []
165
+ for r in normalized_results:
166
+ if isinstance(r, dict):
167
+ url = r.get("url")
168
+ if url and url not in existing_urls:
169
+ new_results.append(r)
170
+ existing_urls.add(url)
171
+ sources.extend(new_results)
172
  # Summarise results into a simple string with title and URL
173
  summary_lines = []
174
+ for r in new_results:
175
  # Defensive: ensure r is a dict
176
  if isinstance(r, dict):
177
  title = r.get("title", "")
 
181
  if summary_lines:
182
  assistant_reply = "Here are some resources I found:\n" + "\n".join(summary_lines)
183
  else:
184
+ if normalized_results:
185
+ assistant_reply = "I've already shared the relevant resources from this search."
186
+ else:
187
+ assistant_reply = "I couldn't find any results for that query."
188
  except Exception as e:
189
  assistant_reply = (
190
  "An error occurred during web search. Please ensure your search API key is configured.\n"
 
253
  chat_history.append({"role": "assistant", "content": assistant_reply})
254
  # Append pair to display history for any other uses (kept for compatibility)
255
  chat_pairs.append((user_message, assistant_reply))
256
+ # For Chatbot with default type (list of (user, assistant) tuples), return chat_pairs as the first output
257
+ return chat_pairs, chat_history, chat_pairs, sources, plan, resource_cache
258
 
259
  def run_search(query, chat_history, chat_pairs, sources, plan, num_results=5, domain_filter=""):
260
  """Execute a web search and update sources list."""
 
334
  Chat with the assistant to brainstorm your course idea. You can ask the assistant to search the internet directly in the chat. When you're ready, click **Finalize Outline** to generate a course plan. Then generate the final course package (ZIP)."""
335
  )
336
  # Chat interface components
337
+ # Use the default Chatbot type (list of (user, assistant) tuples). This avoids CSS issues with the
338
+ # messages type and makes the conversation visible without custom styling. Specify a height to
339
+ # constrain the display area.
340
+ chatbot = gr.Chatbot(label="Conversation", height=400)
341
  msg_input = gr.Textbox(label="Your message", placeholder="Type your message and press Enter", lines=1)
342
  # Buttons and outputs (search is triggered via chat; no separate search controls)
343
  finalize_btn = gr.Button("Finalize Outline")
 
349
  state_chat_pairs = gr.State([])
350
  state_sources = gr.State([])
351
  state_plan = gr.State("")
352
+ # Cache for previously fetched search results (keyed by lowercased query). Helps prevent
353
+ # duplicate web searches and ensures all returned resources are real and consistent.
354
+ state_resource_cache = gr.State({})
355
  # Handle chat submission: update chatbot display and states
356
  msg_input.submit(
357
  chat,
358
+ inputs=[msg_input, state_chat_history, state_chat_pairs, state_sources, state_plan, state_resource_cache],
359
+ # First output goes to the Chatbot; return the updated chat pairs for proper display
360
+ outputs=[chatbot, state_chat_history, state_chat_pairs, state_sources, state_plan, state_resource_cache],
361
  )
362
  # Finalize outline button: generate course plan and store it
363
  finalize_btn.click(