| import sqlite3 |
| import difflib |
| import os |
| from src.utils import setup_logger |
|
|
| logger = setup_logger("FAQ_DB") |
|
|
| DB_PATH = "faq_cache.db" |
|
|
| class FAQManager: |
| def __init__(self): |
| self.init_db() |
|
|
| def get_conn(self): |
| return sqlite3.connect(DB_PATH, check_same_thread=False) |
|
|
| def init_db(self): |
| try: |
| with self.get_conn() as conn: |
| cursor = conn.cursor() |
| cursor.execute(''' |
| CREATE TABLE IF NOT EXISTS faq ( |
| id INTEGER PRIMARY KEY AUTOINCREMENT, |
| question_norm TEXT UNIQUE, |
| original_question TEXT, |
| answer_eng TEXT, |
| answer_sat TEXT, |
| source TEXT, |
| timestamp DATETIME DEFAULT CURRENT_TIMESTAMP |
| ) |
| ''') |
| conn.commit() |
| |
| |
| cursor.execute("PRAGMA table_info(faq)") |
| cols = [c[1] for c in cursor.fetchall()] |
| if 'answer_eng' not in cols: |
| logger.info("Migrating FAQ DB: Adding language columns...") |
| cursor.execute("ALTER TABLE faq ADD COLUMN answer_eng TEXT") |
| cursor.execute("ALTER TABLE faq ADD COLUMN answer_sat TEXT") |
| |
| |
| |
| cursor.execute("UPDATE faq SET answer_eng = answer") |
| conn.commit() |
| except Exception as e: |
| logger.error(f"FAQ DB Init Error: {e}") |
|
|
| def normalize(self, text): |
| |
| return ' '.join(text.lower().strip().split()) |
|
|
| def get_answer(self, question): |
| """ |
| Tries to find answer in DB. Returns (eng, sat) tuple or None. |
| 1. Exact match on normalized question. |
| 2. Fuzzy match using difflib. |
| """ |
| clean_q = self.normalize(question) |
| if not clean_q: return None |
| |
| try: |
| with self.get_conn() as conn: |
| cursor = conn.cursor() |
| |
| |
| cursor.execute("SELECT answer_eng, answer_sat FROM faq WHERE question_norm = ?", (clean_q,)) |
| row = cursor.fetchone() |
| if row: |
| logger.info(f"FAQ Exact Hit: {clean_q}") |
| return {"eng": row[0], "sat": row[1], "match_type": "EXACT"} |
| |
| |
| cursor.execute("SELECT question_norm, answer_eng, answer_sat FROM faq") |
| all_rows = cursor.fetchall() |
| if not all_rows: return None |
| |
| questions = [r[0] for r in all_rows] |
| matches = difflib.get_close_matches(clean_q, questions, n=1, cutoff=0.8) |
| |
| if matches: |
| best_match_q = matches[0] |
| for r in all_rows: |
| if r[0] == best_match_q: |
| logger.info(f"FAQ Fuzzy Hit: '{clean_q}' ~= '{best_match_q}'") |
| return {"eng": r[1], "sat": r[2], "match_type": "FUZZY"} |
| |
| except Exception as e: |
| logger.error(f"FAQ Read Error: {e}") |
| return None |
| return None |
|
|
| def add_entry(self, question, answer_eng, answer_sat, source="LLM"): |
| """ |
| Caches a new Q/A pair with both languages. |
| """ |
| clean_q = self.normalize(question) |
| if not answer_eng and not answer_sat: return |
| |
| try: |
| with self.get_conn() as conn: |
| cursor = conn.cursor() |
| |
| |
| |
| |
| cursor.execute("SELECT id FROM faq WHERE question_norm = ?", (clean_q,)) |
| row = cursor.fetchone() |
| |
| if row: |
| |
| cursor.execute(''' |
| UPDATE faq SET answer_eng = ?, answer_sat = ?, source = ? |
| WHERE question_norm = ? |
| ''', (answer_eng, answer_sat, source, clean_q)) |
| else: |
| |
| cursor.execute(''' |
| INSERT INTO faq (question_norm, original_question, answer_eng, answer_sat, source) |
| VALUES (?, ?, ?, ?, ?) |
| ''', (clean_q, question, answer_eng, answer_sat, source)) |
| conn.commit() |
| logger.info(f"FAQ Cached: {clean_q}") |
| except Exception as e: |
| logger.error(f"FAQ Write Error: {e}") |
|
|