Spaces:
Sleeping
Sleeping
""" | |
evo_inference.py — Step 8 (FLAN-optimized) | |
- Generative path uses a FLAN-friendly prompt: Instruction / Context / Question / Answer | |
- Filters placeholder chunks | |
- Cleans common prompt-echo lines | |
- Keeps labeled [Generative] / [Extractive] outputs with safe fallback | |
""" | |
from typing import List, Dict | |
import re | |
from utils_lang import L, normalize_lang | |
# Try to load your real Evo plugin first; else use the example; else None. | |
_GENERATOR = None | |
try: | |
from evo_plugin import load_model as _load_real # your future file (optional) | |
_GENERATOR = _load_real() | |
except Exception: | |
try: | |
from evo_plugin_example import load_model as _load_example | |
_GENERATOR = _load_example() | |
except Exception: | |
_GENERATOR = None # no generator available | |
MAX_SNIPPET_CHARS = 400 | |
def _snippet(text: str) -> str: | |
text = " ".join(text.split()) | |
return text[:MAX_SNIPPET_CHARS] + ("..." if len(text) > MAX_SNIPPET_CHARS else "") | |
def _extractive_answer(user_query: str, lang: str, hits: List[Dict]) -> str: | |
"""Old safe mode: show top snippets + standard steps, now labeled.""" | |
if not hits: | |
return "**[Extractive]**\n\n" + L(lang, "intro_err") | |
bullets = [f"- {_snippet(h['text'])}" for h in hits[:4]] | |
steps = { | |
"en": [ | |
"• Step 1: Check eligibility & gather required documents.", | |
"• Step 2: Confirm fees & payment options.", | |
"• Step 3: Apply online or at the indicated office.", | |
"• Step 4: Keep reference/receipt; track processing time.", | |
], | |
"fr": [ | |
"• Étape 1 : Vérifiez l’éligibilité et rassemblez les documents requis.", | |
"• Étape 2 : Confirmez les frais et les moyens de paiement.", | |
"• Étape 3 : Déposez la demande en ligne ou au bureau indiqué.", | |
"• Étape 4 : Conservez le reçu/la référence et suivez le délai de traitement.", | |
], | |
"mfe": [ | |
"• Step 1: Get dokiman neseser ek verifie si to elegib.", | |
"• Step 2: Konfirm fre ek manyer peyman.", | |
"• Step 3: Fer demand online ouswa dan biro ki indike.", | |
"• Step 4: Gard referans/reso; swiv letan tretman.", | |
], | |
}[normalize_lang(lang)] | |
return ( | |
"**[Extractive]**\n\n" | |
f"**{L(lang, 'intro_ok')}**\n\n" | |
f"**Q:** {user_query}\n\n" | |
f"**Key information:**\n" + "\n".join(bullets) + "\n\n" | |
f"**Suggested steps:**\n" + "\n".join(steps) | |
) | |
def _lang_name(code: str) -> str: | |
return {"en": "English", "fr": "French", "mfe": "Kreol Morisien"}.get(code, "English") | |
def _filter_hits(hits: List[Dict], keep: int = 6) -> List[Dict]: | |
""" | |
Prefer non-placeholder chunks; if all are placeholders, return originals. | |
""" | |
filtered = [ | |
h for h in hits | |
if "placeholder" not in h["text"].lower() and "disclaimer" not in h["text"].lower() | |
] | |
if not filtered: | |
filtered = hits | |
return filtered[:keep] | |
def _build_grounded_prompt(question: str, lang: str, hits: List[Dict]) -> str: | |
""" | |
FLAN-style prompt: | |
Instruction: ... | |
Context: | |
1) ... | |
2) ... | |
Question: ... | |
Answer: | |
""" | |
lang = normalize_lang(lang) | |
lang_readable = _lang_name(lang) | |
instruction = ( | |
"You are the Mauritius Government Copilot. Answer ONLY using the provided context. " | |
"If a detail is missing (fees, required docs, office or processing time), say so clearly. " | |
"Structure the answer as short bullet points with: Required documents, Fees, Where to apply, " | |
"Processing time, and Steps. Keep it concise (6–10 lines)." | |
) | |
if lang == "fr": | |
instruction = ( | |
"Tu es le Copilote Gouvernemental de Maurice. Réponds UNIQUEMENT à partir du contexte fourni. " | |
"Si une information manque (frais, documents requis, bureau ou délai), dis-le clairement. " | |
"Structure en puces courtes : Documents requis, Frais, Où postuler, Délai de traitement, Étapes. " | |
"Reste concis (6–10 lignes)." | |
) | |
elif lang == "mfe": | |
instruction = ( | |
"To enn Copilot Gouv Moris. Reponn zis lor konteks ki donn. " | |
"Si enn detay manke (fre, dokiman, biro, letan tretman), dir li kler. " | |
"Servi pwen kout: Dokiman, Fre, Kot pou al, Letan tretman, Steps. " | |
"Reste kout (6–10 ligner)." | |
) | |
chosen = _filter_hits(hits, keep=6) | |
ctx_lines = [f"{i+1}) {_snippet(h['text'])}" for i, h in enumerate(chosen)] | |
ctx_block = "\n".join(ctx_lines) if ctx_lines else "(none)" | |
prompt = ( | |
f"Instruction ({lang_readable}): {instruction}\n\n" | |
f"Context:\n{ctx_block}\n\n" | |
f"Question: {question}\n\n" | |
f"Answer ({lang_readable}):" | |
) | |
return prompt | |
_ECHO_PATTERNS = [ | |
r"^\s*Instruction.*$", r"^\s*Context:.*$", r"^\s*Question:.*$", r"^\s*Answer.*$", | |
r"^\s*\[Instructions?\].*$", r"^\s*Be concise.*$", r"^\s*Do not invent.*$", | |
r"^\s*(en|fr|mfe)\s*$", | |
] | |
def _clean_generated(text: str) -> str: | |
""" | |
Remove common echoed lines from the model output. | |
""" | |
lines = [ln.strip() for ln in text.strip().splitlines()] | |
out = [] | |
for ln in lines: | |
if any(re.match(pat, ln, flags=re.IGNORECASE) for pat in _ECHO_PATTERNS): | |
continue | |
out.append(ln) | |
cleaned = "\n".join(out).strip() | |
# extra guard: collapse repeated blank lines | |
cleaned = re.sub(r"\n{3,}", "\n\n", cleaned) | |
return cleaned | |
def synthesize_with_evo( | |
user_query: str, | |
lang: str, | |
hits: List[Dict], | |
mode: str = "extractive", | |
max_new_tokens: int = 192, | |
temperature: float = 0.4, | |
) -> str: | |
""" | |
If mode=='generative' and a generator exists, generate a grounded answer | |
(labeled [Generative]). Otherwise, return the labeled extractive fallback. | |
""" | |
lang = normalize_lang(lang) | |
# No retrieved context? Stay safe. | |
if not hits: | |
return _extractive_answer(user_query, lang, hits) | |
if mode != "generative" or _GENERATOR is None: | |
return _extractive_answer(user_query, lang, hits) | |
prompt = _build_grounded_prompt(user_query, lang, hits) | |
try: | |
text = _GENERATOR.generate( | |
prompt, | |
max_new_tokens=int(max_new_tokens), | |
temperature=float(temperature), | |
) | |
text = _clean_generated(text) | |
# Fallback if empty or suspiciously short | |
if not text or len(text) < 20: | |
return _extractive_answer(user_query, lang, hits) | |
return "**[Generative]**\n\n" + text | |
except Exception: | |
return _extractive_answer(user_query, lang, hits) | |