feat: add mirror question generation with canonical distinctness check
This commit is contained in:
@@ -0,0 +1,75 @@
|
||||
"""镜像生成:成功造出正解相反的镜像;正解相同/生成 null → 返回 None。"""
|
||||
|
||||
import pytest
|
||||
|
||||
from app.question_gen.adversarial_filter import generate_mirror_question
|
||||
from core.types import GeneratedQuestion, LLMResponse
|
||||
|
||||
|
||||
class _FakeVLM:
|
||||
def __init__(self, content: str):
|
||||
self._content = content
|
||||
|
||||
async def chat_with_images(self, messages, images, *, session_id=None, parent_call_id=None):
|
||||
return LLMResponse(
|
||||
content=self._content, thinking="", model="fake", provider="fake",
|
||||
prompt_tokens=0, completion_tokens=0, latency_ms=0,
|
||||
ttft_ms=None, max_inter_token_ms=None, cache_hit=False, call_id="c",
|
||||
)
|
||||
|
||||
|
||||
def _q():
|
||||
return GeneratedQuestion(
|
||||
question_id="q1", video_id="v1", task_type="Action Recognition",
|
||||
question="X 之前做了什么?", options=("A. 蒸", "B. 炒", "C. 煮", "D. 炸"),
|
||||
answer="A", source_nodes=("n1",), difficulty="hard",
|
||||
sub_pattern="temporal_reasoning_failure",
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_mirror_distinct_correct_ok():
|
||||
vlm = _FakeVLM('{"mirror": {"question": "X 之后做了什么?", '
|
||||
'"options": ["A. 炒", "B. 蒸", "C. 煮", "D. 炸"], "answer": "A"}}')
|
||||
mirror = await generate_mirror_question(
|
||||
_q(), flip_axis="before/after", vlm=vlm, material=_FakeMaterial(), session_id="s",
|
||||
)
|
||||
assert mirror is not None
|
||||
# 原正解 canonical="蒸",镜像正解 canonical="炒" → 相异,有效
|
||||
assert mirror.answer == "A"
|
||||
assert mirror.options[0] == "A. 炒"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_mirror_same_correct_rejected():
|
||||
# 镜像正解 canonical 仍是"蒸" → 造不出有效对 → None
|
||||
vlm = _FakeVLM('{"mirror": {"question": "X 之后?", '
|
||||
'"options": ["A. 蒸", "B. 炒", "C. 煮", "D. 炸"], "answer": "A"}}')
|
||||
mirror = await generate_mirror_question(
|
||||
_q(), flip_axis="before/after", vlm=vlm, material=_FakeMaterial(), session_id="s",
|
||||
)
|
||||
assert mirror is None
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_mirror_null_returns_none():
|
||||
vlm = _FakeVLM('{"mirror": null}')
|
||||
mirror = await generate_mirror_question(
|
||||
_q(), flip_axis="before/after", vlm=vlm, material=_FakeMaterial(), session_id="s",
|
||||
)
|
||||
assert mirror is None
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_mirror_malformed_response_returns_none():
|
||||
# 畸形 VLM 响应(连 json_repair 都救不回)不得抛异常中断本轮,须返 None
|
||||
vlm = _FakeVLM("对不起,我无法完成这个请求。")
|
||||
mirror = await generate_mirror_question(
|
||||
_q(), flip_axis="before/after", vlm=vlm, material=_FakeMaterial(), session_id="s",
|
||||
)
|
||||
assert mirror is None
|
||||
|
||||
|
||||
class _FakeMaterial:
|
||||
subtitle_sentences = ["先炒后蒸"]
|
||||
frame_paths = ["/f1.jpg"]
|
||||
Reference in New Issue
Block a user