Skip to content

[Core] UTF-8 byte 기반 HeuristicTokenEstimator 구현 - #58

Closed
Rigu1 wants to merge 8 commits into
tokenpliot:mainfrom
Rigu1:feat/HeuristicTokenEstimator-based-on-UTF-8
Closed

[Core] UTF-8 byte 기반 HeuristicTokenEstimator 구현#58
Rigu1 wants to merge 8 commits into
tokenpliot:mainfrom
Rigu1:feat/HeuristicTokenEstimator-based-on-UTF-8

Conversation

@Rigu1

@Rigu1 Rigu1 commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

[Core] UTF-8 byte 기반 HeuristicTokenEstimator 구현

변경 사항

정확한 tokenizer asset 없이 문자열의 token 수를 추정하는 HeuristicTokenEstimator를 Core에 추가했습니다.

  • TokenEstimator를 구현하는 UTF-8 byte 기반 estimator를 추가했습니다.
  • 평균 추정값을 ceil(utf8Bytes / 4)로 계산합니다.
  • safeUpperBoundTokens를 원문의 UTF-8 byte 길이로 제공합니다.
  • 모든 정상 결과를 HEURISTIC, exact=false, TEXT_ONLY로 생성합니다.
  • estimator ID를 tokenpilot-utf8-byte-heuristic으로 고정했습니다.
  • estimator version을 1로 지정했습니다.
  • tokenization basis를 BYTE_LEVEL_BPE_UTF8로 보존합니다.
  • 구현체는 package-private으로 유지하고 LedgerComponents를 통해 생성합니다.
  • 계산식과 범위 제한을 행위 중심 문서로 정리했습니다.

배경

정확한 tokenizer와 encoding asset이 없는 환경에서도 호출 전에 대략적인 token 수와 보수적인 text-only 상한이 필요합니다.

단순 문자열 길이는 한글이나 surrogate pair 같은 다국어 입력의 실제 UTF-8 크기를 반영하지 못합니다. 또한 JVM의 기본 UTF-8 변환은 잘못된 UTF-16 입력을 replacement character로 대체할 수 있어, 원문과 다른 값을 조용히 계산할 수 있습니다.

이번 변경은 순수 Java API만 사용해 원문의 UTF-8 byte 길이를 계산하고, #30에서 정의한 TokenCountResult로 계산 기준과 범위를 함께 반환합니다.

계산 방식

  • utf8Bytes: 원문을 정규화하지 않은 UTF-8 byte 수
  • tokens: Math.ceilDiv(utf8Bytes, 4)
  • safeUpperBoundTokens: utf8Bytes
  • accuracy: HEURISTIC
  • scope: TEXT_ONLY

Math.ceilDiv를 사용해 (utf8Bytes + 3) / 4의 덧셈 overflow 가능성을 피했습니다.

입력 정책

  • null 입력은 NullPointerException으로 거부합니다.
  • 빈 문자열은 estimate와 upper bound가 0인 정상 counted 결과입니다.
  • CharsetEncoderCodingErrorAction.REPORT를 사용해 unpaired surrogate를 명시적으로 거부합니다.
  • 올바른 surrogate pair는 정상적으로 UTF-8 byte 길이를 계산합니다.
  • precomposed/combining 문자열을 NFC 또는 NFD로 자동 정규화하지 않습니다.

Scope

결과 scope는 항상 TEXT_ONLY입니다.

다음 request 구성요소는 계산하지 않습니다.

  • system/developer/user role framing
  • message 구분 token
  • tool definition과 tool call
  • image/audio metadata
  • structured-output schema
  • provider/model별 request framing

따라서 계산 결과가 context window보다 작더라도 전체 요청의 fits=true 근거로 사용할 수 없습니다. REQUEST 결과를 생성하거나 기존 결과의 scope를 승격하는 경로도 제공하지 않습니다.

범위 메모

이번 PR은 문자열 하나에 대한 UTF-8 휴리스틱 계산만 구현합니다.

  • exact BPE와 encoding asset은 추가하지 않습니다.
  • request framing과 multimodal/tool token은 계산하지 않습니다.
  • allocation 최적화와 JMH benchmark는 post-MVP 범위로 남깁니다.
  • #33에서 TEXT_ONLY 결과를 context admission 상태와 연결합니다.
  • #34에서 Unicode corpus와 normalization 차이 검증을 확장합니다.

체크리스트

필수 테스트

  • 빈 문자열, ASCII, 한글과 혼합 문자열을 계산한다.
  • 모든 정상 결과가 HEURISTIC, exact=false, TEXT_ONLY다.
  • estimator ID/version/tokenization basis가 고정된다.
  • safeUpperBoundTokens가 UTF-8 byte 길이와 같다.
  • estimate가 safe upper bound를 넘지 않는다.
  • null과 unpaired surrogate를 거부한다.
  • 올바른 surrogate pair를 허용한다.
  • precomposed/combining 문자열을 자동 정규화하지 않는다.
  • Core-only consumer가 실제 estimator를 생성하고 실행한다.

