File size: 10,360 Bytes
53c10a4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
#!/usr/bin/env python3
"""Single source of truth for evaluation prompts.

Exactly ONE prompt per task — the one used by ``eval/task/eval.sh`` — kept
byte-for-byte aligned with the SFT training data in
``data/joint/sft_joint_all.jsonl``.

Task -> prompt mapping (verbatim from sft_joint_all.jsonl):

  video_qa_mc (videomme, videommmu, mmvu, mvbench, videoholmes,
               longvideobench, lvbench, mlvu)
      "{question}\\nOptions:\\n{opts}\\n" + VIDEO_QA_MC_TAIL

  spatial_intelligence (vsi)
      MC      -> "{q}\\nOptions:\\n{opts}\\n" + VSI_MC_TAIL
      integer -> "{q} " + VSI_INTEGER_TAIL
      meters  -> "{q} " + VSI_METERS_TAIL
      cm      -> "{q} " + VSI_CENTIMETERS_TAIL
      room m2 -> "{q} " + VSI_SQUARE_METERS_TAIL
      (each prefixed with "These are frames of a video.\\n")

  sensenova_si / image_sequence_mc (mmsi, mindcube)
      "{q}\\n" + SENSENOVA_MC_TAIL   (alias PROMPT_TAIL)

  spatial grounding (spatial_grounding)
      build_spatial_grounding_prompt(expr)  == QWEN_NATIVE_PROMPT_SG

  tracking
      GROUNDING_QUESTION_TEMPLATE_NO_THINK.format(Question=q) + TRACKING_TAIL

  stvg
      GROUNDING_QUESTION_TEMPLATE_NO_THINK.format(
          Question=TRAIN_STVG_QUESTION_PREFIX.format(query=q)) + STVG_TAIL

  segmentation
      "{question}\\n" + TRAIN_SEG_IMAGE_TAIL / TRAIN_SEG_VIDEO_TAIL

  temporal grounding (temporal_grounding)
      TEMPORAL_GROUNDING_PROMPT.format(event)

Keep this file dependency-free (stdlib only) so it can be imported from every
worker regardless of the runtime environment.
"""

# ===========================================================================
# Multiple-choice / numeric answer tails
# ===========================================================================

# video_qa_mc — videomme, videommmu, mmvu, mvbench, videoholmes,
# longvideobench, lvbench, mlvu.
VIDEO_QA_MC_TAIL = (
    "Answer with the option letter only within <answer>...</answer> tags. "
    "Example: <answer>A</answer>"
)

# spatial_intelligence multiple choice — vsi (object_rel_distance,
# object_rel_direction, obj_appearance_order, route_plan, ...).
VSI_MC_TAIL = (
    "Answer with the option letter within <answer>...</answer> tags. "
    "Example: <answer>A</answer>"
)

# spatial_intelligence numeric — vsi.
VSI_INTEGER_TAIL = (
    "Answer with an integer within <answer>...</answer> tags. Example: <answer>3</answer>"
)
VSI_METERS_TAIL = (
    "Answer with a number in meters within <answer>...</answer> tags. Example: <answer>2.3</answer>"
)
VSI_CENTIMETERS_TAIL = (
    "Answer with a number in centimeters within <answer>...</answer> tags. Example: <answer>120.5</answer>"
)
VSI_SQUARE_METERS_TAIL = (
    "Answer with a number in square meters within <answer>...</answer> tags. Example: <answer>25.5</answer>"
)

# sensenova_si / image_sequence_mc_answer_only — mmsi, mindcube.
SENSENOVA_MC_TAIL = (
    "Choose the best answer from the options. "
    "Put exactly one uppercase option letter inside <answer>...</answer> "
    "Do not explain. Example: <answer>A</answer>"
)
# Backwards-compatible alias used throughout eval_vllm.py.
PROMPT_TAIL = SENSENOVA_MC_TAIL


# ===========================================================================
# Tracking
# ===========================================================================

# Question scaffold shared by tracking + stvg (the tail carries the full
# answer-format spec).
GROUNDING_QUESTION_TEMPLATE_NO_THINK = (
    "{Question}\n"
    "Please answer this question based on the visual content. "
)

TRACKING_TAIL = (
    "Please track the target object throughout the video and provide one bounding box per second, "
    "ONLY up to 32 seconds, within the <answer>...</answer> tags.\n"
    "Example:\n"
    "<answer>{\"boxes\": {\"1\": [405, 230, 654, 463], \"2\": [435, 223, 678, 446], "
    "\"32\": [415, 203, 691, 487]}}</answer>\n"
    "Note: Each key in 'boxes' must correspond to a second (1, 2, 3, ..., 32) "
    "and contain a 4-number bounding box [x1, y1, x2, y2]."
)


# ===========================================================================
# Spatial-temporal grounding (STVG)
# ===========================================================================

STVG_TAIL = (
    "Please provide only the time span in seconds and bounding boxes as JSON "
    "within the <answer>...</answer> tags.\n"
    "You MUST output one bounding box for every integer second within the "
    "given time span (inclusive).\n"
    "Example:\n"
    "<answer>{\"time\": [8.1, 13.5], \"boxes\": {\"9\": [317, 422, 582, 997], "
    "\"10\": [332, 175, 442, 369], \"11\": [340, 180, 450, 370]}}</answer>\n"
    "Note: Each key in 'boxes' must be an integer second within the span, "
    "and its value must be a 4-number bounding box [x1, y1, x2, y2]."
)

