Spaces:
Sleeping
Sleeping
Create evo_inference.py
Browse files- evo_inference.py +112 -44
evo_inference.py
CHANGED
@@ -1,62 +1,97 @@
|
|
1 |
"""
|
2 |
evo_inference.py
|
3 |
-
Step
|
4 |
|
5 |
(Objective)
|
6 |
-
-
|
7 |
-
|
8 |
-
-
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
1) Load your Evo weights once at module import time.
|
13 |
-
2) Build a prompt with the top retrieved chunks.
|
14 |
-
3) Generate a response (max_new_tokens ~ 200–300).
|
15 |
-
4) Always include the key fields users expect (docs required, fees, where to apply, timing).
|
16 |
-
5) Return the generated text.
|
17 |
"""
|
18 |
|
19 |
-
from typing import List, Dict
|
20 |
from utils_lang import L, normalize_lang
|
21 |
|
22 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
23 |
|
|
|
24 |
|
25 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
26 |
"""
|
27 |
-
(Objective)
|
|
|
|
|
|
|
28 |
"""
|
29 |
-
|
30 |
-
if
|
31 |
-
|
32 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
33 |
|
|
|
|
|
|
|
34 |
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
40 |
|
41 |
-
Inputs:
|
42 |
-
user_query: the user's question (string)
|
43 |
-
lang: 'en' | 'fr' | 'mfe'
|
44 |
-
hits: list of dicts with keys: 'text', 'meta', 'score'
|
45 |
|
46 |
-
|
47 |
-
|
|
|
48 |
"""
|
49 |
-
lang = normalize_lang(lang)
|
50 |
-
|
51 |
if not hits:
|
52 |
return L(lang, "intro_err")
|
53 |
|
54 |
-
|
55 |
-
|
56 |
-
|
57 |
|
58 |
-
|
59 |
-
headings = {
|
60 |
"en": [
|
61 |
"• Step 1: Check eligibility & gather required documents.",
|
62 |
"• Step 2: Confirm fees & payment options.",
|
@@ -75,14 +110,47 @@ def synthesize_with_evo(user_query: str, lang: str, hits: List[Dict]) -> str:
|
|
75 |
"• Step 3: Fer demand online ouswa dan biro ki indike.",
|
76 |
"• Step 4: Gard referans/reso; swiv letan tretman.",
|
77 |
],
|
78 |
-
}[lang]
|
79 |
|
80 |
-
# Compose final answer (Objective)
|
81 |
-
intro = L(lang, "intro_ok")
|
82 |
md = (
|
83 |
-
f"**{
|
84 |
f"**Q:** {user_query}\n\n"
|
85 |
-
f"**Key information:**\n
|
86 |
-
f"**Suggested steps:**\n" + "\n".join(
|
87 |
)
|
88 |
return md
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
"""
|
2 |
evo_inference.py
|
3 |
+
Step 8: Evo-ready synthesis with plugin support + safe fallback.
|
4 |
|
5 |
(Objective)
|
6 |
+
- Try to import your real Evo plugin: `evo_plugin.load_model()`.
|
7 |
+
- If not present, fall back to `evo_plugin_example.load_model()`.
|
8 |
+
- Provide `synthesize_with_evo(...)` that builds a grounded prompt from retrieved hits
|
9 |
+
and either:
|
10 |
+
a) calls the generator (generative mode), or
|
11 |
+
b) returns a clean extractive answer (safe mode).
|
|
|
|
|
|
|
|
|
|
|
12 |
"""
|
13 |
|
14 |
+
from typing import List, Dict, Optional
|
15 |
from utils_lang import L, normalize_lang
|
16 |
|
17 |
+
# Try to load your real Evo plugin first; else example plugin
|
18 |
+
_GENERATOR = None
|
19 |
+
try:
|
20 |
+
from evo_plugin import load_model as _load_real # your file (optional)
|
21 |
+
_GENERATOR = _load_real()
|
22 |
+
except Exception:
|
23 |
+
try:
|
24 |
+
from evo_plugin_example import load_model as _load_example
|
25 |
+
_GENERATOR = _load_example()
|
26 |
+
except Exception:
|
27 |
+
_GENERATOR = None # fallback to extractive-only
|
28 |
|
29 |
+
MAX_SNIPPET_CHARS = 400 # (Objective) keep context concise
|
30 |
|
31 |
+
|
32 |
+
def _snippet(text: str) -> str:
|
33 |
+
text = " ".join(text.split())
|
34 |
+
return text[:MAX_SNIPPET_CHARS] + ("..." if len(text) > MAX_SNIPPET_CHARS else "")
|
35 |
+
|
36 |
+
|
37 |
+
def _build_grounded_prompt(question: str, lang: str, hits: List[Dict]) -> str:
|
38 |
"""
|
39 |
+
(Objective) Construct a compact prompt that includes:
|
40 |
+
- role + response style instruction (language-aware),
|
41 |
+
- the user's question,
|
42 |
+
- the top retrieved chunks as "Context #i".
|
43 |
"""
|
44 |
+
lang = normalize_lang(lang)
|
45 |
+
if lang == "fr":
|
46 |
+
system = (
|
47 |
+
"Tu es le Copilote Gouvernemental de Maurice. Réponds dans la langue demandée, "
|
48 |
+
"clairement et étape par étape, en te basant STRICTEMENT sur le contexte. "
|
49 |
+
"Inclure: documents requis, frais, où postuler, délais. Si une info manque, dis-le."
|
50 |
+
)
|
51 |
+
elif lang == "mfe":
|
52 |
+
system = (
|
53 |
+
"To enn Copilot Gouv Moris. Reponn dan langaz itilizater, kler ek pas-a-pas, "
|
54 |
+
"bas lor KI SUIVAN. Met: ki dokiman bizin, fre, kot pou al, delai. "
|
55 |
+
"Si pa ase info, dir li."
|
56 |
+
)
|
57 |
+
else:
|
58 |
+
system = (
|
59 |
+
"You are the Mauritius Government Copilot. Answer in the user's language, "
|
60 |
+
"clearly and step-by-step, using ONLY the provided context. Include: required documents, "
|
61 |
+
"fees, where to apply, processing time. If something is missing, say so."
|
62 |
+
)
|
63 |
|
64 |
+
ctx_lines = []
|
65 |
+
for i, h in enumerate(hits[:6], 1):
|
66 |
+
ctx_lines.append(f"[Context #{i}] { _snippet(h['text']) }")
|
67 |
|
68 |
+
ctx_block = "\n".join(ctx_lines) if ctx_lines else "[Context] (none)"
|
69 |
+
prompt = (
|
70 |
+
f"{system}\n\n"
|
71 |
+
f"[Question]\n{question}\n\n"
|
72 |
+
f"{ctx_block}\n\n"
|
73 |
+
f"[Instructions]\n"
|
74 |
+
f"- Be concise (6–10 lines).\n"
|
75 |
+
f"- Use bullet steps.\n"
|
76 |
+
f"- Do not invent links or fees; mention if unknown.\n"
|
77 |
+
f"- Answer in language code: {lang}.\n"
|
78 |
+
f"[Answer]\n"
|
79 |
+
)
|
80 |
+
return prompt
|
81 |
|
|
|
|
|
|
|
|
|
82 |
|
83 |
+
def _extractive_answer(user_query: str, lang: str, hits: List[Dict]) -> str:
|
84 |
+
"""
|
85 |
+
(Objective) The safe fallback: bullet points from hits + standard steps.
|
86 |
"""
|
|
|
|
|
87 |
if not hits:
|
88 |
return L(lang, "intro_err")
|
89 |
|
90 |
+
bullets = []
|
91 |
+
for h in hits[:4]:
|
92 |
+
bullets.append(f"- {_snippet(h['text'])}")
|
93 |
|
94 |
+
steps = {
|
|
|
95 |
"en": [
|
96 |
"• Step 1: Check eligibility & gather required documents.",
|
97 |
"• Step 2: Confirm fees & payment options.",
|
|
|
110 |
"• Step 3: Fer demand online ouswa dan biro ki indike.",
|
111 |
"• Step 4: Gard referans/reso; swiv letan tretman.",
|
112 |
],
|
113 |
+
}[normalize_lang(lang)]
|
114 |
|
|
|
|
|
115 |
md = (
|
116 |
+
f"**{L(lang, 'intro_ok')}**\n\n"
|
117 |
f"**Q:** {user_query}\n\n"
|
118 |
+
f"**Key information:**\n" + "\n".join(bullets) + "\n\n"
|
119 |
+
f"**Suggested steps:**\n" + "\n".join(steps)
|
120 |
)
|
121 |
return md
|
122 |
+
|
123 |
+
|
124 |
+
def synthesize_with_evo(
|
125 |
+
user_query: str,
|
126 |
+
lang: str,
|
127 |
+
hits: List[Dict],
|
128 |
+
mode: str = "extractive", # "extractive" | "generative"
|
129 |
+
max_new_tokens: int = 192,
|
130 |
+
temperature: float = 0.4,
|
131 |
+
) -> str:
|
132 |
+
"""
|
133 |
+
(Objective)
|
134 |
+
- If mode == 'generative' and a generator is available, build a grounded prompt and generate.
|
135 |
+
- Else, return the safe extractive answer.
|
136 |
+
"""
|
137 |
+
lang = normalize_lang(lang)
|
138 |
+
|
139 |
+
if mode != "generative" or _GENERATOR is None:
|
140 |
+
return _extractive_answer(user_query, lang, hits)
|
141 |
+
|
142 |
+
prompt = _build_grounded_prompt(user_query, lang, hits)
|
143 |
+
try:
|
144 |
+
text = _GENERATOR.generate(
|
145 |
+
prompt=prompt,
|
146 |
+
max_new_tokens=int(max_new_tokens),
|
147 |
+
temperature=float(temperature),
|
148 |
+
)
|
149 |
+
# In case the generator echos or misses structure, still return something readable
|
150 |
+
text = text.strip()
|
151 |
+
if not text:
|
152 |
+
return _extractive_answer(user_query, lang, hits)
|
153 |
+
return text
|
154 |
+
except Exception:
|
155 |
+
# Any runtime issue falls back to safe mode
|
156 |
+
return _extractive_answer(user_query, lang, hits)
|