Acceptance Criteria

  • UTF-8 byte 기반 estimator가 구현된다.
  • 평균 estimate와 text-only safe upper bound가 분리된다.
  • 결과가 항상 HEURISTIC/TEXT_ONLY임이 테스트와 Javadoc에 고정된다.
  • estimator ID/version/tokenization basis가 결과에 포함된다.
  • empty/invalid UTF-16 정책이 테스트된다.
  • 전체 요청의 정확 계산이나 허용 근거가 아님이 문서화된다.
  • Core에 새로운 runtime dependency가 추가되지 않는다.

Closes #31

@coderabbitai

coderabbitai Bot commented Aug 8, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 8eeb52f8-dd4f-4e50-b920-207d4022f004

📥 Commits

Reviewing files that changed from the base of the PR and between 100f5ab and c96e3ad.

📒 Files selected for processing (14)
  • build.gradle
  • token-pilot-core/src/main/java/io/tokenpilot/core/TokenEstimator.java
  • token-pilot-core/src/main/java/io/tokenpilot/core/domain/TokenCountAccuracy.java
  • token-pilot-core/src/main/java/io/tokenpilot/core/domain/TokenCountResult.java
  • token-pilot-core/src/main/java/io/tokenpilot/core/domain/TokenCountScope.java
  • token-pilot-core/src/main/java/io/tokenpilot/core/domain/TokenCountUnavailableReason.java
  • token-pilot-core/src/main/java/io/tokenpilot/core/domain/TokenEstimatorDescriptor.java
  • token-pilot-core/src/main/java/io/tokenpilot/core/domain/TokenizationBasis.java
  • token-pilot-core/src/main/java/io/tokenpilot/core/internal/HeuristicTokenEstimator.java
  • token-pilot-core/src/main/java/io/tokenpilot/core/internal/LedgerComponents.java
  • token-pilot-core/src/test/java/io/tokenpilot/core/domain/TokenCountResultTest.java
  • token-pilot-core/src/test/java/io/tokenpilot/core/domain/TokenEstimatorDescriptorTest.java
  • token-pilot-core/src/test/java/io/tokenpilot/core/domain/TokenizationBasisTest.java
  • token-pilot-core/src/test/java/io/tokenpilot/core/internal/HeuristicTokenEstimatorTest.java

📝 Walkthrough

Summary by CodeRabbit

  • 새 기능

    • 텍스트의 토큰 수를 추정하고 결과 상태, 정확도, 안전 상한을 확인할 수 있습니다.
    • UTF-8 바이트 기반 휴리스틱 추정을 지원하며, ASCII·한글·혼합 문자열 등 다양한 입력을 처리합니다.
    • 텍스트 전용 범위와 요청 범위를 구분하고, 추정기 및 토큰화 기준 정보를 제공합니다.
    • 계산 불가 상태를 명확히 표현하며, 잘못된 입력과 유효하지 않은 결과 조건을 검증합니다.
  • 테스트

    • 토큰 계산 결과, 메타데이터, 예외 처리 및 다양한 유니코드 입력에 대한 검증을 강화했습니다.

Walkthrough

공개 토큰 추정 계약과 결과 모델을 추가했습니다. UTF-8 바이트 기반 휴리스틱 추정기와 factory를 구현했습니다. 생성된 consumer는 추정 결과와 런타임 classpath를 검증합니다.

Changes

토큰 추정 기능

