Add FireRed OCR comparison and preserve attribution layout

This commit is contained in:
Kai Tan
2026-10-29 00:14:13 +08:00
parent cad96d59c8
commit 166ea871d3
10 changed files with 355 additions and 16 deletions
+70
View File
@@ -143,3 +143,73 @@ def test_corrections_require_source_and_exact_expected_text():
applied=apply_corrections(pages,patch,'source')
assert pages[0]['lines'][0]['text']=='牲畜' and len(applied)==1
with pytest.raises(ValueError,match='Stale'):apply_corrections(pages,patch,'source')
def test_signature_and_date_keep_separate_right_aligned_paragraphs():
page={'number':1,'lines':[{'id':'body','text':'正文最后一段。','bbox':[.1,.4,.85,.44]}, {'id':'name','text':'作者甲','bbox':[.72,.50,.84,.52]}, {'id':'date','text':'2020.2.16','bbox':[.70,.54,.84,.56]}]}
blocks,_=paragraphs(page,'zh-CN',set())
assert [b['text'] for b in blocks][-2:]==['作者甲','2020.2.16']
assert all(b.get('alignment')=='right' and b.get('role')=='attribution' for b in blocks[-2:])
from src.pdf_import.markdown_layout import markdown_blocks
md=markdown_blocks({'number':1,'markdown':'识别后的正文最后一段。\n\n作者甲\n\n2020.2.16'},'zh-CN',page)
assert md[0]['text']=='识别后的正文最后一段。'
assert all(b.get('alignment')=='right' for b in md[-2:])
assert md[-1]['geometry_source']=='reference-vision-text-alignment'
assert md[-1]['line_ids']==[] # Never pretend FireRed emitted coordinates or Vision text.
def test_firered_http_cache_and_truncation_rejection(tmp_path,monkeypatch):
from PIL import Image
import io
import src.pdf_import.firered as firered
calls=[]
def response(request,timeout):
payload=json.loads(request.data);calls.append(payload)
return io.BytesIO(json.dumps({'model':'firered-ocr','choices':[{'finish_reason':'stop','message':{'content':'原文段落。\n\n作者甲'}}],'usage':{'completion_tokens':10}}).encode())
monkeypatch.setattr(firered,'urlopen',response)
image=tmp_path/'page.png';Image.new('RGB',(40,60),'white').save(image)
engine=firered.FireRedOCR(tmp_path,'zh-CN',base_url='http://ocr.test:8001')
result=engine.recognize(image);assert result['markdown'].startswith('原文段落') and result['lines']==[]
assert engine.recognize(image)==result and len(calls)==1
assert calls[0]['model']=='firered-ocr' and calls[0]['temperature']==0
def truncated(request,timeout):
return io.BytesIO(json.dumps({'choices':[{'finish_reason':'length','message':{'content':'仅一部分'}}]}).encode())
monkeypatch.setattr(firered,'urlopen',truncated)
rejected=firered.FireRedOCR(tmp_path/'rejected','zh-CN',base_url='http://ocr.test:8001')
with pytest.raises(RuntimeError,match='incomplete'):rejected.recognize(image)
assert list((tmp_path/'rejected/firered-raw').glob('*.rejected.json'))
def test_snippet_comparison_excludes_page_context_and_counts_missing_characters():
from scripts.compare_pdf_ocr import snippet_errors
assert snippet_errors('原始正文','页眉原始正文页脚')['errors']==0
result=snippet_errors('拥有识字能力','页眉有识字能力页脚')
assert result['errors']==1
assert sum(result[k] for k in ['substitutions','deletions','insertions'])==1
def test_markdown_ocr_is_exported_as_text_not_an_image(native_pdf,tmp_path,monkeypatch):
import src.pdf_import.firered as firered
from src.pdf_import.converter import digest,save_json
class FakeOCR:
identity='markdown-fixture'
def __init__(self,*args,**kwargs):pass
def recognize(self,image):return {'lines':[],'markdown':'这是重新识别后的完整正文段落,保留新模型文字而不是旧模型的文字内容。\n\n作者甲\n\n2020.2.16'}
def close(self):pass
monkeypatch.setattr(firered,'FireRedOCR',FakeOCR)
source=native_pdf(image=True);work=tmp_path/'work';geom=tmp_path/'geometry';save_json(geom/'source.json',{'sha256':digest(source)})
for n in [1,2]:
save_json(geom/'pages'/f'{n:04d}.json',{'number':n,'stamp':{'source':digest(source)},'lines':[{'id':f'p{n}-a','text':'这是旧模型的正文段落。','bbox':[.1,.2,.9,.3]},{'id':f'p{n}-b','text':'作者甲','bbox':[.72,.5,.84,.52]},{'id':f'p{n}-c','text':'2020.2.16','bbox':[.7,.54,.84,.56]}]})
out=tmp_path/'md.epub';convert(source,out,work,engine='firered',ocr_url='http://ocr.test',geometry_work=geom)
with zipfile.ZipFile(out) as z:
text=''.join(z.read(n).decode() for n in z.namelist() if '/chapters/' in n)
assert '重新识别后的完整正文' in text and 'class="attribution"' in text
assert '作者甲2020' not in text
assert audit_epub(out)['issues']==[]
def test_markdown_does_not_erase_uncertain_sentence_spacing():
from src.pdf_import.markdown_layout import markdown_blocks
blocks=markdown_blocks({'number':1,'markdown':'前一句 后一句'},'zh-CN')
assert blocks[0]['text']=='前一句 后一句'
assert blocks[0]['bbox'] is None