nikeshn commited on
Commit
a01c4fd
·
verified ·
1 Parent(s): e1a1b12

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +66 -210
app.py CHANGED
@@ -1,212 +1,68 @@
1
- from fastapi import APIRouter, HTTPException, Request
2
- from pydantic import BaseModel
3
- from typing import List, Optional, Dict
4
- import time
5
  import os
6
- import re
7
- import json
8
- from datetime import datetime
9
 
10
- # Import OpenAI for classification
11
- try:
12
- from langchain_openai import ChatOpenAI
13
- from langchain_anthropic import ChatAnthropic
14
- except ImportError:
15
- pass
16
-
17
- router = APIRouter()
18
-
19
- class ChatMessage(BaseModel):
20
- role: str
21
- content: str
22
-
23
- class AgentRequest(BaseModel):
24
- question: str
25
- model: str = "gpt"
26
- history: List[ChatMessage] = []
27
-
28
- class AgentResponse(BaseModel):
29
- answer: str
30
- intent: str = "general"
31
- tools_used: List[str] = []
32
- search_results: List[dict] = []
33
- sources: List[dict] = []
34
- model_used: str = "gpt"
35
- response_time: float = 0
36
-
37
- # ========== STAFF DIRECTORY DATA (from app81.py) ==========
38
- STAFF_DIRECTORY = [
39
- {
40
- "full_name": "Dr. Abdulla Al Hefeiti",
41
- "role": "Library Director / Assistant Provost, Libraries",
42
- "details": "He can help with strategic and institutional matters, library leadership, and partnerships. Email: abdulla.alhefeiti@ku.ac.ae | Phone: +971 2 312 3331",
43
- "tokens": ["abdulla", "hefeiti", "abdulla al hefeiti", "al hefeiti"],
44
- },
45
- {
46
- "full_name": "Nikesh Narayanan",
47
- "role": "Research & Access Services Librarian",
48
- "details": "He can help with research support, Khazna institutional repository, ORCID, SciVal, Scopus, research impact, AI tools for research, bibliometrics, scholarly communication, and LibGuides. Email: nikesh.narayanan@ku.ac.ae | Phone: +971 2 312 3980",
49
- "tokens": ["nikesh", "narayanan", "nikesh narayanan"],
50
- },
51
- {
52
- "full_name": "Jason Fetty",
53
- "role": "Medical Librarian",
54
- "details": "He can help with medical and health sciences research, systematic reviews, PubMed, Embase, CINAHL, UpToDate, and clinical databases. Email: jason.fetty@ku.ac.ae | Phone: +971 2 312 4722",
55
- "tokens": ["jason", "fetty", "jason fetty"],
56
- },
57
- {
58
- "full_name": "Walter Brian Hall",
59
- "role": "Digital & Technology Services Librarian / Systems Librarian",
60
- "details": "He can help with library website, systems, technology, digital infrastructure, ORCID, STEAM, coding, Open Access publishing, APC funding, general library services, and public services. Email: walter.hall@ku.ac.ae | Phone: +971 2 312 3163",
61
- "tokens": ["walter", "brian", "hall", "walter brian hall"],
62
- },
63
- ]
64
-
65
- # ========== REGEX PATTERNS (from app81.py) ==========
66
- SOCIAL_RE = re.compile(r"^(hi|hello|hey|good morning|good afternoon|good evening|how are you|who are you|what can you do|thanks|thank you|bye|goodbye)\b", re.IGNORECASE)
67
- PURE_GREETING_RE = re.compile(r"^(hi|hello|hey|good morning|good afternoon|good evening)[!.\s]*$", re.IGNORECASE)
68
- HOURS_RE = re.compile(r"\b(library hours?|opening hours?|when.*open|when.*close|open today|closed today)\b", re.IGNORECASE)
69
- LIBRARY_CUE_RE = re.compile(r"\b(library|librarian|borrow|primo|database|article|book|ill|interlibrary|full text)\b", re.IGNORECASE)
70
- MEDICAL_KEYWORDS = ['medicine','medical','health','clinical','nursing','pharmacy','pubmed','embase']
71
-
72
- _CLASSIFIER_SYSTEM = """You are the routing brain of LibBee...""" # [Truncated for brevity - copy from app81.py lines ~875-915]
73
-
74
- # ========== HELPER FUNCTIONS ==========
75
- def _normalize_name_query(text: str):
76
- return [t for t in re.sub(r"[^a-z0-9 ]+", " ", (text or "").lower()).split() if t]
77
-
78
- def _match_staff_name(question: str) -> Optional[Dict]:
79
- tokens = _normalize_name_query(question)
80
- if not tokens:
81
- return None
82
- for staff in STAFF_DIRECTORY:
83
- staff_tokens = set()
84
- for tok in staff.get("tokens", []):
85
- staff_tokens.update(_normalize_name_query(tok))
86
- if all(tok in staff_tokens for tok in tokens):
87
- return staff
88
- return None
89
-
90
- def _staff_name_answer(staff: dict) -> str:
91
- return f"Did you mean <strong>{staff['full_name']}</strong>?<br><br><strong>{staff['full_name']}</strong> is the <strong>{staff['role']}</strong>. {staff['details']}"
92
-
93
- def _looks_library_hours_question(question: str) -> bool:
94
- return bool(HOURS_RE.search(question.lower()))
95
-
96
- def _hours_redirect_answer() -> str:
97
- return """
98
- Here are the current <strong>Khalifa University Library</strong> hours:<br><br>
99
- <strong>Habshan Library (SAN Campus):</strong><br>
100
- Sunday – Wednesday: 8:00 AM – 7:00 PM<br>
101
- Thursday: 8:00 AM – 4:00 PM<br>
102
- Friday: Closed<br>
103
- Saturday: 12:00 PM – 8:00 PM<br><br>
104
- ⚠️ Check official page for latest times: <a href="https://library.ku.ac.ae/hours">library.ku.ac.ae/hours</a>
105
- """
106
-
107
- async def _llm_classify(question: str, history: list, model: str = "gpt"):
108
- """Copy this function from app81.py lines ~918-960"""
109
- # Simplified version for now:
110
- q = question.lower()
111
- if SOCIAL_RE.match(q):
112
- return {"intent": "social", "casual_answer": "Hello! I'm LibBee, the KU Library AI Assistant."}
113
- elif any(k in q for k in MEDICAL_KEYWORDS):
114
- return {"intent": "search_medical"}
115
- elif LIBRARY_CUE_RE.search(q):
116
- return {"intent": "library_info"}
117
- else:
118
- return {"intent": "general"}
119
-
120
- # ========== MAIN ENDPOINT ==========
121
- @router.post("", response_model=AgentResponse)
122
- async def agent_query(request: Request, req: AgentRequest):
123
- start = time.time()
124
-
125
- question = req.question.strip()
126
-
127
- # 1. Instant handlers (from app81.py Layer 0)
128
-
129
- # Staff name match
130
- staff_match = _match_staff_name(question)
131
- if staff_match:
132
- return AgentResponse(
133
- answer=_staff_name_answer(staff_match),
134
- intent="library_info",
135
- tools_used=["staff_name_match"],
136
- model_used=req.model,
137
- response_time=time.time() - start
138
- )
139
-
140
- # Hours question
141
- if _looks_library_hours_question(question):
142
- return AgentResponse(
143
- answer=_hours_redirect_answer(),
144
- intent="library_info",
145
- tools_used=["hours_hardcoded"],
146
- model_used=req.model,
147
- response_time=time.time() - start
148
- )
149
-
150
- # Greeting
151
- if PURE_GREETING_RE.match(question):
152
- return AgentResponse(
153
- answer="Hi! I'm <strong>LibBee</strong>, the Khalifa University Library AI Assistant. I can help you find articles and books, search databases, and answer library questions. What would you like help with?",
154
- intent="social_greeting",
155
- tools_used=[],
156
- model_used=req.model,
157
- response_time=time.time() - start
158
- )
159
-
160
- # 2. LLM Classification (Layer 1)
161
- try:
162
- classification = await _llm_classify(question, req.history, req.model)
163
- intent = classification.get("intent", "general")
164
-
165
- # 3. Route by intent
166
- if intent == "social":
167
- return AgentResponse(
168
- answer=classification.get("casual_answer", "Hello! How can I help?"),
169
- intent="social",
170
- model_used=req.model,
171
- response_time=time.time() - start
172
- )
173
-
174
- elif intent == "library_info":
175
- # For now, return generic library info response
176
- # Later: Add RAG here
177
- return AgentResponse(
178
- answer="I can help with library information. This is where your RAG logic will go.",
179
- intent="library_info",
180
- tools_used=["get_library_info"],
181
- model_used=req.model,
182
- response_time=time.time() - start
183
- )
184
-
185
- elif intent in ["search_academic", "search_medical"]:
186
- return AgentResponse(
187
- answer=f"I'll help you search for {'medical' if intent == 'search_medical' else 'academic'} literature on: {question}",
188
- intent=intent,
189
- tools_used=["search_primo"],
190
- model_used=req.model,
191
- response_time=time.time() - start
192
- )
193
-
194
- else:
195
- return AgentResponse(
196
- answer="I'm the KU Library AI Assistant. I can help you find articles, books, and answer library questions.",
197
- intent="general",
198
- model_used=req.model,
199
- response_time=time.time() - start
200
- )
201
-
202
- except Exception as e:
203
- return AgentResponse(
204
- answer=f"Error processing request: {str(e)}",
205
- intent="error",
206
- model_used=req.model,
207
- response_time=time.time() - start
208
- )
209
-
210
- @router.get("/test")
211
- async def test():
212
- return {"status": "ok", "message": "Agent is working!"}
 
