feat: unify scoring and chat evidence pages

This commit is contained in:
2026-09-27 17:47:32 +08:00
parent 039e93d0de
commit 83aa0e84a2
21 changed files with 511 additions and 132 deletions
+45 -6
View File
@@ -14,12 +14,13 @@ from wisegrade.actions import confirm, prepare
from wisegrade.agent_tools import execute
from wisegrade.db import get_db
from wisegrade.indexing import run_index_task
from wisegrade.llm import DeepSeekClient, ModelError
from wisegrade.llm import DeepSeekClient, ModelError, ScoreResult
from wisegrade.main import app
from wisegrade.models import (AgentRun, ChatMessage, Conversation, CourseVersion, CriterionScore, IndexTask, PendingAction,
Project, RubricVersion, ScoringBatch, Student, TeacherRevision, now)
Project, RubricVersion, ScoreEvidence, ScoringBatch, Student, SubmissionFile,
TeacherRevision, now)
from wisegrade.rag import ingest
from wisegrade.scoring import run_batch
from wisegrade.scoring import _score_criterion, run_batch
from wisegrade.storage import scan
@@ -93,9 +94,9 @@ def test_get_scores_pages_projects_and_criteria(db, monkeypatch):
assert first_page["results"][0]["project_id"] == first.id
assert len(first_page["results"][0]["rows"]) == 6
assert first_page["next_criterion_offset"] == 6 and first_page["next_project_offset"] == 1
assert first_page["citations"][0]["url"] == f"/?page=scores&batch_id={created['id']}&project_id={first.id}"
assert first_page["citations"][0]["image_url"] == f"/api/assets/{first.id}/1"
assert first_page["citations"][0]["text_url"] == "/api/knowledge/documents/100/pages/1"
assert first_page["citations"][0]["url"] is None
assert first_page["citations"][0]["image_url"] is None
assert first_page["citations"][0]["score_url"] == f"/?page=scores&batch_id={created['id']}&project_id={first.id}"
assert first_page["citations"][1]["url"] == first_page["citations"][0]["url"]
assert first_page["citations"][1]["image_url"] is None
assert len(json.dumps(first_page, ensure_ascii=False)) < 12000
@@ -106,6 +107,44 @@ def test_get_scores_pages_projects_and_criteria(db, monkeypatch):
assert later["next_project_offset"] is None and later["next_criterion_offset"] is None
def test_scoring_uses_the_same_page_image_as_its_saved_evidence(db, environment):
project = add_project(db, "123450090")
created = services.create_batch(SimpleNamespace(course_version_id=1, rubric_version_id=1), db)
batch = db.get(ScoringBatch, created["id"])
criterion = batch.rubric_snapshot["criteria"][0]
file = SubmissionFile(project_id=project.id, category=criterion["category"],
relative_path="test/score/source.pdf", mime_type="application/pdf", size=1,
mtime=1, sha256="b" * 64, recognition_status="recognized", parse_status="ready")
db.add(file)
db.flush()
image = environment / "processed" / f"file-{file.id}" / "page-2.png"
image.parent.mkdir(parents=True)
image.write_bytes(b"\x89PNG\r\n\x1a\n")
source = {"page_number": 2, "text": "评分所用原文证据", "rendered_image_path": str(image)}
file.parsed = {"pages": [source]}
document = ingest(db, course_version_id=1, project_id=project.id, submission_file_id=file.id,
title="报告.pdf", category=criterion["category"], source_hash=file.sha256,
pages=[source], embedder=lambda texts: [[0.1] * 1024 for _ in texts])
from wisegrade.models import KnowledgeChunk
chunk = db.scalar(select(KnowledgeChunk).where(KnowledgeChunk.document_id == document.id))
evidence = {"chunk_id": chunk.id, "document_id": document.id, "project_id": project.id,
"page": 2, "text": chunk.text}
sent_images = []
class FakeClient:
def json_call(self, _prompt, _payload, _schema, *, images=None):
sent_images.extend(images or [])
return ScoreResult(criterion_id=criterion["criterion_id"], score=1,
max_score=criterion["max_score"], reason="测试评分",
evidence=[{"chunk_id": chunk.id, "page": 2, "quote": "原文证据"}], confidence=.9)
scored = _score_criterion(db, batch, project, criterion, FakeClient(),
retriever=lambda *_, **kwargs: [] if kwargs.get("public_only") else [evidence])
saved = db.scalar(select(ScoreEvidence).where(ScoreEvidence.score_id == scored.id))
assert sent_images == [str(image)]
assert saved.document_id == document.id and saved.page == 2 and saved.image_path == str(image)
def test_import_confirmation_hash_change_index_and_idempotency(environment, db, monkeypatch, session_factory):
folder = environment / "inbox" / "batch" / "123450003_张三_机器人"
folder.mkdir(parents=True)