Spaces:
Sleeping
Sleeping
Update evo_inference.py
Browse files- evo_inference.py +91 -36
evo_inference.py
CHANGED
@@ -1,25 +1,19 @@
|
|
1 |
"""
|
2 |
-
evo_inference.py — Step 8 (
|
3 |
-
|
4 |
-
|
5 |
-
|
6 |
-
|
7 |
-
- Answers are explicitly labeled **[Generative]** or **[Extractive]** so you
|
8 |
-
can tell which path ran at a glance.
|
9 |
-
|
10 |
-
How it works:
|
11 |
-
- We try to import your real evo plugin (evo_plugin.py). If not found, we load
|
12 |
-
evo_plugin_example.py instead. If both fail, we stay in extractive mode.
|
13 |
-
- synthesize_with_evo(...) accepts mode/temp/max_tokens from the UI.
|
14 |
"""
|
15 |
|
16 |
from typing import List, Dict
|
|
|
17 |
from utils_lang import L, normalize_lang
|
18 |
|
19 |
# Try to load your real Evo plugin first; else use the example; else None.
|
20 |
_GENERATOR = None
|
21 |
try:
|
22 |
-
from evo_plugin import load_model as _load_real #
|
23 |
_GENERATOR = _load_real()
|
24 |
except Exception:
|
25 |
try:
|
@@ -72,27 +66,90 @@ def _extractive_answer(user_query: str, lang: str, hits: List[Dict]) -> str:
|
|
72 |
)
|
73 |
|
74 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
75 |
def _build_grounded_prompt(question: str, lang: str, hits: List[Dict]) -> str:
|
76 |
-
"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
77 |
lang = normalize_lang(lang)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
78 |
if lang == "fr":
|
79 |
-
|
80 |
-
|
81 |
-
|
|
|
|
|
|
|
82 |
elif lang == "mfe":
|
83 |
-
|
84 |
-
|
85 |
-
|
86 |
-
|
87 |
-
|
88 |
-
|
89 |
-
|
90 |
-
|
91 |
-
|
92 |
-
|
93 |
-
|
94 |
-
|
|
|
|
|
|
|
|
|
95 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
96 |
|
97 |
|
98 |
def synthesize_with_evo(
|
@@ -105,7 +162,7 @@ def synthesize_with_evo(
|
|
105 |
) -> str:
|
106 |
"""
|
107 |
If mode=='generative' and a generator exists, generate a grounded answer
|
108 |
-
|
109 |
"""
|
110 |
lang = normalize_lang(lang)
|
111 |
|
@@ -122,13 +179,11 @@ def synthesize_with_evo(
|
|
122 |
prompt,
|
123 |
max_new_tokens=int(max_new_tokens),
|
124 |
temperature=float(temperature),
|
125 |
-
)
|
126 |
-
|
127 |
-
if
|
|
|
128 |
return _extractive_answer(user_query, lang, hits)
|
129 |
-
|
130 |
return "**[Generative]**\n\n" + text
|
131 |
-
|
132 |
except Exception:
|
133 |
-
# Any runtime issue falls back to safe mode
|
134 |
return _extractive_answer(user_query, lang, hits)
|
|
|
1 |
"""
|
2 |
+
evo_inference.py — Step 8 (FLAN-optimized)
|
3 |
+
- Generative path uses a FLAN-friendly prompt: Instruction / Context / Question / Answer
|
4 |
+
- Filters placeholder chunks
|
5 |
+
- Cleans common prompt-echo lines
|
6 |
+
- Keeps labeled [Generative] / [Extractive] outputs with safe fallback
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
7 |
"""
|
8 |
|
9 |
from typing import List, Dict
|
10 |
+
import re
|
11 |
from utils_lang import L, normalize_lang
|
12 |
|
13 |
# Try to load your real Evo plugin first; else use the example; else None.
|
14 |
_GENERATOR = None
|
15 |
try:
|
16 |
+
from evo_plugin import load_model as _load_real # your future file (optional)
|
17 |
_GENERATOR = _load_real()
|
18 |
except Exception:
|
19 |
try:
|
|
|
66 |
)
|
67 |
|
68 |
|
69 |
+
def _lang_name(code: str) -> str:
|
70 |
+
return {"en": "English", "fr": "French", "mfe": "Kreol Morisien"}.get(code, "English")
|
71 |
+
|
72 |
+
|
73 |
+
def _filter_hits(hits: List[Dict], keep: int = 6) -> List[Dict]:
|
74 |
+
"""
|
75 |
+
Prefer non-placeholder chunks; if all are placeholders, return originals.
|
76 |
+
"""
|
77 |
+
filtered = [
|
78 |
+
h for h in hits
|
79 |
+
if "placeholder" not in h["text"].lower() and "disclaimer" not in h["text"].lower()
|
80 |
+
]
|
81 |
+
if not filtered:
|
82 |
+
filtered = hits
|
83 |
+
return filtered[:keep]
|
84 |
+
|
85 |
+
|
86 |
def _build_grounded_prompt(question: str, lang: str, hits: List[Dict]) -> str:
|
87 |
+
"""
|
88 |
+
FLAN-style prompt:
|
89 |
+
Instruction: ...
|
90 |
+
Context:
|
91 |
+
1) ...
|
92 |
+
2) ...
|
93 |
+
Question: ...
|
94 |
+
Answer:
|
95 |
+
"""
|
96 |
lang = normalize_lang(lang)
|
97 |
+
lang_readable = _lang_name(lang)
|
98 |
+
|
99 |
+
instruction = (
|
100 |
+
"You are the Mauritius Government Copilot. Answer ONLY using the provided context. "
|
101 |
+
"If a detail is missing (fees, required docs, office or processing time), say so clearly. "
|
102 |
+
"Structure the answer as short bullet points with: Required documents, Fees, Where to apply, "
|
103 |
+
"Processing time, and Steps. Keep it concise (6–10 lines)."
|
104 |
+
)
|
105 |
if lang == "fr":
|
106 |
+
instruction = (
|
107 |
+
"Tu es le Copilote Gouvernemental de Maurice. Réponds UNIQUEMENT à partir du contexte fourni. "
|
108 |
+
"Si une information manque (frais, documents requis, bureau ou délai), dis-le clairement. "
|
109 |
+
"Structure en puces courtes : Documents requis, Frais, Où postuler, Délai de traitement, Étapes. "
|
110 |
+
"Reste concis (6–10 lignes)."
|
111 |
+
)
|
112 |
elif lang == "mfe":
|
113 |
+
instruction = (
|
114 |
+
"To enn Copilot Gouv Moris. Reponn zis lor konteks ki donn. "
|
115 |
+
"Si enn detay manke (fre, dokiman, biro, letan tretman), dir li kler. "
|
116 |
+
"Servi pwen kout: Dokiman, Fre, Kot pou al, Letan tretman, Steps. "
|
117 |
+
"Reste kout (6–10 ligner)."
|
118 |
+
)
|
119 |
+
|
120 |
+
chosen = _filter_hits(hits, keep=6)
|
121 |
+
ctx_lines = [f"{i+1}) {_snippet(h['text'])}" for i, h in enumerate(chosen)]
|
122 |
+
ctx_block = "\n".join(ctx_lines) if ctx_lines else "(none)"
|
123 |
+
|
124 |
+
prompt = (
|
125 |
+
f"Instruction ({lang_readable}): {instruction}\n\n"
|
126 |
+
f"Context:\n{ctx_block}\n\n"
|
127 |
+
f"Question: {question}\n\n"
|
128 |
+
f"Answer ({lang_readable}):"
|
129 |
)
|
130 |
+
return prompt
|
131 |
+
|
132 |
+
|
133 |
+
_ECHO_PATTERNS = [
|
134 |
+
r"^\s*Instruction.*$", r"^\s*Context:.*$", r"^\s*Question:.*$", r"^\s*Answer.*$",
|
135 |
+
r"^\s*\[Instructions?\].*$", r"^\s*Be concise.*$", r"^\s*Do not invent.*$",
|
136 |
+
r"^\s*(en|fr|mfe)\s*$",
|
137 |
+
]
|
138 |
+
|
139 |
+
def _clean_generated(text: str) -> str:
|
140 |
+
"""
|
141 |
+
Remove common echoed lines from the model output.
|
142 |
+
"""
|
143 |
+
lines = [ln.strip() for ln in text.strip().splitlines()]
|
144 |
+
out = []
|
145 |
+
for ln in lines:
|
146 |
+
if any(re.match(pat, ln, flags=re.IGNORECASE) for pat in _ECHO_PATTERNS):
|
147 |
+
continue
|
148 |
+
out.append(ln)
|
149 |
+
cleaned = "\n".join(out).strip()
|
150 |
+
# extra guard: collapse repeated blank lines
|
151 |
+
cleaned = re.sub(r"\n{3,}", "\n\n", cleaned)
|
152 |
+
return cleaned
|
153 |
|
154 |
|
155 |
def synthesize_with_evo(
|
|
|
162 |
) -> str:
|
163 |
"""
|
164 |
If mode=='generative' and a generator exists, generate a grounded answer
|
165 |
+
(labeled [Generative]). Otherwise, return the labeled extractive fallback.
|
166 |
"""
|
167 |
lang = normalize_lang(lang)
|
168 |
|
|
|
179 |
prompt,
|
180 |
max_new_tokens=int(max_new_tokens),
|
181 |
temperature=float(temperature),
|
182 |
+
)
|
183 |
+
text = _clean_generated(text)
|
184 |
+
# Fallback if empty or suspiciously short
|
185 |
+
if not text or len(text) < 20:
|
186 |
return _extractive_answer(user_query, lang, hits)
|
|
|
187 |
return "**[Generative]**\n\n" + text
|
|
|
188 |
except Exception:
|
|
|
189 |
return _extractive_answer(user_query, lang, hits)
|