Layer / File(s) Summary
토큰 추정 결과 계약
token-pilot-core/src/main/java/io/tokenpilot/core/..., token-pilot-core/src/test/java/io/tokenpilot/core/domain/*
TokenEstimator 계약과 TokenCountResult 상태 모델을 추가했습니다. 정확도, 범위, unavailable 사유, estimator 및 tokenization 메타데이터를 정의했습니다. 생성자 검증과 상태 접근자를 테스트했습니다.
UTF-8 휴리스틱 추정기
token-pilot-core/src/main/java/io/tokenpilot/core/internal/*, token-pilot-core/src/test/java/io/tokenpilot/core/internal/*
UTF-8 byte length를 기준으로 ceil(bytes / 4) 토큰 수와 byte length 안전 상한을 계산합니다. 결과는 HEURISTICTEXT_ONLY로 생성됩니다. malformed UTF-16을 거부하고 factory와 Unicode 사례를 테스트했습니다.
생성 consumer 검증
build.gradle
생성된 consumer가 "hello"의 추정 결과 상태, 토큰 수, 안전 상한 및 범위를 확인합니다. run 실행 전에 Spring, Spring AI, Micrometer, Reactor 런타임 의존성을 검사합니다.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 2.38% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed 제목은 UTF-8 byte 기반 HeuristicTokenEstimator 구현이라는 PR의 핵심 변경을 정확하고 간결하게 설명합니다.
Description check ✅ Passed 설명은 estimator의 계산식, 입력 정책, 범위 제한, 메타데이터, 테스트와 의존성 요구사항을 변경 내용과 일치하게 설명합니다.
Linked Issues check ✅ Passed 구현은 #31의 UTF-8 계산, HEURISTIC/TEXT_ONLY 계약, 입력 검증, 메타데이터, overflow 방지와 runtime dependency 제한을 충족합니다.
Out of Scope Changes check ✅ Passed 변경된 API, 구현, 테스트와 classpath 검증 태스크는 모두 #31의 estimator 구현과 Core 의존성 요구사항에 직접 관련됩니다.
✨ Finishing Touches 💡 1
⚔️ Resolve merge conflicts 💡
  • Resolve merge conflict in branch feat/HeuristicTokenEstimator-based-on-UTF-8

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.

@HuitaePark

Copy link
Copy Markdown
Member

검토 결과 UTF-8 byte 계산식과 HEURISTIC/TEXT_ONLY 결과 계약은 #31 요구사항을 충족합니다. 다만 현재 구현에는 병합 전에 해결할 두 가지가 있습니다.

  1. TOKENIZATION_BASIS_ID가 BYTE_LEVEL_BPE_UTF8로 선언되어 있지만 구현은 BPE가 아니라 UTF-8 byte 길이 휴리스틱입니다. 이 값은 실제 계산 기준을 설명하도록 바꿔 주세요.
  2. 새로운 HeuristicTokenEstimator 기능에 맞춰 AGENTS.md의 구현 현황과 변경 이력을 갱신해 주세요.

첫 번째는 코드 식별자 오류이고, 두 번째는 저장소 규칙 누락이므로 현재는 병합하지 않겠습니다.

private static final int BYTES_PER_ESTIMATED_TOKEN = 4;
private static final String ESTIMATOR_ID = "tokenpilot-utf8-byte-heuristic";
private static final String ESTIMATOR_VERSION = "1";
private static final String TOKENIZATION_BASIS_ID = "BYTE_LEVEL_BPE_UTF8";

@HuitaePark HuitaePark Aug 9, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

[P2] 추가 확인 결과 BYTE_LEVEL_BPE_UTF8은 이슈에서 허용한 호환성 기준으로 사용할 수 있습니다. 다만 현재 클래스는 실제 BPE merge를 수행하지 않고 UTF-8 byte 길이로 추정하므로, 이 이름만 보면 exact BPE 구현으로 오해할 수 있습니다. Javadoc이나 AGENTS.md에 이 값이 byte-level 안전성 가정이며 exact BPE 구현은 아니라는 점을 명시해 주세요. 계산 로직 자체의 병합 blocker는 아니고 문서 명확화 요청입니다.

private static final int BYTES_PER_ESTIMATED_TOKEN = 4;
private static final String ESTIMATOR_ID = "tokenpilot-utf8-byte-heuristic";
private static final String ESTIMATOR_VERSION = "1";
private static final String TOKENIZATION_BASIS_ID = "BYTE_LEVEL_BPE_UTF8";

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

추가 확인 결과 UTF-8 byte 기반 계산식과 HEURISTIC/TEXT_ONLY 결과는 #31 요구사항을 충족합니다. BYTE_LEVEL_BPE_UTF8은 이슈에서 허용한 호환성 기준으로 볼 수 있지만, 현재 구현이 실제 BPE merge를 수행하는 것은 아니므로 Javadoc이나 AGENTS.md에 ‘byte-level 안전성 가정이며 exact BPE 구현은 아님’을 명시해 주세요. 새 estimator 기능에 대한 AGENTS.md 갱신도 필요합니다. 두 항목은 문서 보완으로 처리할 수 있으므로 기능상 병합 blocker로 보지는 않겠습니다.

@HuitaePark

Copy link
Copy Markdown
Member

재검토 결과 #58의 UTF-8 byte 기반 estimator 구현과 테스트는 #31 요구사항을 충족합니다. BYTE_LEVEL_BPE_UTF8은 이슈에 정의된 호환성 기준으로 사용할 수 있으나 실제 BPE 구현으로 오해하지 않도록 Javadoc에 안전성 가정을 명시해 주세요. 새 기능에 맞춘 AGENTS.md 갱신은 문서 후속 작업으로 남겼습니다. 기능상 병합 blocker는 없다고 판단합니다.

@HuitaePark
HuitaePark marked this pull request as ready for review August 9, 2026 23:59
@HuitaePark

Copy link
Copy Markdown
Member

PR #56이 main에 squash 병합된 뒤 이 PR의 head가 이전 main을 기준으로 남아 있어 현재 GitHub에서 mergeable_state=dirty가 되었습니다. 이 PR은 #56의 선행 API를 포함하고 있으므로, 원래 head 저장소(Rigu1/token-ledger)의 브랜치를 최신 tokenpliot/tokenpilot/main 위로 rebase한 뒤 다시 푸시해야 병합할 수 있습니다. 현재는 충돌 해결 전이라 병합하지 않았습니다. AGENTS.md와 basis 설명 보완도 함께 반영해 주세요.

@HuitaePark
HuitaePark marked this pull request as draft August 9, 2026 23:59
@HuitaePark

Copy link
Copy Markdown
Member

UTF-8 estimator 수정 사항을 통합 PR #59에 반영해 main에 병합했습니다. 이 PR은 중복이므로 닫습니다.

@HuitaePark HuitaePark closed this Aug 10, 2026
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.

[Core] UTF-8 byte 기반 HeuristicTokenEstimator 구현

2 participants