Spaces:
Running
Running
Update app/app.py
Browse files- app/app.py +27 -5
app/app.py
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
|
|
| 1 |
import os
|
| 2 |
import json
|
| 3 |
import asyncio
|
|
@@ -100,6 +101,20 @@ class Feedback(BaseModel):
|
|
| 100 |
feedback: str
|
| 101 |
comment: str | None = None
|
| 102 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 103 |
# -----------------------------
|
| 104 |
# ✅ Endpoints
|
| 105 |
# -----------------------------
|
|
@@ -170,13 +185,23 @@ async def chat(query: Query, request: Request):
|
|
| 170 |
"answer": "Sorry, I could not find a relevant policy to answer that question. Please try rephrasing."
|
| 171 |
}
|
| 172 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 173 |
scores = [f"{result['relevance_score']:.4f}" for result in search_results]
|
| 174 |
adapter.info(f"Found {len(search_results)} relevant chunks with scores: {scores}")
|
| 175 |
|
| 176 |
# 2. Prepare Context
|
| 177 |
context_chunks = [result['text'] for result in search_results[:TOP_K_CONTEXT]]
|
| 178 |
context = "\n---\n".join(context_chunks)
|
| 179 |
-
|
| 180 |
# 3. Build Prompt with Separator Instruction
|
| 181 |
prompt = f"""<|system|>
|
| 182 |
You are a precise and factual assistant for NEEPCO's Delegation of Powers (DoP) policy.
|
|
@@ -187,6 +212,7 @@ Your task is to answer the user's question based ONLY on the provided context.
|
|
| 187 |
</s>
|
| 188 |
<|user|>
|
| 189 |
### Relevant Context:
|
|
|
|
| 190 |
```
|
| 191 |
{context}
|
| 192 |
```
|
|
@@ -212,14 +238,10 @@ Your task is to answer the user's question based ONLY on the provided context.
|
|
| 212 |
# Check if the model used the pipe separator, indicating a list.
|
| 213 |
if '|' in raw_answer:
|
| 214 |
adapter.info("Pipe separator found. Formatting response as a bulleted list.")
|
| 215 |
-
# Split the string into a list of items
|
| 216 |
items = raw_answer.split('|')
|
| 217 |
-
# Clean up each item and format it as a bullet point
|
| 218 |
cleaned_items = [f"* {item.strip()}" for item in items if item.strip()]
|
| 219 |
-
# Join them back together with newlines
|
| 220 |
answer = "\n".join(cleaned_items)
|
| 221 |
else:
|
| 222 |
-
# If no separator, use the answer as is.
|
| 223 |
answer = raw_answer
|
| 224 |
|
| 225 |
except asyncio.TimeoutError:
|
|
|
|
| 1 |
+
//app.py
|
| 2 |
import os
|
| 3 |
import json
|
| 4 |
import asyncio
|
|
|
|
| 101 |
feedback: str
|
| 102 |
comment: str | None = None
|
| 103 |
|
| 104 |
+
# -----------------------------
|
| 105 |
+
# ✅ Helper for metadata-based filtering
|
| 106 |
+
# -----------------------------
|
| 107 |
+
def is_personnel_related(metadata: dict) -> bool:
|
| 108 |
+
# Keywords indicating personnel or HR related policy sections
|
| 109 |
+
personnel_keywords = [
|
| 110 |
+
"ii", "personnel", "recruitment", "resignation", "hr", "promotion",
|
| 111 |
+
"employee", "staff", "service", "termination", "transfer"
|
| 112 |
+
]
|
| 113 |
+
section = str(metadata.get("section", "")).lower()
|
| 114 |
+
title = str(metadata.get("title", "")).lower()
|
| 115 |
+
# Return True if any keyword is found in section or title
|
| 116 |
+
return any(kw in section for kw in personnel_keywords) or any(kw in title for kw in personnel_keywords)
|
| 117 |
+
|
| 118 |
# -----------------------------
|
| 119 |
# ✅ Endpoints
|
| 120 |
# -----------------------------
|
|
|
|
| 185 |
"answer": "Sorry, I could not find a relevant policy to answer that question. Please try rephrasing."
|
| 186 |
}
|
| 187 |
|
| 188 |
+
# Post-search metadata-based filtering for personnel/HR queries
|
| 189 |
+
if any(keyword in question_lower for keyword in ["personnel", "hr", "recruitment", "resignation",
|
| 190 |
+
"promotion", "employee", "termination", "transfer"]):
|
| 191 |
+
filtered_results = [res for res in search_results if is_personnel_related(res.get('metadata', {}))]
|
| 192 |
+
if filtered_results:
|
| 193 |
+
adapter.info(f"Filtered {len(search_results) - len(filtered_results)} irrelevant chunks for personnel query.")
|
| 194 |
+
search_results = filtered_results
|
| 195 |
+
else:
|
| 196 |
+
adapter.info("No personnel-related chunks found after filtering; using unfiltered results.")
|
| 197 |
+
|
| 198 |
scores = [f"{result['relevance_score']:.4f}" for result in search_results]
|
| 199 |
adapter.info(f"Found {len(search_results)} relevant chunks with scores: {scores}")
|
| 200 |
|
| 201 |
# 2. Prepare Context
|
| 202 |
context_chunks = [result['text'] for result in search_results[:TOP_K_CONTEXT]]
|
| 203 |
context = "\n---\n".join(context_chunks)
|
| 204 |
+
|
| 205 |
# 3. Build Prompt with Separator Instruction
|
| 206 |
prompt = f"""<|system|>
|
| 207 |
You are a precise and factual assistant for NEEPCO's Delegation of Powers (DoP) policy.
|
|
|
|
| 212 |
</s>
|
| 213 |
<|user|>
|
| 214 |
### Relevant Context:
|
| 215 |
+
|
| 216 |
```
|
| 217 |
{context}
|
| 218 |
```
|
|
|
|
| 238 |
# Check if the model used the pipe separator, indicating a list.
|
| 239 |
if '|' in raw_answer:
|
| 240 |
adapter.info("Pipe separator found. Formatting response as a bulleted list.")
|
|
|
|
| 241 |
items = raw_answer.split('|')
|
|
|
|
| 242 |
cleaned_items = [f"* {item.strip()}" for item in items if item.strip()]
|
|
|
|
| 243 |
answer = "\n".join(cleaned_items)
|
| 244 |
else:
|
|
|
|
| 245 |
answer = raw_answer
|
| 246 |
|
| 247 |
except asyncio.TimeoutError:
|