repair-guy / models /minicpm.py
airayven7's picture
Sync from GitHub ea32217
3f08eff verified
Raw
History Blame
2.74 kB
"""MiniCPM-V on ZeroGPU: 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.
"""
from __future__ import annotations
import spaces
import torch
from PIL import Image
from transformers import AutoModel, AutoTokenizer
from core.constants import ANSWER_GPU_DURATION, ANSWER_MAX_NEW_TOKENS, MINICPM_MODEL_ID
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,
trust_remote_code=True,
dtype=torch.bfloat16,
attn_implementation="sdpa",
)
.to("cuda")
.eval()
)
_TOKENIZER = AutoTokenizer.from_pretrained(MINICPM_MODEL_ID, trust_remote_code=True)
@spaces.GPU(duration=ANSWER_GPU_DURATION)
def _answer_on_gpu(question: str, pages: list[tuple[str, Image.Image]]) -> str:
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()
class MiniCPM:
def answer(self, question: str, pages: list[tuple[str, Image.Image]]) -> str:
"""pages: [(label, page image)] in retrieval order."""
return _answer_on_gpu(question, pages)