fix: improve import, evidence indexing and material-scoped grading
Preserve document revisions and evidence, harden indexing and export behavior, add explicit material selection and direct-folder import, validate model JSON schemas with safe error messages, and retain student selection while loading scoring data.
This commit is contained in:
@@ -11,7 +11,7 @@ from .actions import prepare, require_scope
|
||||
from .evidence import CONTENT_ALLOWED, STRUCTURE_ALLOWED
|
||||
from .exports import STATUS_LABELS, batch_projects, page_type_label, project_result
|
||||
from .knowledge_pages import page_links
|
||||
from .models import (Conversation, Course, CourseVersion, IndexTask, KnowledgeChunk, KnowledgeDocument,
|
||||
from .models import (Conversation, Course, CourseVersion, IndexTask, KnowledgeChunk, KnowledgeDocument, KnowledgePage,
|
||||
Project, RubricVersion, ScoringBatch, Student, SubmissionFile)
|
||||
from .pages import toc_entries
|
||||
from .rag import retrieve
|
||||
@@ -233,7 +233,8 @@ def _search(db, version_id, project_id, query, *, public_only=False, scopes=None
|
||||
"source_role": "structure" if "structure" in scopes else "content",
|
||||
# Stable identity so an answer can cite exactly what it used.
|
||||
"citation_id": f"chunk:{chunk['chunk_id']}",
|
||||
**links})
|
||||
"answer_url": f"{links['url']}#chunk-{chunk['chunk_id']}",
|
||||
"url": links["url"], "image_url": links["image_url"]})
|
||||
return {"citations": citations, "found": bool(citations), "scopes": list(scopes)}
|
||||
|
||||
|
||||
@@ -271,9 +272,10 @@ def search_structure(db, convo, args, _user):
|
||||
"section": chunk.get("section") or "", "block_type": chunk.get("block_type") or "body",
|
||||
"source_role": "structure",
|
||||
"citation_id": f"chunk:{chunk['chunk_id']}",
|
||||
"answer_url": f"{links['url']}#chunk-{chunk['chunk_id']}",
|
||||
"toc_entries": toc_entries([{"kind": "toc", "text": chunk["text"]}])[:40]
|
||||
if page_type == "toc" else [],
|
||||
**links})
|
||||
"url": links["url"], "image_url": links["image_url"]})
|
||||
return {"citations": citations, "found": bool(citations), "scopes": ["structure"],
|
||||
"note": "这些是结构或目录信息,只能说明章节安排,不能证明章节内容。"}
|
||||
|
||||
@@ -281,16 +283,18 @@ def search_structure(db, convo, args, _user):
|
||||
def get_document_pages(db, convo, args, _user):
|
||||
"""List a document's pages with page type, so a caller can navigate precisely."""
|
||||
_document(db, convo, args.document_id)
|
||||
rows = db.scalars(select(KnowledgeChunk).where(KnowledgeChunk.document_id == args.document_id)
|
||||
.order_by(KnowledgeChunk.page, KnowledgeChunk.id)).all()
|
||||
rows = db.scalars(select(KnowledgePage).where(KnowledgePage.document_id == args.document_id)
|
||||
.order_by(KnowledgePage.page)).all()
|
||||
pages: dict[int, dict] = {}
|
||||
for chunk in rows:
|
||||
entry = pages.setdefault(chunk.page, {"page": chunk.page, "page_type": chunk.page_type,
|
||||
"page_type_label": page_type_label(chunk.page_type),
|
||||
"chapter": chunk.chapter, "section": chunk.section,
|
||||
"block_types": []})
|
||||
if chunk.block_type not in entry["block_types"]:
|
||||
entry["block_types"].append(chunk.block_type)
|
||||
for block in (chunk.classification or {}).get("blocks", []):
|
||||
kind = block.get("kind")
|
||||
if kind and kind not in entry["block_types"]:
|
||||
entry["block_types"].append(kind)
|
||||
if chunk.section:
|
||||
entry["section"] = chunk.section
|
||||
return {"document_id": args.document_id, "count": len(pages),
|
||||
@@ -300,9 +304,9 @@ def get_document_pages(db, convo, args, _user):
|
||||
def search_document(db, convo, args, _user):
|
||||
"""Content search restricted to one document, under the same page-type policy."""
|
||||
document = _document(db, convo, args.document_id)
|
||||
chunks = retrieve(db, course_version_id=document.course_version_id, project_id=document.project_id or 0,
|
||||
chunks = retrieve(db, course_version_id=document.course_version_id, project_id=document.project_id,
|
||||
query=args.query, limit=6, include_public=False,
|
||||
allowed_page_types=sorted(CONTENT_ALLOWED), substantive_only=True)
|
||||
allowed_page_types=sorted(CONTENT_ALLOWED), substantive_only=True, document_id=document.id)
|
||||
citations = []
|
||||
for chunk in chunks:
|
||||
if chunk["document_id"] != document.id:
|
||||
@@ -313,7 +317,9 @@ def search_document(db, convo, args, _user):
|
||||
"project_id": chunk["project_id"], "page_type": chunk.get("page_type"),
|
||||
"page_type_label": page_type_label(chunk.get("page_type")),
|
||||
"section": chunk.get("section") or "", "source_role": "content",
|
||||
"citation_id": f"chunk:{chunk['chunk_id']}", **links})
|
||||
"citation_id": f"chunk:{chunk['chunk_id']}",
|
||||
"answer_url": f"{links['url']}#chunk-{chunk['chunk_id']}",
|
||||
"url": links["url"], "image_url": links["image_url"]})
|
||||
return {"citations": citations, "found": bool(citations), "scopes": ["content"]}
|
||||
|
||||
|
||||
@@ -402,7 +408,7 @@ def scores(db, convo, args, _user):
|
||||
"target_label": str(evidence.get("target_label") or "")[:120],
|
||||
"historical": bool(evidence.get("historical")),
|
||||
"source": "historical_score_evidence" if evidence.get("historical") else "score_evidence",
|
||||
**links, "score_url": url})
|
||||
"url": links["url"], "image_url": links["image_url"], "score_url": url})
|
||||
if citations:
|
||||
# Scoring quotations are never presented as report body evidence in an answer.
|
||||
for citation in citations:
|
||||
|
||||
Reference in New Issue
Block a user