Spaces:
Running
Running
Update app.py
Browse files
app.py
CHANGED
@@ -256,12 +256,14 @@ def perform_duckduckgo_search(query: str, max_results: int = 5): # Reduced max_r
|
|
256 |
# Define the new semantic date/time detection and calculation function using dateparser
|
257 |
def perform_date_calculation(query: str) -> str or None:
|
258 |
"""
|
259 |
-
Analyzes query for date/time
|
260 |
-
|
261 |
-
|
262 |
-
|
|
|
263 |
"""
|
264 |
print(f"Executing Tool: perform_date_calculation with query='{query}') using dateparser.search_dates")
|
|
|
265 |
|
266 |
try:
|
267 |
eafrica_tz = pytz.timezone('Africa/Dar_es_Salaam')
|
@@ -270,35 +272,68 @@ def perform_date_calculation(query: str) -> str or None:
|
|
270 |
print("Error: Unknown timezone 'Africa/Dar_es_Salaam'. Using default system time.")
|
271 |
now = datetime.now()
|
272 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
273 |
try:
|
274 |
-
# Try parsing with Swahili first, then English
|
275 |
found = search_dates(
|
276 |
query,
|
277 |
settings={
|
278 |
"PREFER_DATES_FROM": "future",
|
279 |
"RELATIVE_BASE": now
|
280 |
},
|
281 |
-
languages=['
|
282 |
)
|
283 |
|
284 |
if not found:
|
285 |
-
print("dateparser.search_dates could not parse
|
286 |
-
return
|
287 |
|
288 |
text_snippet, parsed = found[0]
|
289 |
print(f"dateparser.search_dates found: text='{text_snippet}', parsed='{parsed}'")
|
290 |
|
291 |
-
is_swahili = any(swahili_phrase in
|
292 |
|
293 |
-
# Handle timezone information
|
294 |
if now.tzinfo is not None and parsed.tzinfo is None:
|
295 |
parsed = now.tzinfo.localize(parsed)
|
296 |
elif now.tzinfo is None and parsed.tzinfo is not None:
|
297 |
parsed = parsed.replace(tzinfo=None)
|
298 |
|
299 |
-
# Check if the parsed date is today and time is close to now or midnight
|
300 |
if parsed.date() == now.date():
|
301 |
-
# Consider it "now" if within a small time window or if no specific time was parsed (midnight)
|
302 |
if abs((parsed - now).total_seconds()) < 60 or parsed.time() == datetime.min.time():
|
303 |
print("Query parsed to today's date and time is close to 'now' or midnight, returning current time/date.")
|
304 |
if is_swahili:
|
@@ -336,43 +371,15 @@ def perform_date_calculation(query: str) -> str or None:
|
|
336 |
def determine_tool_usage(query: str) -> str:
|
337 |
"""
|
338 |
Analyzes the query to determine if a specific tool is needed.
|
339 |
-
Returns the name of the tool ('duckduckgo_search', 'business_info_retrieval'
|
340 |
-
|
341 |
-
|
342 |
"""
|
343 |
query_lower = query.lower()
|
344 |
|
345 |
-
# 1.
|
346 |
-
date_time_check_result = perform_date_calculation(query)
|
347 |
-
if date_time_check_result is not None:
|
348 |
-
print(f"Detected as date/time calculation query based on dateparser result for: '{query}'")
|
349 |
-
return "date_calculation"
|
350 |
-
|
351 |
-
# 2. Use LLM to determine if DuckDuckGo search is needed
|
352 |
-
messages_tool_determination_search = [{"role": "user", "content": f"Does the following query require searching the web for current or general knowledge information (e.g., news, facts, definitions, current events)? Respond ONLY with 'duckduckgo_search' or 'none'. Query: {query}"}]
|
353 |
-
try:
|
354 |
-
search_determination_response = client.chat_completion(
|
355 |
-
messages=messages_tool_determination_search,
|
356 |
-
max_tokens=20,
|
357 |
-
temperature=0.1,
|
358 |
-
top_p=0.9
|
359 |
-
).choices[0].message.content or ""
|
360 |
-
response_lower = search_determination_response.strip().lower()
|
361 |
-
|
362 |
-
if "duckduckgo_search" in response_lower:
|
363 |
-
print(f"Model-determined tool for '{query}': 'duckduckgo_search'")
|
364 |
-
return "duckduckgo_search"
|
365 |
-
else:
|
366 |
-
print(f"Model-determined tool for '{query}': 'none' (for search)")
|
367 |
-
|
368 |
-
except Exception as e:
|
369 |
-
print(f"Error during LLM call for search tool determination for query '{query}': {e}")
|
370 |
-
print(traceback.format_exc())
|
371 |
-
print(f"Proceeding without search tool check for query '{query}' due to error.")
|
372 |
-
|
373 |
-
|
374 |
-
# 3. If neither date calculation nor DuckDuckGo search, check for Business Info Retrieval if RAG is available
|
375 |
if business_info_available:
|
|
|
376 |
messages_business_check = [{"role": "user", "content": f"Does the following query ask about a specific person, service, offering, or description that would likely be found in a business's knowledge base? Answer only 'yes' or 'no'. Query: {query}"}]
|
377 |
try:
|
378 |
business_check_response = client.chat_completion(
|
@@ -380,8 +387,7 @@ def determine_tool_usage(query: str) -> str:
|
|
380 |
max_tokens=10,
|
381 |
temperature=0.1
|
382 |
).choices[0].message.content.strip().lower()
|
383 |
-
|
384 |
-
if business_check_response == "yes":
|
385 |
print(f"Detected as business info query based on LLM check: '{query}'")
|
386 |
return "business_info_retrieval"
|
387 |
else:
|
@@ -391,9 +397,40 @@ def determine_tool_usage(query: str) -> str:
|
|
391 |
print(traceback.format_exc())
|
392 |
print(f"Proceeding without business info check for query '{query}' due to error.")
|
393 |
|
394 |
-
#
|
395 |
-
|
396 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
397 |
|
398 |
|
399 |
# Function to generate text using the LLM, incorporating tool results if available
|
@@ -432,8 +469,7 @@ def generate_text(prompt: str, tool_results: dict = None) -> str:
|
|
432 |
else:
|
433 |
full_prompt_builder.append(f"{results}\n\n") # Handle single string results (like date calculation)
|
434 |
|
435 |
-
|
436 |
-
print("Added tool results and instruction to final prompt.")
|
437 |
else:
|
438 |
print("No tool results to add to final prompt.")
|
439 |
|
@@ -590,6 +626,5 @@ if __name__ == "__main__":
|
|
590 |
iface.launch(debug=True)
|
591 |
except Exception as e:
|
592 |
print(f"Error launching Gradio interface: {e}")
|
593 |
-
print(traceback.
|
594 |
-
print("Please check the console output for more details on the error.")
|
595 |
-
|
|
|
256 |
# Define the new semantic date/time detection and calculation function using dateparser
|
257 |
def perform_date_calculation(query: str) -> str or None:
|
258 |
"""
|
259 |
+
Analyzes query for explicit date/time calculation requests and performs the calculation.
|
260 |
+
Returns a human-friendly response string if a calculation is clearly requested,
|
261 |
+
otherwise returns None to indicate the LLM should handle the query.
|
262 |
+
Uses dateparser.search_dates but is more selective about returning a result.
|
263 |
+
It is also designed to handle multiple languages and provide the time for East Africa (Tanzania).
|
264 |
"""
|
265 |
print(f"Executing Tool: perform_date_calculation with query='{query}') using dateparser.search_dates")
|
266 |
+
query_lower = query.lower()
|
267 |
|
268 |
try:
|
269 |
eafrica_tz = pytz.timezone('Africa/Dar_es_Salaam')
|
|
|
272 |
print("Error: Unknown timezone 'Africa/Dar_es_Salaam'. Using default system time.")
|
273 |
now = datetime.now()
|
274 |
|
275 |
+
calculation_phrases = [
|
276 |
+
r"\b(what is|what'?s|tell me|do you know|can you tell me)?\s*the\s*date\s+(of|for)?\s*(today|today'?s)?\b", # "what is today's date" (English)
|
277 |
+
r'\b(\d+)\s+(days?|weeks?|months?|years?)\s+(ago|from now)\b', # "3 days ago", "2 weeks from now" (English)
|
278 |
+
r'\bwhat day is it\b', # "what day is it" (English)
|
279 |
+
r'\bwhat is the time\b', # "what is the time" (English)
|
280 |
+
r'\bwhen is\s+', # "when is..." (English)
|
281 |
+
r'\bon\s+what\s+date\s+did\s+', # "on what date did..." (English)
|
282 |
+
r'\btarehe\s+ya\s+leo\b', # "date of today" (Swahili)
|
283 |
+
r'\bsaa\s+ngapi\b', # "what time" (Swahili)
|
284 |
+
r'\bmuda\s+gani\b', # "what time" (Swahili)
|
285 |
+
r'\btarehe\s+gani\b', # "what date" (Swahili)
|
286 |
+
r'\bni\s+saa\s+ngapi\b', # "it is what time" (Swahili)
|
287 |
+
r'\bni\s+tarehe\s+gani\b', # "it is what date" (Swahili)
|
288 |
+
r'\btarehe\s+ya\s+kesho\b', # "date of tomorrow" (Swahili)
|
289 |
+
r'\btarehe\s+ya\s+jana\b', # "date of yesterday" (Swahili)
|
290 |
+
r'\bsiku\s+ya\s+leo\b', # "day of today" (Swahili)
|
291 |
+
r'\bsiku\s+gani\b', # "what day" (Swahili)
|
292 |
+
r'\bmwezi\s+gani\b', # "what month" (Swahili)
|
293 |
+
r'\b(mwaka|mwaka\s+gani)\b', # "year" or "what year" (Swahili)
|
294 |
+
r'\bsaa\s+za\s+sasa\b', # "current time" (Swahili)
|
295 |
+
r'\btarehe\s+ngapi\s+leo\b', # "date how much today" (Swahili - common phrasing)
|
296 |
+
r'\bni\s+tarehe\s+ngapi\s+leo\b', # "it is date how much today" (Swahili - common phrasing)
|
297 |
+
r'\bsiku\s+ngapi\s+leo\b', # "day how much today" (Swahili - common phrasing)
|
298 |
+
r'\bni\s+siku\s+ngapi\s+leo\b', # "it is day how much today" (Swahili - common phrasing)
|
299 |
+
r'\b(leo|leo\s+hii)\b.*?\b(tarehe|siku|saa|muda)\b', # "today" followed by date/day/time (Swahili)
|
300 |
+
]
|
301 |
+
|
302 |
+
is_calculation_query = False
|
303 |
+
for phrase in calculation_phrases:
|
304 |
+
if re.search(phrase, query_lower):
|
305 |
+
is_calculation_query = True
|
306 |
+
break
|
307 |
+
|
308 |
+
if not is_calculation_query:
|
309 |
+
print("Query does not contain explicit date/time calculation phrases. Returning None.")
|
310 |
+
return None
|
311 |
+
|
312 |
try:
|
|
|
313 |
found = search_dates(
|
314 |
query,
|
315 |
settings={
|
316 |
"PREFER_DATES_FROM": "future",
|
317 |
"RELATIVE_BASE": now
|
318 |
},
|
319 |
+
languages=['en', 'sw']
|
320 |
)
|
321 |
|
322 |
if not found:
|
323 |
+
print("Explicit date/time phrase found, but dateparser.search_dates could not parse it.")
|
324 |
+
return "Sorry, I couldn’t understand that date/time. Could you rephrase?"
|
325 |
|
326 |
text_snippet, parsed = found[0]
|
327 |
print(f"dateparser.search_dates found: text='{text_snippet}', parsed='{parsed}'")
|
328 |
|
329 |
+
is_swahili = any(swahili_phrase in query_lower for swahili_phrase in ['tarehe', 'siku', 'saa', 'muda', 'leo', 'kesho', 'jana', 'ngapi', 'gani', 'mwezi', 'mwaka'])
|
330 |
|
|
|
331 |
if now.tzinfo is not None and parsed.tzinfo is None:
|
332 |
parsed = now.tzinfo.localize(parsed)
|
333 |
elif now.tzinfo is None and parsed.tzinfo is not None:
|
334 |
parsed = parsed.replace(tzinfo=None)
|
335 |
|
|
|
336 |
if parsed.date() == now.date():
|
|
|
337 |
if abs((parsed - now).total_seconds()) < 60 or parsed.time() == datetime.min.time():
|
338 |
print("Query parsed to today's date and time is close to 'now' or midnight, returning current time/date.")
|
339 |
if is_swahili:
|
|
|
371 |
def determine_tool_usage(query: str) -> str:
|
372 |
"""
|
373 |
Analyzes the query to determine if a specific tool is needed.
|
374 |
+
Returns the name of the tool ('duckduckgo_search', 'business_info_retrieval')
|
375 |
+
or 'none' if no specific tool is clearly indicated or if it's a date/time query
|
376 |
+
(as date/time is handled by semantic detection first).
|
377 |
"""
|
378 |
query_lower = query.lower()
|
379 |
|
380 |
+
# 1. Prioritize Business Info Retrieval if RAG is available
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
381 |
if business_info_available:
|
382 |
+
# Use a more direct prompt to encourage "yes" for relevant queries
|
383 |
messages_business_check = [{"role": "user", "content": f"Does the following query ask about a specific person, service, offering, or description that would likely be found in a business's knowledge base? Answer only 'yes' or 'no'. Query: {query}"}]
|
384 |
try:
|
385 |
business_check_response = client.chat_completion(
|
|
|
387 |
max_tokens=10,
|
388 |
temperature=0.1
|
389 |
).choices[0].message.content.strip().lower()
|
390 |
+
if "yes" in business_check_response:
|
|
|
391 |
print(f"Detected as business info query based on LLM check: '{query}'")
|
392 |
return "business_info_retrieval"
|
393 |
else:
|
|
|
397 |
print(traceback.format_exc())
|
398 |
print(f"Proceeding without business info check for query '{query}' due to error.")
|
399 |
|
400 |
+
# 2. If not a business info query, check for date calculation
|
401 |
+
date_time_check_result = perform_date_calculation(query)
|
402 |
+
if date_time_check_result is not None and "Sorry, I couldn’t understand" not in date_time_check_result:
|
403 |
+
print(f"Detected as date/time calculation query: '{query}'")
|
404 |
+
return "date_calculation"
|
405 |
+
|
406 |
+
# 3. If neither business info nor date calculation, use LLM for other tool determination (DuckDuckGo or none)
|
407 |
+
messages_tool_determination = [{"role": "user", "content": f"Determine the most suitable tool for the following query from the options: 'duckduckgo_search', or 'none'. Respond ONLY with the tool name. Query: {query}"}]
|
408 |
+
try:
|
409 |
+
initial_response = client.chat_completion(
|
410 |
+
messages=messages_tool_determination,
|
411 |
+
max_tokens=20,
|
412 |
+
temperature=0.1,
|
413 |
+
top_p=0.9
|
414 |
+
).choices[0].message.content or ""
|
415 |
+
response_lower = initial_response.strip().lower()
|
416 |
+
|
417 |
+
if "duckduckgo_search" in response_lower:
|
418 |
+
determined_tool = "duckduckgo_search"
|
419 |
+
elif "none" in response_lower:
|
420 |
+
# Allow the LLM to answer simple questions like "who are you" internally
|
421 |
+
# without a tool if it determines 'none'.
|
422 |
+
determined_tool = "none"
|
423 |
+
else:
|
424 |
+
print(f"Warning: LLM returned unexpected tool determination '{initial_response.strip()}'. Defaulting to 'none'.")
|
425 |
+
determined_tool = "none"
|
426 |
+
print(f"Model-determined tool: {determined_tool}")
|
427 |
+
return determined_tool
|
428 |
+
|
429 |
+
except Exception as e:
|
430 |
+
print(f"Error during LLM call for tool determination for query '{query}': {e}")
|
431 |
+
print(traceback.format_exc())
|
432 |
+
print(f"Defaulting to 'none' tool due to error for query '{query}'.")
|
433 |
+
return "none"
|
434 |
|
435 |
|
436 |
# Function to generate text using the LLM, incorporating tool results if available
|
|
|
469 |
else:
|
470 |
full_prompt_builder.append(f"{results}\n\n") # Handle single string results (like date calculation)
|
471 |
|
472 |
+
print("Added tool results to final prompt.")
|
|
|
473 |
else:
|
474 |
print("No tool results to add to final prompt.")
|
475 |
|
|
|
626 |
iface.launch(debug=True)
|
627 |
except Exception as e:
|
628 |
print(f"Error launching Gradio interface: {e}")
|
629 |
+
print(traceback. обзоr)
|
630 |
+
print("Please check the console output for more details on the error.")
|
|