Update app.py
Browse files
app.py
CHANGED
|
@@ -118,12 +118,13 @@ def set_config(key, value):
|
|
| 118 |
conn.commit()
|
| 119 |
conn.close()
|
| 120 |
|
|
|
|
| 121 |
def get_behavior_instructions():
|
| 122 |
base = get_config("bot_personality", "").strip()
|
| 123 |
extra = get_config("custom_instructions", "").strip()
|
| 124 |
if base and extra:
|
| 125 |
-
return
|
| 126 |
-
return base or extra or ""
|
| 127 |
|
| 128 |
|
| 129 |
|
|
@@ -419,32 +420,107 @@ async def tool_library_info(question, history=None, model="gpt"):
|
|
| 419 |
# Common synonym mappings for library staff/service terms
|
| 420 |
# This improves FAISS retrieval when users use informal terms
|
| 421 |
SYNONYMS = {
|
| 422 |
-
"research librarian": "Research and Access Services Librarian Nikesh Narayanan",
|
| 423 |
"who is the librarian": "library staff contacts librarian",
|
| 424 |
-
"subject librarian": "Research and Access Services Librarian",
|
| 425 |
-
"medical librarian": "Jason Fetty Medical Librarian",
|
| 426 |
-
"systems librarian": "Walter Hall Digital Technology Services Librarian",
|
| 427 |
-
"
|
| 428 |
-
"
|
| 429 |
-
"
|
| 430 |
-
"
|
| 431 |
-
"
|
| 432 |
-
"
|
| 433 |
-
"
|
| 434 |
-
"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 435 |
"reference manager": "RefWorks citation bibliography",
|
| 436 |
"researcher id": "ORCID researcher identifier profile",
|
| 437 |
-
"impact factor": "journal citation reports SciVal",
|
| 438 |
-
"systematic review": "Cochrane Embase CINAHL PICO",
|
| 439 |
-
"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 440 |
}
|
| 441 |
question_lower = question.lower()
|
| 442 |
expanded_query = base_query
|
|
|
|
| 443 |
for term, expansion in SYNONYMS.items():
|
| 444 |
if term in question_lower:
|
| 445 |
-
|
| 446 |
-
|
| 447 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 448 |
|
| 449 |
# ── FAISS scored search ──
|
| 450 |
docs_with_scores = vectorstore.similarity_search_with_score(expanded_query, k=TOP_K)
|
|
@@ -473,7 +549,10 @@ async def tool_library_info(question, history=None, model="gpt"):
|
|
| 473 |
then give the answer.""" if moderate_match else ""
|
| 474 |
|
| 475 |
behavior = get_behavior_instructions()
|
| 476 |
-
prompt = f"""{behavior
|
|
|
|
|
|
|
|
|
|
| 477 |
|
| 478 |
RULES — follow exactly:
|
| 479 |
1. Answer ONLY using the context provided below.
|
|
@@ -778,7 +857,8 @@ async def _answer_general(question: str, history=None) -> dict:
|
|
| 778 |
import openai
|
| 779 |
client = openai.OpenAI(api_key=os.environ.get("OPENAI_API_KEY"))
|
| 780 |
|
| 781 |
-
|
|
|
|
| 782 |
if history:
|
| 783 |
for m in history[-5:]:
|
| 784 |
role = m.get("role", "user")
|
|
@@ -787,15 +867,11 @@ async def _answer_general(question: str, history=None) -> dict:
|
|
| 787 |
messages.append({"role": role, "content": msg_content})
|
| 788 |
|
| 789 |
messages.append({"role": "user", "content": question})
|
| 790 |
-
|
| 791 |
-
|
| 792 |
-
"
|
| 793 |
-
|
| 794 |
-
|
| 795 |
-
}
|
| 796 |
-
if behavior:
|
| 797 |
-
kwargs["instructions"] = behavior
|
| 798 |
-
response = client.responses.create(**kwargs)
|
| 799 |
|
| 800 |
answer = ""
|
| 801 |
sources = []
|
|
@@ -817,10 +893,7 @@ async def _answer_general(question: str, history=None) -> dict:
|
|
| 817 |
print(f"Web search failed, falling back to plain GPT: {e}")
|
| 818 |
try:
|
| 819 |
llm = ChatOpenAI(model="gpt-4o-mini", temperature=0.3, max_tokens=500)
|
| 820 |
-
msgs = []
|
| 821 |
-
behavior = get_behavior_instructions()
|
| 822 |
-
if behavior:
|
| 823 |
-
msgs.append({"role": "system", "content": behavior})
|
| 824 |
for m in (history or [])[-5:]:
|
| 825 |
if m.get("role") in ("user", "assistant"):
|
| 826 |
msgs.append({"role": m["role"], "content": m.get("content", "")})
|
|
@@ -1041,8 +1114,7 @@ Return JSON like: {{"intent":"general_recent"}}"""
|
|
| 1041 |
context_parts.append(f"Natural query for AI tools: {natural_query}")
|
| 1042 |
context_parts.append(f"Database query for PRIMO/PubMed: {database_query}")
|
| 1043 |
|
| 1044 |
-
|
| 1045 |
-
synthesis_prompt = f"""{behavior if behavior else 'You are the Khalifa University Library AI Assistant (Abu Dhabi, UAE). KU = Khalifa University.'}
|
| 1046 |
Be concise (3-5 sentences).
|
| 1047 |
- For library_info, answer from the library context and include URLs when useful.
|
| 1048 |
- For search intents, briefly summarise the search direction and mention the top 2-3 relevant results if present.
|
|
@@ -1543,4 +1615,4 @@ async def clear_logs():
|
|
| 1543 |
conn.execute("DELETE FROM queries")
|
| 1544 |
conn.commit()
|
| 1545 |
conn.close()
|
| 1546 |
-
return {"status": "ok", "message": "All logs cleared"}
|
|
|
|
| 118 |
conn.commit()
|
| 119 |
conn.close()
|
| 120 |
|
| 121 |
+
|
| 122 |
def get_behavior_instructions():
|
| 123 |
base = get_config("bot_personality", "").strip()
|
| 124 |
extra = get_config("custom_instructions", "").strip()
|
| 125 |
if base and extra:
|
| 126 |
+
return base + "\n\nAdditional instructions:\n" + extra
|
| 127 |
+
return base or extra or "You are a helpful, friendly, and knowledgeable library assistant at Khalifa University, Abu Dhabi, UAE. KU = Khalifa University, NOT Kuwait University. Be concise and include relevant URLs when useful."
|
| 128 |
|
| 129 |
|
| 130 |
|
|
|
|
| 420 |
# Common synonym mappings for library staff/service terms
|
| 421 |
# This improves FAISS retrieval when users use informal terms
|
| 422 |
SYNONYMS = {
|
| 423 |
+
"research librarian": "Research and Access Services Librarian Nikesh Narayanan research support",
|
| 424 |
"who is the librarian": "library staff contacts librarian",
|
| 425 |
+
"subject librarian": "Research and Access Services Librarian Nikesh Narayanan",
|
| 426 |
+
"medical librarian": "Jason Fetty Medical Librarian PubMed Embase CINAHL UpToDate",
|
| 427 |
+
"systems librarian": "Walter Brian Hall Digital Technology Services Librarian website systems technology",
|
| 428 |
+
"website issue": "Walter Brian Hall website systems technology",
|
| 429 |
+
"technology issue": "Walter Brian Hall website systems technology",
|
| 430 |
+
"acquisitions librarian": "Alia Al-Harrasi acquisitions collection development request title",
|
| 431 |
+
"public services": "Muna Ahmad Al Blooshi public services circulation general library services",
|
| 432 |
+
"e-resources librarian": "Rani Anand e-resources databases access problems vendor issues",
|
| 433 |
+
"database access": "Rani Anand e-resources databases access problems",
|
| 434 |
+
"open access": "Nikesh Narayanan open access APC scholarly communication",
|
| 435 |
+
"orcid": "Nikesh Narayanan ORCID researcher identifier profile",
|
| 436 |
+
"research impact": "Nikesh Narayanan research impact SciVal Scopus bibliometrics",
|
| 437 |
+
"library director": "Dr Abdulla Al Hefeiti Assistant Provost Libraries",
|
| 438 |
+
"ebook": "ebook central proquest download ebooks",
|
| 439 |
+
"ebooks": "ebook central proquest download ebooks",
|
| 440 |
+
"how to borrow": "borrowing loan period renew circulation",
|
| 441 |
+
"borrow books": "borrowing loan period renew circulation",
|
| 442 |
+
"renew book": "renew borrowing circulation My Library Account",
|
| 443 |
+
"renew loan": "renew borrowing circulation My Library Account",
|
| 444 |
+
"hold item": "request hold checked out item My Library Account",
|
| 445 |
+
"reserve item": "request hold checked out item My Library Account",
|
| 446 |
+
"access from home": "remote access off campus proxy no VPN",
|
| 447 |
+
"off campus": "remote access off campus proxy no VPN",
|
| 448 |
+
"remote access": "remote access off campus proxy no VPN",
|
| 449 |
+
"cite": "RefWorks citation reference management bibliography",
|
| 450 |
+
"citation": "RefWorks citation reference management bibliography",
|
| 451 |
"reference manager": "RefWorks citation bibliography",
|
| 452 |
"researcher id": "ORCID researcher identifier profile",
|
| 453 |
+
"impact factor": "journal citation reports JCR Scopus CiteScore SciVal",
|
| 454 |
+
"systematic review": "Cochrane Embase CINAHL PICO PubMed evidence based medicine",
|
| 455 |
+
"evidence based medicine": "Cochrane PubMed Embase CINAHL UpToDate",
|
| 456 |
+
"ai tools": "LeapSpace Scopus AI ScienceDirect AI EBSCO Research AI PRIMO AI",
|
| 457 |
+
"inter library loan": "interlibrary loan ILL document delivery article request",
|
| 458 |
+
"interlibrary loan": "interlibrary loan ILL document delivery article request",
|
| 459 |
+
"ill": "interlibrary loan ILL document delivery article request",
|
| 460 |
+
"document delivery": "interlibrary loan ILL document delivery article request",
|
| 461 |
+
"borrow from another library": "interlibrary loan ILL document delivery",
|
| 462 |
+
"request from another library": "interlibrary loan ILL document delivery",
|
| 463 |
+
"article not available": "full text article not available interlibrary loan LibKey Nomad",
|
| 464 |
+
"can't get article": "full text article not available interlibrary loan LibKey Nomad",
|
| 465 |
+
"cannot get article": "full text article not available interlibrary loan LibKey Nomad",
|
| 466 |
+
"don't get article": "full text article not available interlibrary loan LibKey Nomad",
|
| 467 |
+
"full text unavailable": "full text not available interlibrary loan LibKey Nomad",
|
| 468 |
+
"no full text": "full text not available interlibrary loan LibKey Nomad",
|
| 469 |
+
"full text": "full text LibKey Nomad article access PDF download",
|
| 470 |
+
"pdf": "full text LibKey Nomad article access PDF download",
|
| 471 |
+
"get article": "full text article access LibKey Nomad interlibrary loan",
|
| 472 |
+
"catalog": "PRIMO library catalog discovery holdings publication finder",
|
| 473 |
+
"library catalog": "PRIMO library catalog discovery holdings publication finder",
|
| 474 |
+
"discovery": "PRIMO library catalog discovery holdings publication finder",
|
| 475 |
+
"holdings": "PRIMO library catalog discovery holdings publication finder",
|
| 476 |
+
"does the library have": "PRIMO library catalog holdings publication finder",
|
| 477 |
+
"find journal": "PRIMO publication finder journal access holdings",
|
| 478 |
+
"publication finder": "PRIMO publication finder journal access holdings",
|
| 479 |
+
"circulation": "borrowing renew return hold loan period My Library Account",
|
| 480 |
+
"due date": "borrowing renew due date My Library Account",
|
| 481 |
+
"reserve room": "study room reserve rooms booking",
|
| 482 |
+
"book room": "study room reserve rooms booking",
|
| 483 |
+
"study room": "study room reserve rooms booking",
|
| 484 |
+
"ask librarian": "Ask a Librarian research help reference consultation",
|
| 485 |
+
"research help": "Ask a Librarian research consultation reference help",
|
| 486 |
+
"consultation": "Ask a Librarian research consultation reference help",
|
| 487 |
+
"peer reviewed": "peer reviewed scholarly journal article database filters",
|
| 488 |
+
"scholarly article": "peer reviewed scholarly journal article database filters",
|
| 489 |
+
"abstract": "article abstract citation database search results",
|
| 490 |
+
"doi": "DOI article identifier full text citation",
|
| 491 |
+
"call number": "PRIMO call number browse book location",
|
| 492 |
+
"course reserve": "reserve course materials reserve collection",
|
| 493 |
+
"visitor": "visitors external visitors access library",
|
| 494 |
+
"alumni": "alumni services library access alumni",
|
| 495 |
+
"civil engineering": "ASCE Library Knovel ScienceDirect Scopus ASTM Compass",
|
| 496 |
+
"mechanical engineering": "ASME Digital Library Knovel ScienceDirect ASTM Compass",
|
| 497 |
+
"electrical engineering": "IEEE Xplore INSPEC ScienceDirect",
|
| 498 |
+
"computer science": "ACM Digital Library IEEE Xplore arXiv ScienceDirect",
|
| 499 |
+
"artificial intelligence": "ACM Digital Library IEEE Xplore arXiv ScienceDirect Scopus",
|
| 500 |
+
"medicine": "PubMed Embase Cochrane UpToDate CINAHL",
|
| 501 |
+
"nursing": "CINAHL PubMed nursing allied health",
|
| 502 |
+
"chemistry": "ACS SciFindern RSC Journals Reaxys",
|
| 503 |
+
"physics": "APS Journals AIP IOPScience",
|
| 504 |
+
"business": "Business Source Complete Emerald ProQuest",
|
| 505 |
+
"dissertations": "ProQuest Dissertations Khazna theses",
|
| 506 |
+
"theses": "ProQuest Dissertations Khazna theses",
|
| 507 |
+
"standards": "ASTM Compass IEEE standards ASME ASCE",
|
| 508 |
}
|
| 509 |
question_lower = question.lower()
|
| 510 |
expanded_query = base_query
|
| 511 |
+
matched_expansions = []
|
| 512 |
for term, expansion in SYNONYMS.items():
|
| 513 |
if term in question_lower:
|
| 514 |
+
matched_expansions.append(expansion)
|
| 515 |
+
if matched_expansions:
|
| 516 |
+
unique_expansions = []
|
| 517 |
+
seen_exp = set()
|
| 518 |
+
for exp in matched_expansions:
|
| 519 |
+
if exp not in seen_exp:
|
| 520 |
+
seen_exp.add(exp)
|
| 521 |
+
unique_expansions.append(exp)
|
| 522 |
+
expanded_query = f"{base_query} {' '.join(unique_expansions[:4])}"
|
| 523 |
+
print(f"Query expanded with {len(unique_expansions[:4])} glossary hints")
|
| 524 |
|
| 525 |
# ── FAISS scored search ──
|
| 526 |
docs_with_scores = vectorstore.similarity_search_with_score(expanded_query, k=TOP_K)
|
|
|
|
| 549 |
then give the answer.""" if moderate_match else ""
|
| 550 |
|
| 551 |
behavior = get_behavior_instructions()
|
| 552 |
+
prompt = f"""{behavior}
|
| 553 |
+
|
| 554 |
+
You are the Khalifa University Library AI Assistant in Abu Dhabi, UAE.
|
| 555 |
+
KU means Khalifa University, NOT Kuwait University.
|
| 556 |
|
| 557 |
RULES — follow exactly:
|
| 558 |
1. Answer ONLY using the context provided below.
|
|
|
|
| 857 |
import openai
|
| 858 |
client = openai.OpenAI(api_key=os.environ.get("OPENAI_API_KEY"))
|
| 859 |
|
| 860 |
+
behavior = get_behavior_instructions()
|
| 861 |
+
messages = [{"role": "system", "content": behavior}]
|
| 862 |
if history:
|
| 863 |
for m in history[-5:]:
|
| 864 |
role = m.get("role", "user")
|
|
|
|
| 867 |
messages.append({"role": role, "content": msg_content})
|
| 868 |
|
| 869 |
messages.append({"role": "user", "content": question})
|
| 870 |
+
response = client.responses.create(
|
| 871 |
+
model="gpt-4o-mini",
|
| 872 |
+
tools=[{"type": "web_search_preview"}],
|
| 873 |
+
input=messages,
|
| 874 |
+
)
|
|
|
|
|
|
|
|
|
|
|
|
|
| 875 |
|
| 876 |
answer = ""
|
| 877 |
sources = []
|
|
|
|
| 893 |
print(f"Web search failed, falling back to plain GPT: {e}")
|
| 894 |
try:
|
| 895 |
llm = ChatOpenAI(model="gpt-4o-mini", temperature=0.3, max_tokens=500)
|
| 896 |
+
msgs = [{"role": "system", "content": get_behavior_instructions()}]
|
|
|
|
|
|
|
|
|
|
| 897 |
for m in (history or [])[-5:]:
|
| 898 |
if m.get("role") in ("user", "assistant"):
|
| 899 |
msgs.append({"role": m["role"], "content": m.get("content", "")})
|
|
|
|
| 1114 |
context_parts.append(f"Natural query for AI tools: {natural_query}")
|
| 1115 |
context_parts.append(f"Database query for PRIMO/PubMed: {database_query}")
|
| 1116 |
|
| 1117 |
+
synthesis_prompt = f"""You are the Khalifa University Library AI Assistant (Abu Dhabi, UAE). KU = Khalifa University.
|
|
|
|
| 1118 |
Be concise (3-5 sentences).
|
| 1119 |
- For library_info, answer from the library context and include URLs when useful.
|
| 1120 |
- For search intents, briefly summarise the search direction and mention the top 2-3 relevant results if present.
|
|
|
|
| 1615 |
conn.execute("DELETE FROM queries")
|
| 1616 |
conn.commit()
|
| 1617 |
conn.close()
|
| 1618 |
+
return {"status": "ok", "message": "All logs cleared"}
|