Spaces:
Sleeping
Sleeping
| import re | |
| from typing import List, Dict, Any | |
| from app.core.config import settings | |
| BAD_ANSWER_MARKERS = [ | |
| "local llm generation failed", | |
| "i don't know", | |
| "i do not know", | |
| "unknown", | |
| "not enough information", | |
| "could not find", | |
| "cannot answer", | |
| "as an ai", | |
| "i am unable", | |
| "the evidence does not" | |
| ] | |
| def answer_has_citation(answer: str) -> bool: | |
| if not answer: | |
| return False | |
| return bool(re.search(r"\[S\d+\]", answer)) | |
| def answer_is_too_short(answer: str) -> bool: | |
| if not answer: | |
| return True | |
| return len(answer.strip().split()) < settings.MIN_LLM_ANSWER_WORDS | |
| def answer_repeats_prompt(answer: str) -> bool: | |
| answer_lower = answer.lower() | |
| prompt_markers = [ | |
| "you are a research assistant", | |
| "answer the question using", | |
| "sources:", | |
| "question:", | |
| "evidence:", | |
| "final answer:" | |
| ] | |
| return any(marker in answer_lower for marker in prompt_markers) | |
| def answer_has_bad_marker(answer: str) -> bool: | |
| answer_lower = answer.lower() | |
| return any(marker in answer_lower for marker in BAD_ANSWER_MARKERS) | |
| def answer_is_mostly_citation(answer: str) -> bool: | |
| without_citations = re.sub(r"\[S\d+\]", "", answer).strip() | |
| return len(without_citations.split()) < 8 | |
| def is_answer_good_enough(answer: str) -> bool: | |
| """ | |
| Quality gate for accepting LLM answer. | |
| If answer fails this, we use evidence-based fallback. | |
| """ | |
| if answer_is_too_short(answer): | |
| return False | |
| if answer_repeats_prompt(answer): | |
| return False | |
| if answer_has_bad_marker(answer): | |
| return False | |
| if answer_is_mostly_citation(answer): | |
| return False | |
| if not answer_has_citation(answer): | |
| return False | |
| return True | |
| def append_missing_citations(answer: str, sources: List[Dict[str, Any]]) -> str: | |
| """ | |
| If model gives a good explanation but forgets citations, | |
| append top citations. Quality checker still decides acceptance. | |
| """ | |
| if not answer: | |
| return answer | |
| if answer_has_citation(answer): | |
| return answer | |
| citation_ids = [] | |
| for source in sources[:2]: | |
| source_id = source.get("source_id") | |
| if source_id: | |
| citation_ids.append(f"[{source_id}]") | |
| if not citation_ids: | |
| return answer | |
| return answer.strip() + " " + " ".join(citation_ids) | |