repair-guy / models /minicpm.py
airayven7's picture
Sync from GitHub 4fcc227
293ed8c verified
Raw
History Blame
3.07 kB
"""MiniCPM-V: answers a question grounded in the retrieved repair-manual pages.
The model and tokenizer are module-level globals: ZeroGPU packs module-level
CUDA tensors at startup and shares them with the GPU worker, whereas function
arguments are pickled — and trust_remote_code model classes are not picklable.
generate_answer is a plain function: the ask pipeline calls it inside its own
single @spaces.GPU call, right after retrieval.
"""
from __future__ import annotations
import torch
from PIL import Image
from transformers import AutoModel, AutoProcessor, AutoTokenizer
from core.constants import ANSWER_MAX_NEW_TOKENS, MINICPM_MODEL_ID, MINICPM_REVISION
PROMPT = (
"You are a repair-manual assistant. The images are the manual pages most "
"relevant to the user's question, each preceded by its label (manual name "
"and page number).\n\n"
"Answer the question using ONLY what is printed on these pages, following "
"these rules:\n"
"1. If the answer is a procedure, reproduce EVERY step in order, numbered "
"exactly as in the manual. Never skip, merge, or summarize steps. Keep each "
"step's notes, model exceptions, and specifications (e.g. torque values) "
"with that step, exactly as printed.\n"
"2. Quote exact values (torques, clearances, part numbers, capacities) as "
"printed, including units.\n"
"3. End with the page label(s) you used, e.g. (Manual — p.238). If a "
"procedure clearly continues on a page you were not given, say so.\n"
"4. If the pages do not contain the answer, say so instead of guessing.\n"
"5. Start directly with the answer — no preamble like 'Based on the "
"provided pages'.\n\n"
"Question: {question}"
)
_MODEL = (
AutoModel.from_pretrained(
MINICPM_MODEL_ID,
revision=MINICPM_REVISION,
trust_remote_code=True,
dtype=torch.bfloat16,
attn_implementation="sdpa",
)
.to("cuda")
.eval()
)
_TOKENIZER = AutoTokenizer.from_pretrained(
MINICPM_MODEL_ID, revision=MINICPM_REVISION, trust_remote_code=True
)
# Pre-build the processor chat() would otherwise lazily create per GPU worker
# (it caches on this exact attribute, see modeling_minicpmv.chat).
_MODEL.processor = AutoProcessor.from_pretrained(
MINICPM_MODEL_ID, revision=MINICPM_REVISION, trust_remote_code=True
)
def generate_answer(question: str, pages: list[tuple[str, Image.Image]]) -> str:
"""pages: [(label, page image)] in retrieval order. Must run on GPU
(called from within a @spaces.GPU context)."""
content = []
for label, img in pages: # chat() accepts interleaved strings and PIL images
content.append(f"[{label}]")
content.append(img.convert("RGB"))
content.append(PROMPT.format(question=question))
with torch.no_grad():
answer = _MODEL.chat(
msgs=[{"role": "user", "content": content}],
tokenizer=_TOKENIZER,
enable_thinking=False,
max_new_tokens=ANSWER_MAX_NEW_TOKENS,
)
return str(answer).strip()