# Rewrites an eval question into the exact wording used during STVG training.
TRAIN_STVG_QUESTION_PREFIX = (
    'Given the query "{query}", when and where does the described content '
    'occur in the video? please firstly give the start and end time, spatial '
    'bounding box corresponding to each integer second.'
)


# ===========================================================================
# Segmentation
# ===========================================================================

# Matches sft_joint_all.jsonl seg_image / seg_video samples.
TRAIN_SEG_IMAGE_TAIL = (
    "Please answer this question based on the visual content. "
    "This task prepares inputs for image object segmentation with a specialized model (e.g., SAM2).\n"
    "Please provide ONE bounding box, 3 positive points (clearly INSIDE the object), "
    "and 3 negative points (clearly OUTSIDE the object) within the <answer>...</answer> tags.\n"
    "Choose informative points that help distinguish object vs. background. Prefer negatives on clear non-object "
    "pixels INSIDE the box when safe; otherwise place them just outside on obvious background. "
    "Negatives must NEVER be on the object or on its boundary.\n"
    "Example: <answer>{\"boxes\": [x1, y1, x2, y2], \"positive_points\": [[x,y],[x,y],[x,y]], "
    "\"negative_points\": [[x,y],[x,y],[x,y]]}</answer>"
)

TRAIN_SEG_VIDEO_TAIL = (
    "Please answer this question based on the visual content. "
    "This task prepares inputs for video object segmentation with a specialized model (e.g., SAM2).\n"
    "Please select ONE representative time (in seconds), and provide ONE bounding box, "
    "3 positive points (clearly INSIDE the object), and 3 negative points (clearly OUTSIDE the object) "
    "within the <answer>...</answer> tags.\n"
    "Choose informative points that help distinguish object vs. background. Prefer negatives on clear non-object "
    "pixels INSIDE the box when safe; otherwise place them just outside on obvious background. "
    "Negatives must NEVER be on the object or on its boundary.\n"
    "Example: <answer>{\"time\": <time_in_seconds>, \"boxes\": [x1, y1, x2, y2], "
    "\"positive_points\": [[x,y],[x,y],[x,y]], \"negative_points\": [[x,y],[x,y],[x,y]]}</answer>"
)


# ===========================================================================
# Spatial grounding (RefCOCO / spatial_grounding)
# ===========================================================================

# Matches sft_joint_all.jsonl spatial grounding samples after the <image> token.
QWEN_NATIVE_PROMPT_SG = (
    'Locate "{}" in the image. Output its bounding box in JSON format '
    'within <answer>...</answer> tags. '
    'Example: <answer>[{{"bbox_2d": [123, 30, 404, 846]}}]</answer>'
)


# ===========================================================================
# Temporal grounding (TimeLens / temporal_grounding)
# ===========================================================================

# Matches sft_joint_all.jsonl temporal grounding samples (after <video>).
TEMPORAL_GROUNDING_PROMPT = (
    'To accurately pinpoint the event "{}" in the video, '
    "determine the precise time period of the event. "
    "Provide the start and end times (in seconds) "
    'in the format "start time to end time" within <answer> </answer> tags. '
    "Example: <answer> 12 to 18 </answer>"
)
# Exact prompt used by the official TimeLens evaluation.
TIMELENS_OFFICIAL_PROMPT = (
    "Please find the visual event described by the sentence '{}', "
    "determining its starting and ending times. "
    "The format should be: 'The event happens in <start time> - <end time> seconds'."
)
# Alias used by eval_timelens_hf.py.
PROMPT_WO_THINK = TEMPORAL_GROUNDING_PROMPT
TEMPORAL_GROUNDING_PROMPT_MODES = {
    "same": TEMPORAL_GROUNDING_PROMPT,
    "timelens_official": TIMELENS_OFFICIAL_PROMPT,
}


# ===========================================================================
# Builder helpers
# ===========================================================================

def build_video_qa_mc_prompt(question, options_block):
    """`{question}\\nOptions:\\n{options_block}\\n{VIDEO_QA_MC_TAIL}`."""
    return f"{question}\nOptions:\n{options_block}\n{VIDEO_QA_MC_TAIL}"


def build_sensenova_mc_prompt(question):
    """`{question}\\n{SENSENOVA_MC_TAIL}`."""
    return f"{question}\n{SENSENOVA_MC_TAIL}"


def build_spatial_grounding_prompt(expression):
    """Training-aligned spatial grounding prompt."""
    expression = (expression or "").strip()
    if expression and expression[-1] not in ".?!":
        expression += "."
    return QWEN_NATIVE_PROMPT_SG.format(expression)


def build_tracking_prompt(question):
    return GROUNDING_QUESTION_TEMPLATE_NO_THINK.format(Question=question) + TRACKING_TAIL


def build_stvg_prompt(raw_query):
    q = TRAIN_STVG_QUESTION_PREFIX.format(query=raw_query)
    return GROUNDING_QUESTION_TEMPLATE_NO_THINK.format(Question=q) + STVG_TAIL


def build_seg_prompt(question, data_type):
    tail = TRAIN_SEG_VIDEO_TAIL if str(data_type).strip().lower() == "video" else TRAIN_SEG_IMAGE_TAIL
    return f"{question}\n{tail}"


def build_temporal_grounding_prompt(event, prompt_mode="same"):
    try:
        template = TEMPORAL_GROUNDING_PROMPT_MODES[prompt_mode]
    except KeyError as exc:
        choices = ", ".join(sorted(TEMPORAL_GROUNDING_PROMPT_MODES))
        raise ValueError(
            f"Unknown temporal grounding prompt mode {prompt_mode!r}; "
            f"choose one of: {choices}"
        ) from exc
    return template.format(event)