Skip to content

feat(text): id 붙은 글 덩어리 순회·예산 안 요약 읽기·id 일괄 바꿈 (#666, #667, #668) - #671

Merged
airmang merged 1 commit into
mainfrom
feat/local-llm-text-blocks
Nov 6, 2026
Merged

airmang merged 1 commit into
mainfrom
feat/local-llm-text-blocks

Conversation

@airmang

@airmang airmang commented Nov 6, 2026

Copy link
Copy Markdown
Owner

Closes #666
Closes #667
Closes #668

작은(로컬) 모델이 문서 전체 구조 대신 id 붙은 짧은 목록만 읽고, 바꿀 글만 {id: 새 글}로 돌려주면 코드가 문서에 넣는 흐름을 엔진에서 바로 제공합니다. 이슈에 적힌 우회 코드를 그대로 대체합니다.

추가한 API (doc.text 네임스페이스)

이슈는 doc.iter_text_blocks()처럼 루트 이름을 제안했지만, 6.x 루트 표면은 잠겨 있어(1-in-1-out) 텍스트 축을 모아 둔 doc.text에 두었습니다.

이슈 제안 실제 이름
doc.iter_text_blocks() doc.text.blocks()
doc.outline(max_chars=...) doc.text.outline(max_chars=..., max_items=None, max_block_chars=200, after=None)
doc.apply_text_changes({...}) doc.text.apply_changes({...}, check_overflow=True)

blocks() (#666)

  • 본문 문단과 표 칸 문단(칸 안 표 포함)을 문서 순서대로 내놓습니다. block.id·block.location·block.paragraph·block.text·block.cell.
  • id 형식은 이슈 제안과 같습니다: s0p16, s0p16t0r3c0q0, 칸 안 표는 …q0t0r1c1q0처럼 이어집니다. 행·열은 칸 주소(hp:cellAddr)라 병합 칸은 대표 칸에서 한 번만 나옵니다(이슈의 id(cell.element) 중복 제거가 필요 없음).
  • 위치는 본문 17문단, 표2 4행 1열, 표2 1행 2열 안 표1 1행 1열처럼 씁니다. 최상위 표 번호는 문서 전체 순서입니다.
  • 칸 주소가 없거나 겹치는 깨진 표는 물리 위치로 id를 매겨 id가 겹치지 않게 했습니다.
  • 머리말·꼬리말·각주·메모·글상자는 포함하지 않습니다.

outline() (#668)

  • 글자가 있는 덩어리만 [{"id", "where", "text"}]로 돌려줍니다. 탭·줄 바꿈은 공백, 제어 문자는 지우고, 긴 덩어리는 앞부분만 남기고 …("truncated": True)로 끝냅니다.
  • max_chars는 각 항목의 id·위치·글자를 셉니다. 예산이 다하면 항목 단위로 멈추고 complete=False, next_after로 다음 쪽을 읽습니다. 예산보다 큰 항목도 한 쪽에 적어도 하나는 들어가 쪽 넘기기가 멈추지 않습니다.
  • 결과는 list라 그대로 json.dumps 됩니다. to_text()는 [id] 위치: 글 한 줄씩인 가장 짧은 프롬프트 형태입니다.

apply_changes() (#667)

  • 모든 id를 먼저 찾은 뒤 씁니다. 결과: changed·unchanged·unknown_ids·rewritten·overflow·warnings, ok, to_dict().
  • 한 런 안에서 끝나는 바꿈은 set_text_preserving_runs로 런과 컨트롤을 모두 지킵니다. 서식이 다른 런에 걸치거나 탭·줄 바꿈을 넣는 바꿈은 첫 런 서식으로 쓰고 rewritten에 알립니다(줄 바꿈은 hp:lineBreak로 씁니다).
  • 칸 넘침 경고: 바뀐 칸을 바꾸기 전후로 form-fit 측정기로 재어, 칸이 품는 줄보다(그리고 바꾸기 전보다) 많은 줄이 필요할 것으로 추정되면 overflow에 {id, where, lines, lineBudget, linesBefore, confidence}를 넣습니다. 렌더가 아니라 추정입니다.

검증

  • 새 테스트 tests/test_text_blocks.py 16개: 순서·위치·병합 칸·중첩 표·칸 주소 없는 표, 저장·재열기 뒤 id 동일, outline 정리·자르기·쪽 넘기기·to_text·오류, apply_changes 보고·런 보존·런 걸침·줄 바꿈·칸 넘침·빈 변경.
  • 저장소 픽스처 문서 1,210개(93,688덩어리)로 id 중복 없음과 저장·재열기 뒤 id·글 동일을 확인했습니다. 실패 2건은 일부러 깨 놓은 reader-robustness 픽스처를 저장 게이트가 거부한 것으로 이 변경과 무관합니다.
  • 전체 pytest: 7,660 통과(인벤토리 등록 누락 4건은 이 브랜치에서 고친 뒤 해당 묶음 재통과), ruff E9,F, 공개 위생, mypy, pyright 통과.
  • 지원 매트릭스 등급은 Parse·Edit입니다. 실제 한/글 렌더로 확인하지 않았으므로 Render-verified를 붙이지 않았습니다.

다른 PR과의 관계

🤖 Generated with Claude Code

…xt changes (#666, #667, #668)

doc.text.blocks() walks body paragraphs and table-cell paragraphs (nested
tables included) in document order with positional ids such as
s0p16t0r3c0q0 and a readable location (표1 4행 1열). A merged cell appears
once, at its anchor; a table missing cell addresses falls back to physical
positions so ids stay unique. Ids are identical after save and reopen.

doc.text.outline(max_chars=...) lists only non-empty blocks as
{id, where, text} within a character budget: tabs and line breaks become
spaces, control characters go, long blocks are cut with an ellipsis. A
page always holds one item and next_after resumes paging; to_text() is a
one-line-per-item prompt form.

doc.text.apply_changes({id: text}) resolves every id before writing,
keeps runs and controls when the edit stays inside one run, falls back to
the first run's formatting otherwise (reported as rewritten), and
estimates with the form-fit measurer which changed cells would need more
lines than the cell holds (overflow).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@airmang airmang left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

head 85ac152 / base d1c7d0a 검토 완료. 블록 ID·병합 칸/중첩 표 순회, outline 예산/커서, 일괄 편집의 런 보존·rewritten 보고와 넘침 추정 경로 및 공개 namespace/문서를 읽었습니다. 정확한 head의 전체 CI 7,665 passed/18 skipped/1 xfailed, Ruff/mypy/Pyright 통과 로그와 로컬 새 테스트 16개 통과를 확인했습니다. 최신 base와 합친 tree fd51d7db035e555a6e96d776c61379569c41d21d에서 별도 공개 API 검증도 수행했습니다: 부모 칸에 줄 바꿈을 넣는 편집과 본문 편집을 함께 적용해도 중첩 표의 바이트가 동일하고, 저장·재열기 뒤 id/텍스트와 작은 예산의 전체 페이지 순회가 일치하며 머리말은 포함하지 않습니다. 이 변경에서 미해결 결함은 발견하지 못했습니다. 자기 PR이므로 COMMENT로 기록하고 병합 직전 head/base/CI/리뷰를 다시 확인합니다.

@airmang
airmang merged commit 40a04fe into main Nov 6, 2026
13 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

1 participant