Skip to content

fix(attachment): TextLoader 긴 한 줄 txt/json/md 청킹 잘림 수정 (#333) - #334

Merged
inoray merged 3 commits into
developfrom
bugfix/333-bug-txtmdjson-청킹-시-a4-폭-초과-텍스트-누락
Jul 24, 2026

Hidden character warning

The head ref may contain hidden characters: "bugfix/333-bug-txtmdjson-\uccad\ud0b9-\uc2dc-a4-\ud3ed-\ucd08\uacfc-\ud14d\uc2a4\ud2b8-\ub204\ub77d"
Merged

fix(attachment): TextLoader 긴 한 줄 txt/json/md 청킹 잘림 수정 (#333)#334
inoray merged 3 commits into
developfrom
bugfix/333-bug-txtmdjson-청킹-시-a4-폭-초과-텍스트-누락

Conversation

@HeechanKim-Genon

@HeechanKim-Genon HeechanKim-Genon commented Jul 23, 2026

Copy link
Copy Markdown
Collaborator

fix(attachment): TextLoader 긴 한 줄 txt/json/md 청킹 잘림 수정 (#333)

개요

줄바꿈 없이 한 줄이 긴 txt/json/md 파일을 기본 첨부 전처리기(attachment 모드)로 적재하면, PDF 변환 시 A4 페이지 좌우 폭을 넘어가는 부분이 렌더링되지 않고(잘림), 청킹에서도 넘친 텍스트가 통째로 누락되던 문제를 수정함.

  • txt/json: PDF 뷰어·청킹 둘 다 잘림
  • md: PDF 뷰어는 정상(줄바꿈됨)인데 청킹만 잘림

Closes #333

원인

txt/json/md 는 TextLoader.load() 가 원문을 <pre>{content}</pre> HTML 로 감싸 weasyprint 로 PDF 변환 → 그 PDF 를 PyMuPDF 로 파싱해 청킹함.

그런데 <pre> 의 기본값이 white-space: pre자동 줄바꿈을 하지 않음. 그래서:

  • 긴 줄이 페이지 박스를 넘어감
  • weasyprint 가 넘친 부분을 PDF 에 아예 그리지 않음(discard)
  • PyMuPDF 추출·청킹 단계에서 그대로 소실

md 만 "뷰어는 멀쩡, 청킹만 잘림" 인 이유는 경로가 갈리기 때문:

용도 경로 결과
청킹용 텍스트 추출 get_loaderTextLoader (<pre>) 잘림
표시용(뷰어) PDF compose_vectorsconvert_md_to_pdf (markdown → <p>, 자동 wrap) 정상

두 PDF 모두 weasyprint 로 생성되며(LibreOffice 아님), 차이는 HTML 구조(<pre> no-wrap vs <p> auto-wrap)뿐. 즉 두 증상(txt/json/md)의 근본 원인은 하나 — TextLoader<pre> 로 감싸 긴 줄을 wrap 하지 않는 것. (버전 무관 — v2.2.2 / v2.2.3 / develop 동일)

변경 사항

genon/preprocessor/facade/attachment_processor.py

  • 상단에 import html 추가
  • TextLoader.load()<pre> HTML 생성부 수정:
    • white-space: pre-wrap → 원문 줄바꿈/공백 유지 + 폭 초과 시 자동 줄바꿈
    • overflow-wrap: anywhere → 공백 없는 초장문(URL 등)도 강제 개행
    • html.escape(content)<, & 등이 태그로 해석돼 뒤 텍스트가 유실되는 것 방지
    • 로컬 변수 htmlhtml_doc 개명 (신규 html 모듈과 충돌 회피)
html_doc = (
    "<html><meta charset='utf-8'><body>"
    "<pre style='white-space: pre-wrap; overflow-wrap: anywhere;'>"
    f"{html.escape(content)}</pre></body></html>"
)

이 한 곳 수정으로 txt/json/md 청킹 누락이 함께 해결됨. md 표시용 convert_md_to_pdf 경로는 이미 정상이라 수정 불필요.

테스트

genon/preprocessor/tests/unit/test_attachment_textloader_long_line_333.py 신규 추가 (기존 test_attachment_processor_samples.py 컨벤션 준수: @pytest.mark.unit, _import_processor sys.path 폴백, weasyprint 미설치 시 skip)

  • test_long_single_line_txt_not_truncated — 줄바꿈 없는 긴 txt 의 끝 토큰이 청킹 결과에 보존되는지
  • test_html_special_chars_preserved<, &, 태그 유사 문자열 뒤 텍스트 보존(escape) 검증
  • test_long_single_line_json_not_truncated — json 도 동일 경로라 끝 토큰 보존

검증 결과

TextLoader.load() 의 PDF 경로(escape + pre-wrap → weasyprint → PyMuPDF)를 동일 입력으로 재현해 확인:

  • 세 테스트 모두 수정본에서 통과
  • 대조로 수정 전 <pre> 경로에선 실패(긴 줄 끝 토큰·escape 뒤 토큰 유실) → 진짜 회귀 가드로 작동함을 확인

참고: 저장소 전체 pytest 는 프로젝트 의존성(pandas·langchain·docling 등) 환경에서 실행 필요.

영향 범위

  • tabular / docling 모드 무관, 기본 첨부(attachment) 모드의 txt/json/md 경로에만 영향
  • 기존 정상 문서(짧은 줄)의 렌더 결과는 동일 — pre-wrap 은 원문 줄바꿈/공백을 그대로 유지함
  • 수정 후 대상 문서 재적재 필요

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes

    • Improved text-to-PDF rendering for long single-line TXT and JSON files, preventing content from being truncated.
    • Preserved special characters and markup-like text correctly during conversion.
    • Improved line wrapping for long text in generated PDFs.
  • Tests

    • Added regression coverage for long lines and special-character handling.

<pre> 기본값(white-space: pre)이 자동 줄바꿈을 하지 않아 A4 폭을 넘는 긴 줄이
weasyprint 렌더 단계에서 잘려(discard) PDF·청킹에서 누락되던 문제 수정.

- white-space: pre-wrap 으로 원문 유지 + 폭 초과 시 자동 줄바꿈
- overflow-wrap: anywhere 로 공백 없는 초장문도 강제 개행
- html.escape 로 <, & 태그 오해석에 의한 텍스트 유실 방지
- 로컬 변수 html → html_doc 개명(신규 html 모듈 충돌 회피)
- 이슈 #333 회귀 유닛 테스트 추가(txt/json 끝 토큰 보존, 특수문자 escape)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@gemini-code-assist

Copy link
Copy Markdown

Caution

The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased.

@coderabbitai

coderabbitai Bot commented Jul 23, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@HeechanKim-Genon, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 30 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 8d2b64b3-d221-4bce-8d09-f76ac780f913

📥 Commits

Reviewing files that changed from the base of the PR and between ab9c08d and 8e74a66.

📒 Files selected for processing (1)
  • genon/preprocessor/tests/unit/test_attachment_textloader_long_line_333.py
📝 Walkthrough

Walkthrough

TextLoader.load now wraps and HTML-escapes extracted text before WeasyPrint PDF rendering. New regression tests verify preservation of long single-line TXT, JSON, and HTML-like content.

Changes

TextLoader long-line preservation

Layer / File(s) Summary
HTML preparation for PDF rendering
genon/preprocessor/facade/attachment_processor.py
TextLoader.load writes escaped content in a styled <pre> using wrapping rules before PDF generation.
Long-line regression coverage
genon/preprocessor/tests/unit/test_attachment_textloader_long_line_333.py
Tests cover long TXT and JSON inputs, HTML special characters, text normalization, and environments without WeasyPrint.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Possibly related issues

  • #333 — The change addresses long single-line TextLoader truncation and adds regression coverage.

Suggested reviewers: inoray

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed Title clearly reflects the TextLoader truncation fix for long single-line attachments, though it slightly overstates the affected formats by mentioning md.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch bugfix/333-bug-txtmdjson-청킹-시-a4-폭-초과-텍스트-누락

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@genon/preprocessor/tests/unit/test_attachment_textloader_long_line_333.py`:
- Around line 85-98: Update test_html_special_chars_preserved to assert that the
normalized extracted text retains the literal markers "<b>", "</b>", "a<b", and
"&", or compare against the fully normalized expected content; keep the existing
token-preservation checks as appropriate.
- Around line 65-69: Extend the long-line fixture around head, tail_token, and
content with a sufficiently long space-free token, and assert that this token’s
tail is present in the extracted PDF text. Keep the existing spaced-content
coverage while ensuring the new assertion exercises overflow-wrap: anywhere for
identifier-like strings.
- Around line 32-37: Update _has_weasyprint to catch only the expected
missing-dependency import exception for optional WeasyPrint availability,
allowing other import failures such as transitive or CFFI errors to propagate
instead of returning False.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 4d11d15d-174c-433f-9fe2-e85a8a4811a6

📥 Commits

Reviewing files that changed from the base of the PR and between e3f0656 and ab9c08d.

📒 Files selected for processing (2)
  • genon/preprocessor/facade/attachment_processor.py
  • genon/preprocessor/tests/unit/test_attachment_textloader_long_line_333.py

Comment thread genon/preprocessor/tests/unit/test_attachment_textloader_long_line_333.py Outdated
Comment on lines +85 to +98
def test_html_special_chars_preserved(tmp_path: Path):
"""<, & 등 HTML 특수문자가 태그로 해석돼 뒤 텍스트가 유실되지 않아야 한다(html.escape)."""
_get_pdf_path, TextLoader = _import_processor()

# escape 없으면 '<b>' 이후 '중요' 는 태그로 먹히고, 'a<b 그리고 ... ' 도 유실됨.
content = "머리말 <b>중요구절</b> 그리고 조건 a<b 이고 기호 & 앰퍼샌드 끝토큰ESCAPE333"

txt = tmp_path / "special_chars.txt"
txt.write_text(content, encoding="utf-8")

norm = _norm(_extract_text(TextLoader(str(txt))))

for token in ("중요구절", "앰퍼샌드", "끝토큰ESCAPE333"):
assert _norm(token) in norm, f"escape 누락으로 '{token}' 이 유실됨"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Assert the literal special characters.

The current assertions check surrounding words, not whether <b>, </b>, a<b, and & survive escaping. Include those exact markers, or compare normalized expected content, so punctuation loss cannot pass unnoticed.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@genon/preprocessor/tests/unit/test_attachment_textloader_long_line_333.py`
around lines 85 - 98, Update test_html_special_chars_preserved to assert that
the normalized extracted text retains the literal markers "<b>", "</b>", "a<b",
and "&", or compare against the fully normalized expected content; keep the
existing token-preservation checks as appropriate.

HeechanKim-Genon and others added 2 commits July 24, 2026 10:41
CI 러너에 한글 폰트가 없어 weasyprint→PDF→PyMuPDF round-trip 에서 CJK 글리프가
.notdef 로 깨져(추출 텍스트 손상) 테스트가 실패했음. 잘림/escape 버그는 언어
무관이므로 콘텐츠를 ASCII 로 교체해 폰트 비의존으로 만들고 검증력은 유지.

- pre-wrap(공백 있는 긴 줄) / overflow-wrap(공백 없는 초장문) 케이스 분리
- escape 검증도 ASCII 토큰으로 교체

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
CodeRabbit 리뷰(Ruff BLE001) 반영. blind except 대신 실제로 발생하는 두 경우만
잡는다: ImportError(패키지 미설치), OSError(pango/gobject 등 네이티브 라이브러리
로드 실패). 프로덕션 weasyprint 가드도 두 경우 모두 폴백하므로 skip 판정과 일치하며,
그 외 예기치 않은 예외는 표면화된다.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@HeechanKim-Genon
HeechanKim-Genon requested a review from inoray July 24, 2026 04:36
@inoray
inoray merged commit b6937de into develop Jul 24, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] txt/md/json 청킹 시 A4 폭 초과 텍스트 누락

2 participants