Spaces:
Sleeping
Sleeping
Update services/kb_creation.py
Browse files- services/kb_creation.py +16 -21
services/kb_creation.py
CHANGED
|
@@ -93,7 +93,6 @@ ERROR_TERMS = ["error", "issue", "fail", "failure", "not working", "cannot", "ca
|
|
| 93 |
|
| 94 |
STEP_VERBS = ["navigate", "select", "scan", "verify", "confirm", "print", "move", "complete", "click", "open", "choose", "enter", "update", "save", "delete", "create", "attach", "assign"]
|
| 95 |
|
| 96 |
-
# Module vocabulary → canonical tags
|
| 97 |
MODULE_VOCAB = {
|
| 98 |
"picking": ["pick", "picking", "pick release", "wave", "allocation"],
|
| 99 |
"receiving": ["receive", "receiving", "inbound", "asn", "appointment"],
|
|
@@ -116,17 +115,14 @@ def _infer_intent_tag(section_title: str) -> str:
|
|
| 116 |
return "neutral"
|
| 117 |
|
| 118 |
def _derive_semantic_intent_from_text(text: str) -> Tuple[str, List[str]]:
|
| 119 |
-
"""Return ('errors'|'steps'|'neutral', topic_tags) by scanning the text."""
|
| 120 |
t = (text or "").lower()
|
| 121 |
tags: List[str] = []
|
| 122 |
intent = "neutral"
|
| 123 |
if any(term in t for term in PERMISSION_TERMS):
|
| 124 |
intent = "errors"
|
| 125 |
tags.append("permissions")
|
| 126 |
-
if "role" in t:
|
| 127 |
-
|
| 128 |
-
if "security" in t:
|
| 129 |
-
tags.append("security")
|
| 130 |
if intent == "neutral" and any(term in t for term in ERROR_TERMS):
|
| 131 |
intent = "errors"
|
| 132 |
tags.append("errors")
|
|
@@ -141,7 +137,6 @@ def _derive_module_tags(text: str, filename: str, section_title: str) -> List[st
|
|
| 141 |
for mod, syns in MODULE_VOCAB.items():
|
| 142 |
if any(s in tokens for s in syns):
|
| 143 |
found.append(mod)
|
| 144 |
-
# default: if none found, try broad guess
|
| 145 |
if not found:
|
| 146 |
if "inventory" in tokens or "adjust" in tokens or "uom" in tokens or "cycle" in tokens:
|
| 147 |
found = ["inventory"]
|
|
@@ -175,7 +170,6 @@ def ingest_documents(folder_path: str) -> None:
|
|
| 175 |
final_intent = "errors"
|
| 176 |
elif base_intent == "neutral" and derived_intent in ("steps", "prereqs"):
|
| 177 |
final_intent = derived_intent
|
| 178 |
-
|
| 179 |
module_tags = _derive_module_tags(chunk, file, section_title)
|
| 180 |
|
| 181 |
embedding = model.encode(chunk).tolist()
|
|
@@ -186,13 +180,13 @@ def ingest_documents(folder_path: str) -> None:
|
|
| 186 |
"chunk_index": c_idx,
|
| 187 |
"title": doc_title,
|
| 188 |
"collection": "SOP",
|
| 189 |
-
"intent_tag": final_intent,
|
| 190 |
-
"topic_tags": topic_tags,
|
| 191 |
-
"module_tags": module_tags,
|
| 192 |
}
|
| 193 |
try:
|
| 194 |
collection.add(ids=[doc_id], embeddings=[embedding], documents=[chunk], metadatas=[meta])
|
| 195 |
-
except Exception:
|
| 196 |
try:
|
| 197 |
collection.delete(ids=[doc_id])
|
| 198 |
collection.add(ids=[doc_id], embeddings=[embedding], documents=[chunk], metadatas=[meta])
|
|
@@ -374,7 +368,6 @@ def _extract_modules_from_query(query: str) -> List[str]:
|
|
| 374 |
for mod, syns in MODULE_VOCAB.items():
|
| 375 |
if any(s in q for s in syns):
|
| 376 |
found.append(mod)
|
| 377 |
-
# broad guess if user says 'inventory'
|
| 378 |
if not found and ("inventory" in q or "adjust" in q):
|
| 379 |
found = ["inventory"]
|
| 380 |
return list(sorted(set(found)))
|
|
@@ -388,15 +381,17 @@ def _intent_weight(meta: dict, user_intent: str) -> float:
|
|
| 388 |
if tag in ["purpose", "prereqs"] and user_intent in ["steps", "errors"]:
|
| 389 |
return -0.6
|
| 390 |
st = (meta or {}).get("section", "").lower()
|
| 391 |
-
topics = (meta or {}).get("topic_tags",
|
| 392 |
-
|
|
|
|
| 393 |
return 0.7
|
| 394 |
return -0.2
|
| 395 |
|
| 396 |
def _module_weight(meta: Dict[str, Any], user_modules: List[str]) -> float:
|
| 397 |
if not user_modules:
|
| 398 |
return 0.0
|
| 399 |
-
|
|
|
|
| 400 |
overlap = len(set(user_modules) & set(doc_modules))
|
| 401 |
if overlap == 0:
|
| 402 |
return -0.4 # demote different modules to avoid wrong SOP
|
|
@@ -406,8 +401,8 @@ def _meta_overlap(meta: Dict[str, Any], q_terms: List[str]) -> float:
|
|
| 406 |
fn_tokens = _tokenize_meta_value(meta.get("filename"))
|
| 407 |
title_tokens = _tokenize_meta_value(meta.get("title"))
|
| 408 |
section_tokens = _tokenize_meta_value(meta.get("section"))
|
| 409 |
-
topic_tokens = _tokenize_meta_value(
|
| 410 |
-
module_tokens = _tokenize_meta_value(
|
| 411 |
meta_tokens = set(fn_tokens + title_tokens + section_tokens + topic_tokens + module_tokens)
|
| 412 |
if not meta_tokens or not q_terms:
|
| 413 |
return 0.0
|
|
@@ -570,19 +565,19 @@ def get_best_steps_section_text(filename: str) -> str:
|
|
| 570 |
return "\n\n".join(texts).strip()
|
| 571 |
|
| 572 |
def get_best_errors_section_text(filename: str) -> str:
|
| 573 |
-
"""Return combined text of all error/permission/escalation chunks for the given SOP."""
|
| 574 |
texts: List[str] = []
|
| 575 |
for d in bm25_docs:
|
| 576 |
m = d.get("meta", {})
|
| 577 |
sec = (m.get("section") or "").lower()
|
| 578 |
-
topics = (m.get("topic_tags") or
|
|
|
|
| 579 |
if m.get("filename") == filename and (
|
| 580 |
m.get("intent_tag") == "errors"
|
| 581 |
or "error" in sec
|
| 582 |
or "escalation" in sec
|
| 583 |
or "permission" in sec
|
| 584 |
or "access" in sec
|
| 585 |
-
or ("permissions" in
|
| 586 |
):
|
| 587 |
t = (d.get("text") or "").strip()
|
| 588 |
if t:
|
|
|
|
| 93 |
|
| 94 |
STEP_VERBS = ["navigate", "select", "scan", "verify", "confirm", "print", "move", "complete", "click", "open", "choose", "enter", "update", "save", "delete", "create", "attach", "assign"]
|
| 95 |
|
|
|
|
| 96 |
MODULE_VOCAB = {
|
| 97 |
"picking": ["pick", "picking", "pick release", "wave", "allocation"],
|
| 98 |
"receiving": ["receive", "receiving", "inbound", "asn", "appointment"],
|
|
|
|
| 115 |
return "neutral"
|
| 116 |
|
| 117 |
def _derive_semantic_intent_from_text(text: str) -> Tuple[str, List[str]]:
|
|
|
|
| 118 |
t = (text or "").lower()
|
| 119 |
tags: List[str] = []
|
| 120 |
intent = "neutral"
|
| 121 |
if any(term in t for term in PERMISSION_TERMS):
|
| 122 |
intent = "errors"
|
| 123 |
tags.append("permissions")
|
| 124 |
+
if "role" in t: tags.append("role_access")
|
| 125 |
+
if "security" in t: tags.append("security")
|
|
|
|
|
|
|
| 126 |
if intent == "neutral" and any(term in t for term in ERROR_TERMS):
|
| 127 |
intent = "errors"
|
| 128 |
tags.append("errors")
|
|
|
|
| 137 |
for mod, syns in MODULE_VOCAB.items():
|
| 138 |
if any(s in tokens for s in syns):
|
| 139 |
found.append(mod)
|
|
|
|
| 140 |
if not found:
|
| 141 |
if "inventory" in tokens or "adjust" in tokens or "uom" in tokens or "cycle" in tokens:
|
| 142 |
found = ["inventory"]
|
|
|
|
| 170 |
final_intent = "errors"
|
| 171 |
elif base_intent == "neutral" and derived_intent in ("steps", "prereqs"):
|
| 172 |
final_intent = derived_intent
|
|
|
|
| 173 |
module_tags = _derive_module_tags(chunk, file, section_title)
|
| 174 |
|
| 175 |
embedding = model.encode(chunk).tolist()
|
|
|
|
| 180 |
"chunk_index": c_idx,
|
| 181 |
"title": doc_title,
|
| 182 |
"collection": "SOP",
|
| 183 |
+
"intent_tag": final_intent, # str
|
| 184 |
+
"topic_tags": ", ".join(topic_tags) if topic_tags else "", # str (NOT list)
|
| 185 |
+
"module_tags": ", ".join(module_tags) if module_tags else "", # str (NOT list)
|
| 186 |
}
|
| 187 |
try:
|
| 188 |
collection.add(ids=[doc_id], embeddings=[embedding], documents=[chunk], metadatas=[meta])
|
| 189 |
+
except Exception as e1:
|
| 190 |
try:
|
| 191 |
collection.delete(ids=[doc_id])
|
| 192 |
collection.add(ids=[doc_id], embeddings=[embedding], documents=[chunk], metadatas=[meta])
|
|
|
|
| 368 |
for mod, syns in MODULE_VOCAB.items():
|
| 369 |
if any(s in q for s in syns):
|
| 370 |
found.append(mod)
|
|
|
|
| 371 |
if not found and ("inventory" in q or "adjust" in q):
|
| 372 |
found = ["inventory"]
|
| 373 |
return list(sorted(set(found)))
|
|
|
|
| 381 |
if tag in ["purpose", "prereqs"] and user_intent in ["steps", "errors"]:
|
| 382 |
return -0.6
|
| 383 |
st = (meta or {}).get("section", "").lower()
|
| 384 |
+
topics = (meta or {}).get("topic_tags", "") or ""
|
| 385 |
+
topic_list = [t.strip() for t in topics.split(",") if t.strip()]
|
| 386 |
+
if user_intent == "errors" and (any(k in st for k in ["escalation", "permissions", "access", "known issues"]) or ("permissions" in topic_list)):
|
| 387 |
return 0.7
|
| 388 |
return -0.2
|
| 389 |
|
| 390 |
def _module_weight(meta: Dict[str, Any], user_modules: List[str]) -> float:
|
| 391 |
if not user_modules:
|
| 392 |
return 0.0
|
| 393 |
+
raw = (meta or {}).get("module_tags", "") or ""
|
| 394 |
+
doc_modules = [m.strip() for m in raw.split(",") if m.strip()] if isinstance(raw, str) else (raw or [])
|
| 395 |
overlap = len(set(user_modules) & set(doc_modules))
|
| 396 |
if overlap == 0:
|
| 397 |
return -0.4 # demote different modules to avoid wrong SOP
|
|
|
|
| 401 |
fn_tokens = _tokenize_meta_value(meta.get("filename"))
|
| 402 |
title_tokens = _tokenize_meta_value(meta.get("title"))
|
| 403 |
section_tokens = _tokenize_meta_value(meta.get("section"))
|
| 404 |
+
topic_tokens = _tokenize_meta_value((meta.get("topic_tags") or ""))
|
| 405 |
+
module_tokens = _tokenize_meta_value((meta.get("module_tags") or ""))
|
| 406 |
meta_tokens = set(fn_tokens + title_tokens + section_tokens + topic_tokens + module_tokens)
|
| 407 |
if not meta_tokens or not q_terms:
|
| 408 |
return 0.0
|
|
|
|
| 565 |
return "\n\n".join(texts).strip()
|
| 566 |
|
| 567 |
def get_best_errors_section_text(filename: str) -> str:
|
|
|
|
| 568 |
texts: List[str] = []
|
| 569 |
for d in bm25_docs:
|
| 570 |
m = d.get("meta", {})
|
| 571 |
sec = (m.get("section") or "").lower()
|
| 572 |
+
topics = (m.get("topic_tags") or "")
|
| 573 |
+
topic_list = [t.strip() for t in topics.split(",") if t.strip()]
|
| 574 |
if m.get("filename") == filename and (
|
| 575 |
m.get("intent_tag") == "errors"
|
| 576 |
or "error" in sec
|
| 577 |
or "escalation" in sec
|
| 578 |
or "permission" in sec
|
| 579 |
or "access" in sec
|
| 580 |
+
or ("permissions" in topic_list)
|
| 581 |
):
|
| 582 |
t = (d.get("text") or "").strip()
|
| 583 |
if t:
|