Spaces:
Sleeping
Sleeping
Nikolay Ponomarev commited on
Commit ·
410e024
1
Parent(s): 42bde7a
Item Search
Browse files
app.py
CHANGED
|
@@ -13,6 +13,8 @@ DEFAULT_INTENT_MODEL = os.getenv("INTENT_MODEL", "joeddav/xlm-roberta-large-xnli
|
|
| 13 |
|
| 14 |
# 2) Checklist generator (instruct)
|
| 15 |
DEFAULT_GEN_MODEL = os.getenv("GEN_MODEL", "Qwen/Qwen2.5-0.5B-Instruct")
|
|
|
|
|
|
|
| 16 |
|
| 17 |
# 3) QA over checklist
|
| 18 |
DEFAULT_QA_MODEL = os.getenv("QA_MODEL", "deepset/xlm-roberta-base-squad2")
|
|
@@ -107,16 +109,23 @@ def build_checklist_prompt(user_goal: str, theme: str | None, style: str, constr
|
|
| 107 |
"Составь практичный чек-лист на русском языке.\n"
|
| 108 |
"Верни ТОЛЬКО чек-лист без вступлений.\n"
|
| 109 |
"Запрещено:\n"
|
|
|
|
| 110 |
"- плейсхолдеры и шаблоны: никаких '[секунды]', '[какой-то текст]', '{...}', '<...>'\n"
|
| 111 |
-
"- таблицы, 'Расчёт', 'Банк', 'Сообщение', поля для заполнения\n"
|
| 112 |
"Формат строго:\n"
|
| 113 |
-
"- [ ] пункт\n"
|
| 114 |
" - подпункт (если нужно)\n"
|
|
|
|
| 115 |
"Требования:\n"
|
| 116 |
f"- стиль: {style_hint}\n"
|
| 117 |
"- 12–18 пунктов\n"
|
| 118 |
-
"- без нумерации (никаких '1.')\n"
|
|
|
|
|
|
|
| 119 |
"- пункты конкретные и выполнимые\n"
|
|
|
|
|
|
|
|
|
|
| 120 |
"- в конце 2 блока:\n"
|
| 121 |
"Проверка готовности:\n"
|
| 122 |
"- ... (3–5 вопросов)\n"
|
|
@@ -146,24 +155,47 @@ def clean_checklist(text: str) -> str:
|
|
| 146 |
text = re.sub(r"<extra_id_\d+>", "", text).strip()
|
| 147 |
|
| 148 |
cleaned = []
|
|
|
|
|
|
|
| 149 |
for ln in text.splitlines():
|
| 150 |
s = ln.rstrip()
|
| 151 |
-
|
| 152 |
if not s.strip():
|
| 153 |
continue
|
| 154 |
|
| 155 |
-
# Cut obvious "form-like" garbage blocks
|
| 156 |
low = s.strip().lower()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 157 |
if low.startswith(("расч", "банк", "сообщение", "время ", "план поездки", "расход")):
|
| 158 |
continue
|
| 159 |
|
| 160 |
-
# Remove square-bracket placeholders but keep "[ ]" checkbox
|
|
|
|
| 161 |
s2 = re.sub(r"\[(?!\s*\])([^\]]+)\]", "", s).strip()
|
| 162 |
|
| 163 |
-
#
|
|
|
|
|
|
|
|
|
|
| 164 |
s2 = s2.replace("•", "-").replace("–", "-")
|
|
|
|
| 165 |
|
| 166 |
-
# Keep only checklist
|
| 167 |
if s2.startswith("- [ ]") or s2.startswith(" -") or s2.startswith("Проверка готовности") or s2.startswith("Риски"):
|
| 168 |
cleaned.append(s2)
|
| 169 |
|
|
@@ -174,38 +206,132 @@ def clean_checklist(text: str) -> str:
|
|
| 174 |
raw = [l.strip() for l in text.splitlines() if l.strip()]
|
| 175 |
raw = [re.sub(r"^\d+[\).\s]+", "", r).strip(" -•\t") for r in raw]
|
| 176 |
raw = [r for r in raw if r]
|
| 177 |
-
raw = [f"- [ ] {r}" for r in raw[:18]]
|
| 178 |
out = "\n".join(raw).strip()
|
| 179 |
|
| 180 |
return out
|
| 181 |
|
| 182 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 183 |
def polish_russian_same_model(checklist_text: str) -> str:
|
| 184 |
-
"""Second pass with SAME generator (still transformer #2) to polish grammar."""
|
| 185 |
-
if not checklist_text or "- [ ]"
|
| 186 |
return checklist_text
|
| 187 |
|
| 188 |
polish_prompt = (
|
| 189 |
-
"
|
| 190 |
-
"Н
|
| 191 |
-
"
|
| 192 |
-
"-
|
| 193 |
-
"
|
| 194 |
-
"
|
|
|
|
|
|
|
|
|
|
|
|
|
| 195 |
f"{checklist_text}\n"
|
| 196 |
)
|
| 197 |
polish_prompt = apply_chat_template_if_available(polish_prompt)
|
| 198 |
|
| 199 |
outp = gen_pipe(
|
| 200 |
polish_prompt,
|
| 201 |
-
max_new_tokens=
|
| 202 |
do_sample=False,
|
| 203 |
return_full_text=False,
|
| 204 |
)
|
| 205 |
text2 = (outp[0].get("generated_text") or "").strip()
|
| 206 |
text2 = clean_checklist(text2)
|
|
|
|
| 207 |
|
| 208 |
-
# keep only if not worse
|
| 209 |
if text2.count("- [ ]") >= checklist_text.count("- [ ]"):
|
| 210 |
return text2
|
| 211 |
return checklist_text
|
|
@@ -243,21 +369,24 @@ def generate_checklist(user_goal: str, category: str, style: str, constraints: s
|
|
| 243 |
# Generation (primary)
|
| 244 |
out = gen_pipe(
|
| 245 |
prompt,
|
| 246 |
-
max_new_tokens=
|
| 247 |
do_sample=True,
|
| 248 |
-
temperature=0.
|
| 249 |
-
top_p=0.
|
| 250 |
-
repetition_penalty=1.
|
| 251 |
return_full_text=False,
|
| 252 |
)
|
| 253 |
text = (out[0].get("generated_text") or "").strip()
|
| 254 |
text = clean_checklist(text)
|
|
|
|
| 255 |
|
| 256 |
# Retry if too short/poor format
|
| 257 |
-
if text.count("- [ ]") <
|
| 258 |
retry_prompt = (
|
| 259 |
-
"Сделай чек-лист строго в формате '- [ ] ...' (12–18 пунктов) на русском.\n"
|
| 260 |
-
"
|
|
|
|
|
|
|
| 261 |
"В конце добавь:\n"
|
| 262 |
"Проверка готовности: (3–5 вопросов)\n"
|
| 263 |
"Риски и как снизить: (3–6 пунктов)\n\n"
|
|
@@ -269,26 +398,30 @@ def generate_checklist(user_goal: str, category: str, style: str, constraints: s
|
|
| 269 |
retry_prompt = apply_chat_template_if_available(retry_prompt)
|
| 270 |
out2 = gen_pipe(
|
| 271 |
retry_prompt,
|
| 272 |
-
max_new_tokens=
|
| 273 |
do_sample=True,
|
| 274 |
temperature=0.75,
|
| 275 |
-
top_p=0.
|
| 276 |
-
repetition_penalty=1.
|
| 277 |
return_full_text=False,
|
| 278 |
)
|
| 279 |
text2 = (out2[0].get("generated_text") or "").strip()
|
| 280 |
text2 = clean_checklist(text2)
|
|
|
|
| 281 |
if text2.count("- [ ]") > text.count("- [ ]"):
|
| 282 |
text = text2
|
| 283 |
|
|
|
|
|
|
|
|
|
|
| 284 |
# Polish Russian (still same generator = transformer #2)
|
| 285 |
text = polish_russian_same_model(text)
|
| 286 |
|
| 287 |
# Final guard
|
| 288 |
-
if text.count("- [ ]") <
|
| 289 |
text = (
|
| 290 |
-
"- [ ] Не удалось корректно сформировать чек-лист.\n"
|
| 291 |
-
"- [ ] Попробуйте уточнить цель (страна/
|
| 292 |
)
|
| 293 |
|
| 294 |
meta = {
|
|
@@ -331,7 +464,8 @@ def answer_question(question: str, checklist_state: str, meta_state: dict | None
|
|
| 331 |
|
| 332 |
user_prompt = (
|
| 333 |
"Ответь на вопрос по чек-листу. Пиши по-русски, кратко и практично.\n"
|
| 334 |
-
"Если ответа нет в чек-листе — предложи 3–6 дополнительных пунктов
|
|
|
|
| 335 |
"Не используй плейсхолдеры в квадратных скобках.\n\n"
|
| 336 |
f"Цель: {goal}\n"
|
| 337 |
f"Тема: {theme}\n\n"
|
|
@@ -342,7 +476,7 @@ def answer_question(question: str, checklist_state: str, meta_state: dict | None
|
|
| 342 |
prompt = apply_chat_template_if_available(user_prompt)
|
| 343 |
gen_out = gen_pipe(
|
| 344 |
prompt,
|
| 345 |
-
max_new_tokens=
|
| 346 |
do_sample=False,
|
| 347 |
return_full_text=False,
|
| 348 |
)[0]["generated_text"].strip()
|
|
@@ -366,7 +500,6 @@ with gr.Blocks(title="Умный чек-лист (3 Transformers)") as demo:
|
|
| 366 |
meta_state = gr.State(value=None)
|
| 367 |
|
| 368 |
with gr.Tab("1) Создать чек-лист"):
|
| 369 |
-
# Top row: inputs left, helper mini-blocks right
|
| 370 |
with gr.Row():
|
| 371 |
with gr.Column(scale=3):
|
| 372 |
user_goal = gr.Textbox(
|
|
@@ -412,7 +545,6 @@ with gr.Blocks(title="Умный чек-лист (3 Transformers)") as demo:
|
|
| 412 |
interactive=False,
|
| 413 |
)
|
| 414 |
|
| 415 |
-
# Big checklist output BELOW (full width)
|
| 416 |
checklist_out = gr.Code(label="Чек-лист", language="markdown")
|
| 417 |
|
| 418 |
gen_btn.click(
|
|
|
|
| 13 |
|
| 14 |
# 2) Checklist generator (instruct)
|
| 15 |
DEFAULT_GEN_MODEL = os.getenv("GEN_MODEL", "Qwen/Qwen2.5-0.5B-Instruct")
|
| 16 |
+
# If your Space can handle it, this is often better structured:
|
| 17 |
+
# DEFAULT_GEN_MODEL = os.getenv("GEN_MODEL", "Qwen/Qwen2.5-1.5B-Instruct")
|
| 18 |
|
| 19 |
# 3) QA over checklist
|
| 20 |
DEFAULT_QA_MODEL = os.getenv("QA_MODEL", "deepset/xlm-roberta-base-squad2")
|
|
|
|
| 109 |
"Составь практичный чек-лист на русском языке.\n"
|
| 110 |
"Верни ТОЛЬКО чек-лист без вступлений.\n"
|
| 111 |
"Запрещено:\n"
|
| 112 |
+
"- любые вступления/комментарии (например: 'Конечно!', 'Вот исправленный текст', 'не меняю смысл')\n"
|
| 113 |
"- плейсхолдеры и шаблоны: никаких '[секунды]', '[какой-то текст]', '{...}', '<...>'\n"
|
| 114 |
+
"- таблицы, 'Расчёт', 'Банк', 'Сообщение', поля для заполнения\n\n"
|
| 115 |
"Формат строго:\n"
|
| 116 |
+
"- [ ] (P0) пункт\n"
|
| 117 |
" - подпункт (если нужно)\n"
|
| 118 |
+
"Где P0 = срочно/критично, P1 = важно, P2 = можно позже.\n\n"
|
| 119 |
"Требования:\n"
|
| 120 |
f"- стиль: {style_hint}\n"
|
| 121 |
"- 12–18 пунктов\n"
|
| 122 |
+
"- без нумерации (никаких '1.'), только '- [ ]'\n"
|
| 123 |
+
"- КАЖДЫЙ пункт должен начинаться с (P0) или (P1) или (P2)\n"
|
| 124 |
+
"- Сначала выведи все P0, потом P1, потом P2\n"
|
| 125 |
"- пункты конкретные и выполнимые\n"
|
| 126 |
+
"- для пунктов про документы/банки/счета/визы/страховку ОБЯЗАТЕЛЬНО добавь 2–5 подпунктов\n"
|
| 127 |
+
"- подпункты начинай с ' - '\n"
|
| 128 |
+
"- если тема релокации/иммиграции: обязательно охвати документы, финансы, связь, жильё, медицину, язык\n"
|
| 129 |
"- в конце 2 блока:\n"
|
| 130 |
"Проверка готовности:\n"
|
| 131 |
"- ... (3–5 вопросов)\n"
|
|
|
|
| 155 |
text = re.sub(r"<extra_id_\d+>", "", text).strip()
|
| 156 |
|
| 157 |
cleaned = []
|
| 158 |
+
current_group = "OTHER"
|
| 159 |
+
|
| 160 |
for ln in text.splitlines():
|
| 161 |
s = ln.rstrip()
|
|
|
|
| 162 |
if not s.strip():
|
| 163 |
continue
|
| 164 |
|
|
|
|
| 165 |
low = s.strip().lower()
|
| 166 |
+
|
| 167 |
+
# 1) Cut conversational/assistant boilerplate
|
| 168 |
+
bad_prefixes = (
|
| 169 |
+
"конечно",
|
| 170 |
+
"вот исправленный",
|
| 171 |
+
"вот исправленный текст",
|
| 172 |
+
"исправленный текст",
|
| 173 |
+
"ничего не добавляя",
|
| 174 |
+
"не меняю смысл",
|
| 175 |
+
"я не меняю смысл",
|
| 176 |
+
"готово",
|
| 177 |
+
"итог",
|
| 178 |
+
"результат",
|
| 179 |
+
)
|
| 180 |
+
if low.startswith(bad_prefixes):
|
| 181 |
+
continue
|
| 182 |
+
|
| 183 |
+
# 2) Cut obvious "form-like" garbage blocks
|
| 184 |
if low.startswith(("расч", "банк", "сообщение", "время ", "план поездки", "расход")):
|
| 185 |
continue
|
| 186 |
|
| 187 |
+
# 3) Remove square-bracket placeholders but keep "[ ]" checkbox.
|
| 188 |
+
# This removes [секунды], [какой-то текст], etc.
|
| 189 |
s2 = re.sub(r"\[(?!\s*\])([^\]]+)\]", "", s).strip()
|
| 190 |
|
| 191 |
+
# 4) If model wrapped whole item into [ ... ]: "- [ ] [Сделайте ...]"
|
| 192 |
+
s2 = re.sub(r"^\-\s*\[\s*\]\s*\[(.+)\]\s*$", r"- [ ] \1", s2).strip()
|
| 193 |
+
|
| 194 |
+
# Normalize bullets & spacing
|
| 195 |
s2 = s2.replace("•", "-").replace("–", "-")
|
| 196 |
+
s2 = re.sub(r"\s+", " ", s2)
|
| 197 |
|
| 198 |
+
# Keep only checklist lines + the two required blocks
|
| 199 |
if s2.startswith("- [ ]") or s2.startswith(" -") or s2.startswith("Проверка готовности") or s2.startswith("Риски"):
|
| 200 |
cleaned.append(s2)
|
| 201 |
|
|
|
|
| 206 |
raw = [l.strip() for l in text.splitlines() if l.strip()]
|
| 207 |
raw = [re.sub(r"^\d+[\).\s]+", "", r).strip(" -•\t") for r in raw]
|
| 208 |
raw = [r for r in raw if r]
|
| 209 |
+
raw = [f"- [ ] (P1) {r}" for r in raw[:18]]
|
| 210 |
out = "\n".join(raw).strip()
|
| 211 |
|
| 212 |
return out
|
| 213 |
|
| 214 |
|
| 215 |
+
def split_tail_sections(text: str):
|
| 216 |
+
"""Split checklist body from tail sections (Проверка готовности / Риски...) to keep them at bottom."""
|
| 217 |
+
lines = text.splitlines()
|
| 218 |
+
idx = None
|
| 219 |
+
for i, ln in enumerate(lines):
|
| 220 |
+
if ln.strip().startswith("Проверка готовности"):
|
| 221 |
+
idx = i
|
| 222 |
+
break
|
| 223 |
+
if idx is None:
|
| 224 |
+
return text, ""
|
| 225 |
+
return "\n".join(lines[:idx]).strip(), "\n".join(lines[idx:]).strip()
|
| 226 |
+
|
| 227 |
+
|
| 228 |
+
def sort_by_priority(text: str) -> str:
|
| 229 |
+
"""Ensure P0 -> P1 -> P2 ordering (keeps tail sections at the very end)."""
|
| 230 |
+
body, tail = split_tail_sections(text)
|
| 231 |
+
if not body:
|
| 232 |
+
return text
|
| 233 |
+
|
| 234 |
+
groups = {"P0": [], "P1": [], "P2": [], "OTHER": []}
|
| 235 |
+
current = "OTHER"
|
| 236 |
+
|
| 237 |
+
for ln in body.splitlines():
|
| 238 |
+
s = ln.strip()
|
| 239 |
+
if s.startswith("- [ ]"):
|
| 240 |
+
if "(P0)" in s:
|
| 241 |
+
current = "P0"
|
| 242 |
+
elif "(P1)" in s:
|
| 243 |
+
current = "P1"
|
| 244 |
+
elif "(P2)" in s:
|
| 245 |
+
current = "P2"
|
| 246 |
+
else:
|
| 247 |
+
current = "OTHER"
|
| 248 |
+
groups[current].append(ln)
|
| 249 |
+
else:
|
| 250 |
+
groups[current].append(ln)
|
| 251 |
+
|
| 252 |
+
out_lines = []
|
| 253 |
+
for k in ["P0", "P1", "P2", "OTHER"]:
|
| 254 |
+
if groups[k]:
|
| 255 |
+
out_lines.extend(groups[k])
|
| 256 |
+
|
| 257 |
+
out = "\n".join(out_lines).strip()
|
| 258 |
+
if tail:
|
| 259 |
+
out = (out + "\n\n" + tail).strip()
|
| 260 |
+
return out
|
| 261 |
+
|
| 262 |
+
|
| 263 |
+
def ensure_doc_finance_subpoints(text: str) -> str:
|
| 264 |
+
"""
|
| 265 |
+
If user asked for relocation/travel and the checklist contains a 'documents' or 'accounts' item without subpoints,
|
| 266 |
+
add a minimal set of subpoints using deterministic rules (no extra transformer).
|
| 267 |
+
"""
|
| 268 |
+
lines = text.splitlines()
|
| 269 |
+
out = []
|
| 270 |
+
i = 0
|
| 271 |
+
while i < len(lines):
|
| 272 |
+
ln = lines[i]
|
| 273 |
+
out.append(ln)
|
| 274 |
+
|
| 275 |
+
if ln.startswith("- [ ]"):
|
| 276 |
+
low = ln.lower()
|
| 277 |
+
|
| 278 |
+
# detect if next line(s) already has subpoints
|
| 279 |
+
j = i + 1
|
| 280 |
+
has_sub = False
|
| 281 |
+
while j < len(lines) and lines[j].startswith(" -"):
|
| 282 |
+
has_sub = True
|
| 283 |
+
break
|
| 284 |
+
|
| 285 |
+
# Add subpoints if missing and keyword matches
|
| 286 |
+
if not has_sub and any(k in low for k in ["документ", "виза", "страхов", "паспорт"]):
|
| 287 |
+
out.extend([
|
| 288 |
+
" - Проверь срок действия загранпаспорта и требования к остаточному сроку",
|
| 289 |
+
" - Составь список нужных документов и сделай копии (бумага + облако)",
|
| 290 |
+
" - Уточни требования по визе/ВНЖ и собери подтверждения (финансы, жильё, приглашение)",
|
| 291 |
+
])
|
| 292 |
+
|
| 293 |
+
if not has_sub and any(k in low for k in ["счёт", "счет", "банк", "карта", "финанс"]):
|
| 294 |
+
out.extend([
|
| 295 |
+
" - Проверь лимиты, комиссии и возможность работы карт за границей",
|
| 296 |
+
" - Подготовь резервный способ доступа к деньгам (вторая карта/наличные/перевод)",
|
| 297 |
+
" - Включи уведомления и двухфакторную защиту в банковских приложениях",
|
| 298 |
+
])
|
| 299 |
+
|
| 300 |
+
i += 1
|
| 301 |
+
|
| 302 |
+
return "\n".join(out).strip()
|
| 303 |
+
|
| 304 |
+
|
| 305 |
def polish_russian_same_model(checklist_text: str) -> str:
|
| 306 |
+
"""Second pass with SAME generator (still transformer #2) to polish grammar without 'boilerplate'."""
|
| 307 |
+
if not checklist_text or checklist_text.count("- [ ]") < 6:
|
| 308 |
return checklist_text
|
| 309 |
|
| 310 |
polish_prompt = (
|
| 311 |
+
"Отредактируй текст чек-листа: исправь грамматику и стиль русского языка.\n"
|
| 312 |
+
"ВАЖНО:\n"
|
| 313 |
+
"- Верни ТОЛЬКО чек-лист.\n"
|
| 314 |
+
"- Никаких вступлений (например: 'Конечно', 'Вот исправленный текст').\n"
|
| 315 |
+
"- Никаких комментариев (например: 'не меняю смысл').\n"
|
| 316 |
+
"- Ничего не добавляй и не удаляй по смыслу.\n"
|
| 317 |
+
"- Сохрани ф��рмат:\n"
|
| 318 |
+
" - [ ] (P0/P1/P2) пункт\n"
|
| 319 |
+
" - подпункт\n"
|
| 320 |
+
"- Сохрани блоки 'Проверка готовности' и 'Риски и как снизить'.\n\n"
|
| 321 |
f"{checklist_text}\n"
|
| 322 |
)
|
| 323 |
polish_prompt = apply_chat_template_if_available(polish_prompt)
|
| 324 |
|
| 325 |
outp = gen_pipe(
|
| 326 |
polish_prompt,
|
| 327 |
+
max_new_tokens=300,
|
| 328 |
do_sample=False,
|
| 329 |
return_full_text=False,
|
| 330 |
)
|
| 331 |
text2 = (outp[0].get("generated_text") or "").strip()
|
| 332 |
text2 = clean_checklist(text2)
|
| 333 |
+
text2 = sort_by_priority(text2)
|
| 334 |
|
|
|
|
| 335 |
if text2.count("- [ ]") >= checklist_text.count("- [ ]"):
|
| 336 |
return text2
|
| 337 |
return checklist_text
|
|
|
|
| 369 |
# Generation (primary)
|
| 370 |
out = gen_pipe(
|
| 371 |
prompt,
|
| 372 |
+
max_new_tokens=650,
|
| 373 |
do_sample=True,
|
| 374 |
+
temperature=0.65,
|
| 375 |
+
top_p=0.92,
|
| 376 |
+
repetition_penalty=1.10,
|
| 377 |
return_full_text=False,
|
| 378 |
)
|
| 379 |
text = (out[0].get("generated_text") or "").strip()
|
| 380 |
text = clean_checklist(text)
|
| 381 |
+
text = sort_by_priority(text)
|
| 382 |
|
| 383 |
# Retry if too short/poor format
|
| 384 |
+
if text.count("- [ ]") < 10:
|
| 385 |
retry_prompt = (
|
| 386 |
+
"Сделай чек-лист строго в формате '- [ ] (P0/P1/P2) ...' (12–18 пунктов) на русском.\n"
|
| 387 |
+
"Сначала P0, затем P1, затем P2.\n"
|
| 388 |
+
"Без вступлений. Без комментариев. Без плейсхолдеров в квадратных скобках.\n"
|
| 389 |
+
"Для документов/банков/счётов/визы/страховки добавь подпункты (2–5).\n"
|
| 390 |
"В конце добавь:\n"
|
| 391 |
"Проверка готовности: (3–5 вопросов)\n"
|
| 392 |
"Риски и как снизить: (3–6 пунктов)\n\n"
|
|
|
|
| 398 |
retry_prompt = apply_chat_template_if_available(retry_prompt)
|
| 399 |
out2 = gen_pipe(
|
| 400 |
retry_prompt,
|
| 401 |
+
max_new_tokens=750,
|
| 402 |
do_sample=True,
|
| 403 |
temperature=0.75,
|
| 404 |
+
top_p=0.93,
|
| 405 |
+
repetition_penalty=1.12,
|
| 406 |
return_full_text=False,
|
| 407 |
)
|
| 408 |
text2 = (out2[0].get("generated_text") or "").strip()
|
| 409 |
text2 = clean_checklist(text2)
|
| 410 |
+
text2 = sort_by_priority(text2)
|
| 411 |
if text2.count("- [ ]") > text.count("- [ ]"):
|
| 412 |
text = text2
|
| 413 |
|
| 414 |
+
# Deterministic subpoints for docs/finance if missing
|
| 415 |
+
text = ensure_doc_finance_subpoints(text)
|
| 416 |
+
|
| 417 |
# Polish Russian (still same generator = transformer #2)
|
| 418 |
text = polish_russian_same_model(text)
|
| 419 |
|
| 420 |
# Final guard
|
| 421 |
+
if text.count("- [ ]") < 8:
|
| 422 |
text = (
|
| 423 |
+
"- [ ] (P0) Не удалось корректно сформировать чек-лист.\n"
|
| 424 |
+
"- [ ] (P0) Попробуйте уточнить цель (страна/город, статус визы, бюджет, сроки) и нажать ещё раз.\n"
|
| 425 |
)
|
| 426 |
|
| 427 |
meta = {
|
|
|
|
| 464 |
|
| 465 |
user_prompt = (
|
| 466 |
"Ответь на вопрос по чек-листу. Пиши по-русски, кратко и практично.\n"
|
| 467 |
+
"Если ответа нет в чек-листе — предложи 3–6 дополнительных пунктов (с приоритетом P0/P1/P2).\n"
|
| 468 |
+
"Не используй вступления и комментарии.\n"
|
| 469 |
"Не используй плейсхолдеры в квадратных скобках.\n\n"
|
| 470 |
f"Цель: {goal}\n"
|
| 471 |
f"Тема: {theme}\n\n"
|
|
|
|
| 476 |
prompt = apply_chat_template_if_available(user_prompt)
|
| 477 |
gen_out = gen_pipe(
|
| 478 |
prompt,
|
| 479 |
+
max_new_tokens=320,
|
| 480 |
do_sample=False,
|
| 481 |
return_full_text=False,
|
| 482 |
)[0]["generated_text"].strip()
|
|
|
|
| 500 |
meta_state = gr.State(value=None)
|
| 501 |
|
| 502 |
with gr.Tab("1) Создать чек-лист"):
|
|
|
|
| 503 |
with gr.Row():
|
| 504 |
with gr.Column(scale=3):
|
| 505 |
user_goal = gr.Textbox(
|
|
|
|
| 545 |
interactive=False,
|
| 546 |
)
|
| 547 |
|
|
|
|
| 548 |
checklist_out = gr.Code(label="Чек-лист", language="markdown")
|
| 549 |
|
| 550 |
gen_btn.click(
|