generated from kgod/ai-review-template
111 lines
4.0 KiB
Python
111 lines
4.0 KiB
Python
"""Import parsing speed-ups: slim schema without model evidence (A) + sha256 parse cache (B)."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import io
|
|
from typing import Any
|
|
|
|
from docx import Document
|
|
|
|
from app.import_parser import ImportParseOutput, OpenAIResumeImportParser
|
|
from app.resume_import_service import ResumeImportService
|
|
|
|
|
|
class FakeCompletion:
|
|
def __init__(self) -> None:
|
|
self.calls: list[dict[str, Any]] = []
|
|
|
|
def complete(self, **kwargs: Any) -> Any:
|
|
self.calls.append(kwargs)
|
|
return kwargs["schema"].model_validate(
|
|
{
|
|
"basics": {"name": "张三"},
|
|
"sections": [
|
|
{
|
|
"kind": "education",
|
|
"heading": "教育经历",
|
|
"items": [{"fields": {"school": "示例大学", "major": "软件工程"}}],
|
|
}
|
|
],
|
|
"skill_groups": [],
|
|
}
|
|
)
|
|
|
|
|
|
def _docx(text: str) -> bytes:
|
|
document = Document()
|
|
document.add_paragraph(text)
|
|
buffer = io.BytesIO()
|
|
document.save(buffer)
|
|
return buffer.getvalue()
|
|
|
|
|
|
def _service(tmp_path, completion: FakeCompletion) -> ResumeImportService:
|
|
return ResumeImportService(
|
|
storage_root=tmp_path / "imports",
|
|
parser=OpenAIResumeImportParser(completion=completion),
|
|
)
|
|
|
|
|
|
def test_service_uses_slim_schema_without_model_evidence(tmp_path) -> None:
|
|
"""模型不再逐条输出 evidence 引用(本地匹配已覆盖),输出 token 与时间同降。"""
|
|
from app.import_parser_fast import SlimImportParseOutput
|
|
|
|
completion = FakeCompletion()
|
|
prepared = _service(tmp_path, completion).prepare(
|
|
file_name="r.docx", declared_mime=None, content=_docx("张三 示例大学 软件工程")
|
|
)
|
|
|
|
assert completion.calls[0]["schema"] is SlimImportParseOutput
|
|
assert "evidence" not in completion.calls[0]["system_prompt"].casefold()
|
|
assert completion.calls[0]["timeout_seconds"] == 45.0
|
|
assert completion.calls[0]["max_attempts"] == 1
|
|
assert prepared["document"]["sections"][0]["items"][0]["school"] == "示例大学"
|
|
assert all(item["evidence"] for item in prepared["field_reviews"]) # 本地匹配仍然提供证据
|
|
|
|
|
|
def test_repeated_upload_of_same_file_skips_llm_parse(tmp_path) -> None:
|
|
"""同一文件跨会话重复上传命中 sha256 缓存,不再调 LLM 解析。"""
|
|
completion = FakeCompletion()
|
|
service = _service(tmp_path, completion)
|
|
content = _docx("张三 示例大学 软件工程")
|
|
|
|
first = service.prepare(file_name="a.docx", declared_mime=None, content=content)
|
|
second = service.prepare(file_name="b.docx", declared_mime=None, content=content)
|
|
|
|
assert len(completion.calls) == 1
|
|
assert second["document"] == first["document"]
|
|
assert second["sha256"] == first["sha256"]
|
|
|
|
|
|
|
|
def test_prepare_logs_timing_metadata_without_resume_content(tmp_path, monkeypatch) -> None:
|
|
completion = FakeCompletion()
|
|
events: list[dict[str, Any]] = []
|
|
monkeypatch.setattr(
|
|
"app.resume_import_service.log_ai_event",
|
|
lambda event, **fields: events.append({"event": event, **fields}),
|
|
)
|
|
service = _service(tmp_path, completion)
|
|
content = _docx("private resume text")
|
|
|
|
service.prepare(file_name="resume.docx", declared_mime=None, content=content)
|
|
service.prepare(file_name="resume-copy.docx", declared_mime=None, content=content)
|
|
|
|
assert [event["event"] for event in events] == [
|
|
"resume_import_prepared",
|
|
"resume_import_prepared",
|
|
]
|
|
first, second = events
|
|
for event in events:
|
|
assert {"extract_ms", "parse_ms", "validate_ms", "storage_ms", "total_ms"} <= event.keys()
|
|
assert event["file_extension"] == ".docx"
|
|
assert event["size_bytes"] == len(content)
|
|
assert "content" not in event
|
|
assert "payload" not in event
|
|
assert "private resume text" not in str(event)
|
|
assert first["cache_hit"] is False
|
|
assert first["text_characters"] > 0
|
|
assert second["cache_hit"] is True
|
|
assert second["text_characters"] is None
|