1
+ """
2
+ LibBee - Main Application Entry Point
3
+ """
4
+ import sys
5
  import os
 
 
 
6
 
7
+ # Add current directory to path for src imports
8
+ sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
9
+
10
+ from fastapi import FastAPI
11
+ from fastapi.middleware.cors import CORSMiddleware
12
+
13
+ # Import your routers
14
+ from src.api import agent
15
+ from src.api import search
16
+ from src.api import admin
17
+
18
+ # Create the FastAPI app instance
19
+ app = FastAPI(
20
+ title="LibBee - KU Library AI",
21
+ description="Khalifa University Library AI Assistant",
22
+ version="2.0.0"
23
+ )
24
+
25
+ # CORS Configuration
26
+ app.add_middleware(
27
+ CORSMiddleware,
28
+ allow_origins=[
29
+ "https://nikeshn.github.io", # Your GitHub Pages
30
+ "http://localhost:8080",
31
+ "http://localhost:3000",
32
+ "http://127.0.0.1:5500"
33
+ ],
34
+ allow_credentials=True,
35
+ allow_methods=["POST", "GET", "OPTIONS"],
36
+ allow_headers=["*"],
37
+ max_age=86400,
38
+ )
39
+
40
+ # Include routers
41
+ app.include_router(agent.router, prefix="/agent", tags=["Agent"])
42
+ app.include_router(search.router, prefix="/search", tags=["Search"])
43
+ app.include_router(admin.router, prefix="/admin", tags=["Admin"])
44
+
45
+ @app.get("/")
46
+ def health_check():
47
+ return {
48
+ "status": "ok",
49
+ "version": "2.0.0",
50
+ "service": "LibBee KU Library AI",
51
+ "endpoints": ["/agent", "/search", "/admin"]
52
+ }
53
+
54
+ @app.get("/config")
55
+ def public_config():
56
+ return {
57
+ "welcome_message": "Hi! I'm LibBee, the Khalifa University Library AI Assistant.",
58
+ "default_model": "gpt",
59
+ "max_results": "5"
60
+ }
61
+
62
+ @app.get("/year")
63
+ def get_year():
64
+ from datetime import datetime
65
+ now = datetime.utcnow()
66
+ return {"year": now.year, "month": now.month, "date": now.strftime("%Y-%m-%d")}
67
+
68
+ print("✅ LibBee API loaded successfully")