JEVision / runtime /kev /visual_prompt.py
divyanshx11's picture
Bundle JEVision without benchmark scores
4d27595
Raw History Blame
1.71 kB
"""Build one image-bearing KEV question without allowing user text to forge delimiters."""
import re
SPECIAL = ("<|fim_prefix|>", "<|fim_middle|>", "<|box_start|>",
"<|box_end|>", "<|fim_suffix|>") # kev.model.SPECIAL, without importing model weights/dependencies
_CONTROL = re.compile(r"<\|([A-Za-z0-9_]+)\|>")
def escape_user_text(value: str) -> str:
"""Match kev.model.user_tokens' visible escaping before processor tokenization."""
if not isinstance(value, str):
raise ValueError("visual state, instruction, and options must be strings")
return _CONTROL.sub(r"<¦\1¦>", value)
def build_content(state: str, question: dict, image_count: int) -> list[dict[str, str]]:
"""Return official chat-template content items, with images before a KEV body."""
if not isinstance(image_count, int) or isinstance(image_count, bool) or not 1 <= image_count <= 4:
raise ValueError("visual requests need 1..4 images")
if not isinstance(question, dict):
raise ValueError("visual question must be a dict")
options = question.get("options")
if not isinstance(options, list) or not 1 <= len(options) <= 255:
raise ValueError("visual question needs 1..255 options")
if any(not isinstance(option, str) or not option.strip() for option in options):
raise ValueError("visual options must be non-empty strings")
state_text = escape_user_text(state)
instruction = escape_user_text(question.get("instr"))
s, q, o, c, d = SPECIAL
body = s + state_text + q + instruction + "".join(o + escape_user_text(x) + c for x in options) + d
return [{"type": "image"} for _ in range(image_count)] + [{"type": "text", "text": body}]