feat(ai): gRPC clients 扩展 + server interceptors + proto_gen + 测试 + nextstep 文档

This commit is contained in:
SpecialX
2026-07-14 15:59:41 +08:00
parent fb23c5234e
commit 7b790f1276
21 changed files with 3540 additions and 345 deletions

View File

@@ -23,13 +23,15 @@ from src.ai.workflow.state_store import WorkflowState, WorkflowStateStore
from .conftest import MockProvider
# 有效的 LLM JSON 输出(通过三道防线评估)
VALID_QUESTION_JSON = json.dumps({
"question": "什么是函数?",
"answer": "函数是一种对应关系",
"explanation": "函数定义",
"difficulty": "medium",
"question_type": "short_answer",
})
VALID_QUESTION_JSON = json.dumps(
{
"question": "什么是函数?",
"answer": "函数是一种对应关系",
"explanation": "函数定义",
"difficulty": "medium",
"question_type": "short_answer",
}
)
def _make_chain(provider: MockProvider | None = None) -> ProviderFailoverChain:
@@ -135,11 +137,16 @@ class TestLessonPlanWorkflowConfirm:
store = WorkflowStateStore(redis=None)
state = _make_state(
status="pending_review",
questions=[GeneratedQuestionData(
question="q1", answer="a1", explanation="e1",
question_type="short_answer", difficulty="easy",
knowledge_point_ids=["kp_1"],
)],
questions=[
GeneratedQuestionData(
question="q1",
answer="a1",
explanation="e1",
question_type="short_answer",
difficulty="easy",
knowledge_point_ids=["kp_1"],
)
],
)
await store.create(state)
svc = _make_service(store=store, content_client=ContentClientMock())
@@ -166,11 +173,16 @@ class TestLessonPlanWorkflowConfirm:
store = WorkflowStateStore(redis=None)
state = _make_state(
status="pending_review",
questions=[GeneratedQuestionData(
question="original", answer="a1", explanation="e1",
question_type="short_answer", difficulty="easy",
knowledge_point_ids=["kp_1"],
)],
questions=[
GeneratedQuestionData(
question="original",
answer="a1",
explanation="e1",
question_type="short_answer",
difficulty="easy",
knowledge_point_ids=["kp_1"],
)
],
)
await store.create(state)
@@ -200,7 +212,7 @@ class TestLessonPlanWorkflowSteps:
analysis = await svc._step1_analyze(state)
assert "class_performance" in analysis
assert analysis["class_performance"]["average_score"] == 78.5
assert analysis["class_performance"]["student_count"] == 3
assert analysis["class_performance"]["student_count"] == 2
assert "weak_students" in analysis
async def test_step1_analyze_no_client_degraded(self) -> None:
@@ -214,13 +226,14 @@ class TestLessonPlanWorkflowSteps:
svc = _make_service(content_client=ContentClientMock())
state = _make_state()
kps = await svc._step2_recommend(state)
assert len(kps) == 3
assert len(kps) == 2
assert kps[0]["id"] == "kp_001"
async def test_step2_recommend_no_client_fallback(self) -> None:
svc = _make_service(content_client=None)
state = _make_state(topic="函数")
kps = await svc._step2_recommend(state)
# 无 content_client 时降级到内置 3 个默认知识点
assert len(kps) == 3
assert "基础概念" in kps[0]["title"]
assert "函数" in kps[0]["title"]
@@ -233,7 +246,8 @@ class TestLessonPlanWorkflowSteps:
)
state = _make_state(question_count=1, target_difficulty="medium")
questions = await svc._step3_generate(
state, [{"id": "kp_1", "title": "KP1"}],
state,
[{"id": "kp_1", "title": "KP1"}],
)
assert len(questions) == 1
assert questions[0].question == "什么是函数?"
@@ -245,7 +259,8 @@ class TestLessonPlanWorkflowSteps:
svc = _make_service(provider=provider)
state = _make_state(question_count=1)
questions = await svc._step3_generate(
state, [{"id": "kp_1", "title": "KP1"}],
state,
[{"id": "kp_1", "title": "KP1"}],
)
assert len(questions) == 1
assert questions[0].degraded is True