Update app.py
Browse files
app.py
CHANGED
|
@@ -1286,6 +1286,29 @@ def _make_boolean(text: str) -> str:
|
|
| 1286 |
def _clean_database_keywords(boolean_query: str) -> str:
|
| 1287 |
return re.sub(r'\s+', ' ', re.sub(r'\b(AND|OR|NOT)\b|[()"]', ' ', boolean_query, flags=re.IGNORECASE)).strip()
|
| 1288 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1289 |
def _light_strip_retrieval_boilerplate(text: str) -> str:
|
| 1290 |
cleaned = re.sub(r'^\s*(please\s+)?(?:can you|could you|would you)\s+', '', (text or '').strip(), flags=re.IGNORECASE)
|
| 1291 |
cleaned = re.sub(r'^\s*(please\s+)?help me\s+', '', cleaned, flags=re.IGNORECASE)
|
|
@@ -1583,8 +1606,32 @@ Input: "Find one peer-reviewed article from the last five years on climate chang
|
|
| 1583 |
β natural:"Peer-reviewed article from the last five years on climate change and biodiversity. Summarize the methodology and findings."
|
| 1584 |
β boolean:("climate change" OR "global warming") AND (biodiversity OR ecosystems)
|
| 1585 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1586 |
Return ONLY valid JSON:
|
| 1587 |
-
{{"corrected":"full polished request","natural":"full natural-language research query","boolean":"(A OR B) AND (C OR D)","year_from":"","year_to":"","peer_reviewed":false,"open_access":false}}
|
| 1588 |
|
| 1589 |
Query: "{light_query}"""
|
| 1590 |
try:
|
|
@@ -1598,6 +1645,8 @@ Query: "{light_query}"""
|
|
| 1598 |
has_parens = "(" in boolean if boolean else False
|
| 1599 |
if not has_operators or not has_parens:
|
| 1600 |
boolean = _make_boolean(corrected)
|
|
|
|
|
|
|
| 1601 |
return {
|
| 1602 |
"corrected": corrected,
|
| 1603 |
"natural": natural,
|
|
@@ -2516,14 +2565,21 @@ async def agent_query(req: AgentRequest):
|
|
| 2516 |
combined = synthesis_pool + [r for r in combined if r not in synthesis_pool]
|
| 2517 |
else:
|
| 2518 |
from urllib.parse import quote as _quote
|
| 2519 |
-
#
|
| 2520 |
-
|
| 2521 |
-
_bool =
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2522 |
_primo_url = (
|
| 2523 |
"https://khalifa.primo.exlibrisgroup.com/discovery/search"
|
| 2524 |
+ "?query=any,contains," + _quote(_bool) + ",AND"
|
| 2525 |
+ "&tab=Everything&search_scope=MyInst_and_CI"
|
| 2526 |
+ "&vid=971KUOSTAR_INST:KU&lang=en&mode=advanced&offset=0"
|
|
|
|
| 2527 |
)
|
| 2528 |
answer = (
|
| 2529 |
"I searched PRIMO for literature on this topic but couldn't retrieve "
|
|
|
|
| 1286 |
def _clean_database_keywords(boolean_query: str) -> str:
|
| 1287 |
return re.sub(r'\s+', ' ', re.sub(r'\b(AND|OR|NOT)\b|[()"]', ' ', boolean_query, flags=re.IGNORECASE)).strip()
|
| 1288 |
|
| 1289 |
+
def _sanitize_boolean_for_primo(boolean: str) -> str:
|
| 1290 |
+
"""
|
| 1291 |
+
Clean an LLM-generated Boolean string so it works in PRIMO advanced search URLs.
|
| 1292 |
+
PRIMO accepts: ("smart city" OR "urban mobility") AND ("technocentricity")
|
| 1293 |
+
PRIMO rejects: single quotes, year-only groups, overly long strings.
|
| 1294 |
+
"""
|
| 1295 |
+
if not boolean:
|
| 1296 |
+
return boolean
|
| 1297 |
+
# Replace single quotes with double quotes around terms
|
| 1298 |
+
# e.g. ('Technocentricity') β ("Technocentricity")
|
| 1299 |
+
boolean = re.sub(r"'([^']+)'", r'"\1"', boolean)
|
| 1300 |
+
# Remove pure-year groups like (2025 OR 2026) or (2024) β years belong in date facets
|
| 1301 |
+
boolean = re.sub(r'\(\s*(?:(?:19|20)\d{2}\s*(?:OR\s*(?:19|20)\d{2}\s*)*)\)', '', boolean, flags=re.IGNORECASE)
|
| 1302 |
+
# Remove dangling AND/OR at start or end after year group removal
|
| 1303 |
+
boolean = re.sub(r'^\s*(AND|OR)\s*', '', boolean, flags=re.IGNORECASE)
|
| 1304 |
+
boolean = re.sub(r'\s*(AND|OR)\s*$', '', boolean, flags=re.IGNORECASE)
|
| 1305 |
+
# Collapse double AND/OR
|
| 1306 |
+
boolean = re.sub(r'(AND|OR)\s+(AND|OR)', r'\1', boolean, flags=re.IGNORECASE)
|
| 1307 |
+
# Collapse excess whitespace
|
| 1308 |
+
boolean = re.sub(r'\s+', ' ', boolean).strip()
|
| 1309 |
+
return boolean
|
| 1310 |
+
|
| 1311 |
+
|
| 1312 |
def _light_strip_retrieval_boilerplate(text: str) -> str:
|
| 1313 |
cleaned = re.sub(r'^\s*(please\s+)?(?:can you|could you|would you)\s+', '', (text or '').strip(), flags=re.IGNORECASE)
|
| 1314 |
cleaned = re.sub(r'^\s*(please\s+)?help me\s+', '', cleaned, flags=re.IGNORECASE)
|
|
|
|
| 1606 |
β natural:"Peer-reviewed article from the last five years on climate change and biodiversity. Summarize the methodology and findings."
|
| 1607 |
β boolean:("climate change" OR "global warming") AND (biodiversity OR ecosystems)
|
| 1608 |
|
| 1609 |
+
BOOLEAN RULES β follow strictly:
|
| 1610 |
+
- Use DOUBLE quotes only around multi-word phrases: ("smart city" OR "urban mobility")
|
| 1611 |
+
- NEVER use single quotes: wrong: ('smart city') right: ("smart city")
|
| 1612 |
+
- Max 3 AND groups. Each group max 4 OR terms.
|
| 1613 |
+
- NEVER put years (2024, 2025, 2026) as Boolean terms β put them in year_from/year_to instead.
|
| 1614 |
+
- Comparison topics (e.g. "London vs Bogota", "Global vs Emerging cities") β extract the CONCEPT
|
| 1615 |
+
as a search term, not the city names: e.g. ("smart city" OR "urban governance") AND ("smart mobility")
|
| 1616 |
+
- Keep it simple enough to return results. Prefer broader terms over hyper-specific phrases.
|
| 1617 |
+
|
| 1618 |
+
YEAR DETECTION: if question mentions "2025-2026", "last year", "recent", "current" β set year_from appropriately.
|
| 1619 |
+
|
| 1620 |
+
Examples:
|
| 1621 |
+
Input: "Technocentricity vs Citizen-Centricity in Smart City development, contrast 2025-2026 outcomes of smart mobility in Global Metropolitan Leaders vs Emerging Global Cities"
|
| 1622 |
+
β boolean:("smart city" OR "urban governance") AND ("technocentricity" OR "citizen-centric") AND ("smart mobility" OR "urban transport")
|
| 1623 |
+
β year_from:"2023", year_to:"", peer_reviewed:true
|
| 1624 |
+
|
| 1625 |
+
Input: "Summarize shift in Personalized Genomic Medicine from N-of-1 trials to Scalable Precision Platforms last 24 months"
|
| 1626 |
+
β boolean:("personalized genomic medicine" OR "precision medicine") AND ("N-of-1 trials" OR "scalable precision")
|
| 1627 |
+
β year_from:"{year-2}", peer_reviewed:true
|
| 1628 |
+
|
| 1629 |
+
Input: "Find one peer-reviewed article from the last five years on climate change and biodiversity"
|
| 1630 |
+
β boolean:("climate change" OR "global warming") AND (biodiversity OR ecosystems)
|
| 1631 |
+
β year_from:"{year-5}", peer_reviewed:true
|
| 1632 |
+
|
| 1633 |
Return ONLY valid JSON:
|
| 1634 |
+
{{"corrected":"full polished request","natural":"full natural-language research query","boolean":"(\"A\" OR \"B\") AND (\"C\" OR \"D\")","year_from":"","year_to":"","peer_reviewed":false,"open_access":false}}
|
| 1635 |
|
| 1636 |
Query: "{light_query}"""
|
| 1637 |
try:
|
|
|
|
| 1645 |
has_parens = "(" in boolean if boolean else False
|
| 1646 |
if not has_operators or not has_parens:
|
| 1647 |
boolean = _make_boolean(corrected)
|
| 1648 |
+
# Sanitize: fix single quotes β double quotes, strip year groups (years go in facets)
|
| 1649 |
+
boolean = _sanitize_boolean_for_primo(boolean)
|
| 1650 |
return {
|
| 1651 |
"corrected": corrected,
|
| 1652 |
"natural": natural,
|
|
|
|
| 2565 |
combined = synthesis_pool + [r for r in combined if r not in synthesis_pool]
|
| 2566 |
else:
|
| 2567 |
from urllib.parse import quote as _quote
|
| 2568 |
+
# Sanitize boolean: fix single quotes -> double quotes, strip year groups
|
| 2569 |
+
_bool_raw = search_plan.get("boolean") or database_query
|
| 2570 |
+
_bool = _sanitize_boolean_for_primo(_bool_raw)
|
| 2571 |
+
# Build URL using the SAME format as the frontend buildSearchCard (Approach 1 β confirmed working):
|
| 2572 |
+
# query=any,contains,{encoded_full_boolean},AND&tab=Everything&search_scope=MyInst_and_CI&mode=advanced
|
| 2573 |
+
_facet = ""
|
| 2574 |
+
if year_from:
|
| 2575 |
+
_yt = year_to or str(datetime.now().year)
|
| 2576 |
+
_facet = "&multiFacets=facet_searchcreationdate,include," + year_from + "%7C,%7C" + _yt
|
| 2577 |
_primo_url = (
|
| 2578 |
"https://khalifa.primo.exlibrisgroup.com/discovery/search"
|
| 2579 |
+ "?query=any,contains," + _quote(_bool) + ",AND"
|
| 2580 |
+ "&tab=Everything&search_scope=MyInst_and_CI"
|
| 2581 |
+ "&vid=971KUOSTAR_INST:KU&lang=en&mode=advanced&offset=0"
|
| 2582 |
+
+ _facet
|
| 2583 |
)
|
| 2584 |
answer = (
|
| 2585 |
"I searched PRIMO for literature on this topic but couldn't retrieve "
|