multimodalart's picture
multimodalart HF Staff
Upload folder using huggingface_hub
85300f6 verified
Raw History Blame Contribute Delete
6.26 kB
"""Typed pilot records, strict target parsing and answer-free prompt construction."""
import ast
import math
import re
SCHEMA_VERSION = "spatial-decision/v2"
EXTENDED_SCHEMA_VERSION = "spatial-decision/v3"
TASKS = ("choice", "scalar", "point")
UNIT_FACTORS = {"m": 1.0, "meter": 1.0, "meters": 1.0,
"cm": 0.01, "centimeter": 0.01, "centimeters": 0.01,
"mm": 0.001, "millimeter": 0.001, "millimeters": 0.001,
"inch": 0.0254, "inches": 0.0254, "ft": 0.3048,
"foot": 0.3048, "feet": 0.3048}
_NUMBER = r"[+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]?\d+)?"
def parse_measurement(answer, question=""):
"""Accept a scalar, optionally with an explicit length unit; never extract CoT numbers."""
match = re.fullmatch(rf"\s*({_NUMBER})\s*([a-zA-Z]+)?\s*\.?\s*", answer)
if not match:
raise ValueError("not_a_scalar_answer")
unit = match.group(2)
if unit is None:
units = re.findall(r"\b(" + "|".join(UNIT_FACTORS) + r")\b", question.lower())
factors = {UNIT_FACTORS[u] for u in units}
if len(factors) != 1:
raise ValueError("missing_or_ambiguous_unit")
unit = units[-1]
unit = unit.lower()
if unit not in UNIT_FACTORS:
raise ValueError("unsupported_unit")
value = float(match.group(1)) * UNIT_FACTORS[unit]
if not math.isfinite(value) or value < 0:
raise ValueError("invalid_measurement")
return value, unit
def parse_single_point(answer):
try:
value = ast.literal_eval(answer.strip())
except (ValueError, SyntaxError) as exc:
raise ValueError("not_a_point_literal") from exc
if not isinstance(value, (list, tuple)) or len(value) != 1:
raise ValueError("not_a_single_point")
point = value[0]
if not isinstance(point, (list, tuple)) or len(point) != 2:
raise ValueError("not_xy")
if any(isinstance(x, bool) or not isinstance(x, (int, float)) for x in point):
raise ValueError("non_numeric_point")
if any(not math.isfinite(x) or not 0 <= x <= 1 for x in point):
raise ValueError("point_out_of_range")
return [float(x) for x in point]
def clean_question(question):
question = question.replace("<image>", "").strip()
question = re.split(r"\s*Your answer should be formatted", question, flags=re.I)[0]
question = re.sub(r"\s*Answer the question using a single word or phrase\.?", "",
question, flags=re.I)
return question.strip()
def canonical_metric_question(question):
question = clean_question(question)
# Do not rewrite numeric lengths in a premise without also converting their values.
if re.search(rf"{_NUMBER}\s*(?:cm|mm|meters?|centimeters?|inches|feet)\b",
question, re.I):
raise ValueError("numeric_unit_in_question_premise")
return re.sub(r"\b(?:centimeters?|cm|millimeters?|mm|inches|feet|foot|ft)\b",
"meters", question, flags=re.I)
def validate_record(record, max_choices=32):
if record.get("schema_version") not in (SCHEMA_VERSION, EXTENDED_SCHEMA_VERSION):
raise ValueError("unsupported_schema")
inp = record["input"]
if not inp["question"].strip() or not inp["media"]:
raise ValueError("empty_input")
if any(m["kind"] != "image" for m in inp["media"]):
raise ValueError("pilot_requires_images")
space, target = inp["answer_space"], record["target"]
kind = space["kind"]
if kind == "choice":
options = space["options"]
ids = [o["id"] for o in options]
if not 2 <= len(options) <= max_choices or len(set(ids)) != len(ids):
raise ValueError("invalid_options")
if target["option_id"] not in ids:
raise ValueError("target_missing_from_options")
elif kind == "scalar":
allowed = ("m", "count", "deg") if record["schema_version"] == EXTENDED_SCHEMA_VERSION else ("m",)
if space["unit"] not in allowed or target["unit"] != space["unit"]:
raise ValueError("pilot_scalar_requires_meters")
if isinstance(target["value"], bool) or not math.isfinite(target["value"]) or target["value"] < 0:
raise ValueError("invalid_scalar")
if space["unit"] == "count":
if space.get("quantity") != "count" or int(target["value"]) != target["value"] or target["value"] > 1023:
raise ValueError("invalid_integer_count")
if space["unit"] == "deg":
if target["value"] > 360 or not space.get("angle_semantics"):
raise ValueError("invalid_angle_semantics_or_range")
elif kind == "point":
if len(inp["media"]) != 1:
raise ValueError("pilot_point_requires_one_image")
if space["coordinate_system"] != "normalized_xy" or space["num_points"] != 1:
raise ValueError("unsupported_point_space")
parse_single_point(repr(target["points"]))
else:
raise ValueError("unsupported_task")
def compile_prompt(inp, permutation=None):
"""Only input is accepted; targets and provenance cannot enter the prompt."""
space = inp["answer_space"]
kind = space["kind"]
text = f"Question: {inp['question']}\n"
option_ids = []
if kind == "choice":
options = space["options"]
order = list(range(len(options))) if permutation is None else list(permutation)
if sorted(order) != list(range(len(options))):
raise ValueError("invalid_permutation")
text += "Options:\n"
for i, index in enumerate(order):
option = options[index]
text += f"{i + 1}. {option['text']}\n"
option_ids.append(option["id"])
text += "Select exactly one option."
elif kind == "scalar":
unit = {"m": "meters", "count": "count units (an integer)", "deg": "degrees"}[space["unit"]]
text += f"Estimate {space['quantity']}. Return a nonnegative value in {unit}."
elif kind == "point":
text += ("Return one valid point (x, y) in the original image, normalized to [0, 1]. "
"The origin is top-left; x increases rightward and y increases downward.")
else:
raise ValueError("unsupported_task")
return text, option_ids