feat(core): 追加 GeneratedQuestion frozen dataclass

跨层共享类型,被 core/evolution/ 和 app/harness/、app/question_gen/ 使用。
frozen=True + tuple 字段确保不可变。无默认值(显式传入)。

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-07 04:36:56 -04:00
parent 4686adf266
commit eea3bcba3f
2 changed files with 81 additions and 4 deletions
+53 -4
View File
@@ -1,9 +1,10 @@
"""core/types.py 单元测试。"""
from __future__ import annotations
import pytest
from core.types import LLMResponse
from core.types import GeneratedQuestion, LLMResponse
class TestLLMResponse:
@@ -42,10 +43,58 @@ class TestLLMResponse:
def test_cache_hit_response_has_none_ttft(self) -> None:
resp = LLMResponse(
content="cached", thinking="", model="m", provider="p",
prompt_tokens=0, completion_tokens=0, latency_ms=1,
ttft_ms=None, max_inter_token_ms=None, cache_hit=True, call_id="c",
content="cached",
thinking="",
model="m",
provider="p",
prompt_tokens=0,
completion_tokens=0,
latency_ms=1,
ttft_ms=None,
max_inter_token_ms=None,
cache_hit=True,
call_id="c",
)
assert resp.ttft_ms is None
assert resp.max_inter_token_ms is None
assert resp.cache_hit is True
class TestGeneratedQuestion:
@pytest.fixture()
def sample_question(self) -> GeneratedQuestion:
return GeneratedQuestion(
question_id="719-1",
video_id="B7Hh0PY1kks",
task_type="Action Reasoning",
question="What are the differing motivations?",
options=("A. Option 1", "B. Option 2", "C. Option 3", "D. Option 4"),
answer="B",
source_nodes=(),
difficulty="medium",
)
def test_frozen_prevents_mutation(self, sample_question: GeneratedQuestion) -> None:
with pytest.raises(AttributeError):
sample_question.question = "篡改"
def test_all_fields_accessible(self, sample_question: GeneratedQuestion) -> None:
assert sample_question.question_id == "719-1"
assert sample_question.video_id == "B7Hh0PY1kks"
assert sample_question.task_type == "Action Reasoning"
assert sample_question.question == "What are the differing motivations?"
assert sample_question.options == (
"A. Option 1",
"B. Option 2",
"C. Option 3",
"D. Option 4",
)
assert sample_question.answer == "B"
assert sample_question.source_nodes == ()
assert sample_question.difficulty == "medium"
def test_options_is_tuple(self, sample_question: GeneratedQuestion) -> None:
assert isinstance(sample_question.options, tuple)
def test_source_nodes_is_tuple(self, sample_question: GeneratedQuestion) -> None:
assert isinstance(sample_question.source_nodes, tuple)