Update src/api/agent.py
Browse files- src/api/agent.py +26 -19
src/api/agent.py
CHANGED
|
@@ -1219,6 +1219,9 @@ BOOLEAN RULES β follow strictly:
|
|
| 1219 |
- Max 3 AND groups. Each group max 4 OR terms.
|
| 1220 |
- NEVER put years as Boolean terms β put them in year_from/year_to
|
| 1221 |
- Keep broad enough to return results
|
|
|
|
|
|
|
|
|
|
| 1222 |
|
| 1223 |
EXAMPLES:
|
| 1224 |
Input: "find articles on global warming and renewable energy"
|
|
@@ -1229,6 +1232,14 @@ Input: "peer reviewed articles last 5 years on machine learning healthcare"
|
|
| 1229 |
β boolean:("machine learning" OR "deep learning" OR "AI") AND ("healthcare" OR "medicine" OR "clinical")
|
| 1230 |
β year_from:"{CURRENT_YEAR - 5}", peer_reviewed:true
|
| 1231 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1232 |
Input: "summarize shift in personalized genomic medicine from N-of-1 trials last 24 months"
|
| 1233 |
β boolean:("personalized genomic medicine" OR "precision medicine") AND ("N-of-1 trials" OR "scalable precision")
|
| 1234 |
β year_from:"{CURRENT_YEAR - 2}", peer_reviewed:true
|
|
@@ -1399,31 +1410,27 @@ async def _semantic_scholar_search(topic: str, limit: int = 8) -> List[dict]:
|
|
| 1399 |
|
| 1400 |
async def _openalex_search(topic: str, limit: int = 8) -> List[dict]:
|
| 1401 |
"""
|
| 1402 |
-
Search OpenAlex using the ?search= relevance engine
|
| 1403 |
-
clickable OpenAlex link shown to users
|
| 1404 |
-
|
| 1405 |
-
|
| 1406 |
-
|
| 1407 |
-
returned irrelevant or zero results. The ?search= parameter uses OpenAlex's
|
| 1408 |
-
full relevance engine across all fields, identical to the browser link.
|
| 1409 |
"""
|
| 1410 |
try:
|
| 1411 |
-
from urllib.parse import quote
|
| 1412 |
|
| 1413 |
-
# Strip boolean operators and retrieval filler
|
| 1414 |
-
# Matches what the user-facing OpenAlex link does.
|
| 1415 |
clean = re.sub(r'\b(AND|OR|NOT)\b|[()"\']', ' ', topic, flags=re.IGNORECASE)
|
| 1416 |
clean = re.sub(
|
| 1417 |
-
r'\b(find|search|show|get|give|
|
| 1418 |
-
r'|papers
|
| 1419 |
' ', clean, flags=re.IGNORECASE,
|
| 1420 |
)
|
| 1421 |
clean = re.sub(r'\s+', ' ', clean).strip()[:150]
|
| 1422 |
-
encoded =
|
| 1423 |
|
| 1424 |
select = "id,title,authorships,publication_year,primary_location,abstract_inverted_index,doi,cited_by_count,open_access"
|
| 1425 |
|
| 1426 |
-
# FIX: use ?search= β same as the clickable OpenAlex link
|
| 1427 |
url = (
|
| 1428 |
f"https://api.openalex.org/works"
|
| 1429 |
f"?search={encoded}"
|
|
@@ -1581,8 +1588,8 @@ async def _search_primo_catalog(context: SearchContextPayload, limit: int = 5) -
|
|
| 1581 |
boolean = clean_topic or context.topic
|
| 1582 |
result = await search_primo(
|
| 1583 |
SearchRequest(
|
| 1584 |
-
query=context.topic,
|
| 1585 |
-
boolean_query=boolean,
|
| 1586 |
source="primo",
|
| 1587 |
limit=limit,
|
| 1588 |
peer_reviewed=context.peer_reviewed,
|
|
@@ -2382,7 +2389,7 @@ async def _research_snapshot(context: SearchContextPayload, model: str) -> Tuple
|
|
| 2382 |
|
| 2383 |
# Parallel fetch: OpenAlex (fast, free) + PRIMO
|
| 2384 |
papers, (primo_result, primo_url) = await _asyncio.gather(
|
| 2385 |
-
_openalex_search(context.ai_tool_query or context.topic, limit=8),
|
| 2386 |
_search_primo_catalog(context, limit=6),
|
| 2387 |
)
|
| 2388 |
|
|
@@ -2814,7 +2821,7 @@ async def _run_search_mode(
|
|
| 2814 |
if use_openalex:
|
| 2815 |
(result, source_url), oa_papers = await _aio.gather(
|
| 2816 |
_search_pubmed_articles(context, limit=6),
|
| 2817 |
-
_openalex_search(context.ai_tool_query or context.topic, limit=5),
|
| 2818 |
)
|
| 2819 |
else:
|
| 2820 |
result, source_url = await _search_pubmed_articles(context, limit=6)
|
|
@@ -2824,7 +2831,7 @@ async def _run_search_mode(
|
|
| 2824 |
if use_openalex:
|
| 2825 |
(result, source_url), oa_papers = await _aio.gather(
|
| 2826 |
_search_primo_catalog(context, limit=6),
|
| 2827 |
-
_openalex_search(context.ai_tool_query or context.topic, limit=5),
|
| 2828 |
)
|
| 2829 |
else:
|
| 2830 |
result, source_url = await _search_primo_catalog(context, limit=6)
|
|
|
|
| 1219 |
- Max 3 AND groups. Each group max 4 OR terms.
|
| 1220 |
- NEVER put years as Boolean terms β put them in year_from/year_to
|
| 1221 |
- Keep broad enough to return results
|
| 1222 |
+
- NAMED ENTITIES: Any specific keyword that is a country, island, city, person,
|
| 1223 |
+
organisation, or proper noun MUST be kept as its own AND group. Never drop it.
|
| 1224 |
+
e.g. "Maldives" β AND ("Maldives" OR "Maldivian") ; "UAE" β AND ("UAE" OR "United Arab Emirates")
|
| 1225 |
|
| 1226 |
EXAMPLES:
|
| 1227 |
Input: "find articles on global warming and renewable energy"
|
|
|
|
| 1232 |
β boolean:("machine learning" OR "deep learning" OR "AI") AND ("healthcare" OR "medicine" OR "clinical")
|
| 1233 |
β year_from:"{CURRENT_YEAR - 5}", peer_reviewed:true
|
| 1234 |
|
| 1235 |
+
Input: "impact of global warming in Maldives"
|
| 1236 |
+
β boolean:("global warming" OR "climate change") AND ("Maldives" OR "sea level rise" OR "small island")
|
| 1237 |
+
β year_from:"", peer_reviewed:false
|
| 1238 |
+
|
| 1239 |
+
Input: "water scarcity solutions in UAE"
|
| 1240 |
+
β boolean:("water scarcity" OR "water stress") AND ("UAE" OR "United Arab Emirates" OR "Gulf region")
|
| 1241 |
+
β year_from:"", peer_reviewed:false
|
| 1242 |
+
|
| 1243 |
Input: "summarize shift in personalized genomic medicine from N-of-1 trials last 24 months"
|
| 1244 |
β boolean:("personalized genomic medicine" OR "precision medicine") AND ("N-of-1 trials" OR "scalable precision")
|
| 1245 |
β year_from:"{CURRENT_YEAR - 2}", peer_reviewed:true
|
|
|
|
| 1410 |
|
| 1411 |
async def _openalex_search(topic: str, limit: int = 8) -> List[dict]:
|
| 1412 |
"""
|
| 1413 |
+
Search OpenAlex using the ?search= relevance engine.
|
| 1414 |
+
This matches the clickable OpenAlex link shown to users.
|
| 1415 |
+
The previous ?filter=title.search:X|abstract.search:X approach was a strict
|
| 1416 |
+
field-level filter that returned irrelevant results. The ?search= parameter
|
| 1417 |
+
uses OpenAlex full relevance ranking across all fields.
|
|
|
|
|
|
|
| 1418 |
"""
|
| 1419 |
try:
|
| 1420 |
+
from urllib.parse import quote as _oa_quote
|
| 1421 |
|
| 1422 |
+
# Strip boolean operators and common retrieval filler words.
|
|
|
|
| 1423 |
clean = re.sub(r'\b(AND|OR|NOT)\b|[()"\']', ' ', topic, flags=re.IGNORECASE)
|
| 1424 |
clean = re.sub(
|
| 1425 |
+
r'\b(find|search|show|get|give|research on|articles on'
|
| 1426 |
+
r'|papers on|literature on|studies on|overview of|summary of|review of)\b',
|
| 1427 |
' ', clean, flags=re.IGNORECASE,
|
| 1428 |
)
|
| 1429 |
clean = re.sub(r'\s+', ' ', clean).strip()[:150]
|
| 1430 |
+
encoded = _oa_quote(clean, safe='')
|
| 1431 |
|
| 1432 |
select = "id,title,authorships,publication_year,primary_location,abstract_inverted_index,doi,cited_by_count,open_access"
|
| 1433 |
|
|
|
|
| 1434 |
url = (
|
| 1435 |
f"https://api.openalex.org/works"
|
| 1436 |
f"?search={encoded}"
|
|
|
|
| 1588 |
boolean = clean_topic or context.topic
|
| 1589 |
result = await search_primo(
|
| 1590 |
SearchRequest(
|
| 1591 |
+
query=context.topic,
|
| 1592 |
+
boolean_query=boolean,
|
| 1593 |
source="primo",
|
| 1594 |
limit=limit,
|
| 1595 |
peer_reviewed=context.peer_reviewed,
|
|
|
|
| 2389 |
|
| 2390 |
# Parallel fetch: OpenAlex (fast, free) + PRIMO
|
| 2391 |
papers, (primo_result, primo_url) = await _asyncio.gather(
|
| 2392 |
+
_openalex_search(context.ai_tool_query or context.topic, limit=8),
|
| 2393 |
_search_primo_catalog(context, limit=6),
|
| 2394 |
)
|
| 2395 |
|
|
|
|
| 2821 |
if use_openalex:
|
| 2822 |
(result, source_url), oa_papers = await _aio.gather(
|
| 2823 |
_search_pubmed_articles(context, limit=6),
|
| 2824 |
+
_openalex_search(context.ai_tool_query or context.topic, limit=5),
|
| 2825 |
)
|
| 2826 |
else:
|
| 2827 |
result, source_url = await _search_pubmed_articles(context, limit=6)
|
|
|
|
| 2831 |
if use_openalex:
|
| 2832 |
(result, source_url), oa_papers = await _aio.gather(
|
| 2833 |
_search_primo_catalog(context, limit=6),
|
| 2834 |
+
_openalex_search(context.ai_tool_query or context.topic, limit=5),
|
| 2835 |
)
|
| 2836 |
else:
|
| 2837 |
result, source_url = await _search_primo_catalog(context, limit=6)
|