[Core] framework-independent TokenEstimator와 TokenCountResult API 추가 - #56
Conversation
📝 WalkthroughSummary by CodeRabbit
Walkthrough
ChangesToken 추정 Core 계약
Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
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. Comment |
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
build.gradle (1)
622-654: 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win게시된 core 런타임 의존성을 별도로 검증하십시오.
CoreConsumer는 core artifact만 직접 선언합니다. Gradle은 core POM의 전이 런타임 의존성을runtimeClasspath에 자동으로 포함합니다. 따라서 이 consumer가 컴파일되고 실행되어도 Spring AI, Spring Boot, Micrometer, Reactor가 core artifact를 통해 전이되지 않았다는 보장은 없습니다.생성된 consumer build에서
runtimeClasspath의 해석된 component를 검사하고, 금지된 group 또는 module이 있으면 실패시키십시오. 이 검사를run또는verifyCoreConsumer의 의존성으로 연결하십시오.As per coding guidelines, "Keep
token-pilot-coreindependently consumable without Spring AI, Spring Boot, Micrometer, or Reactor runtime dependencies, while preserving separate publication of existing modules."🤖 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 `@build.gradle` around lines 622 - 654, Add a resolved runtimeClasspath component check to the generated CoreConsumer build, rejecting any Spring AI, Spring Boot, Micrometer, or Reactor group/module and failing with a clear message. Wire this validation into run or verifyCoreConsumer so it executes alongside the existing CoreConsumer verification, while keeping token-pilot-core independently consumable and existing module publication unchanged.Source: Coding guidelines
🤖 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.
Outside diff comments:
In `@build.gradle`:
- Around line 622-654: Add a resolved runtimeClasspath component check to the
generated CoreConsumer build, rejecting any Spring AI, Spring Boot, Micrometer,
or Reactor group/module and failing with a clear message. Wire this validation
into run or verifyCoreConsumer so it executes alongside the existing
CoreConsumer verification, while keeping token-pilot-core independently
consumable and existing module publication unchanged.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 73934c61-4aca-4005-aee0-f7d9eb586b6c
📒 Files selected for processing (11)
build.gradletoken-pilot-core/src/main/java/io/tokenpilot/core/TokenEstimator.javatoken-pilot-core/src/main/java/io/tokenpilot/core/domain/TokenCountAccuracy.javatoken-pilot-core/src/main/java/io/tokenpilot/core/domain/TokenCountResult.javatoken-pilot-core/src/main/java/io/tokenpilot/core/domain/TokenCountScope.javatoken-pilot-core/src/main/java/io/tokenpilot/core/domain/TokenCountUnavailableReason.javatoken-pilot-core/src/main/java/io/tokenpilot/core/domain/TokenEstimatorDescriptor.javatoken-pilot-core/src/main/java/io/tokenpilot/core/domain/TokenizationBasis.javatoken-pilot-core/src/test/java/io/tokenpilot/core/domain/TokenCountResultTest.javatoken-pilot-core/src/test/java/io/tokenpilot/core/domain/TokenEstimatorDescriptorTest.javatoken-pilot-core/src/test/java/io/tokenpilot/core/domain/TokenizationBasisTest.java
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
build.gradle (1)
686-690: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win빈 문자열 입력을 실제로 검증하도록 수정해 주세요.
현재
TokenEstimator estimator = text -> expected;는text를 무시합니다. 따라서estimate("")를 다른 문자열로 변경해도 검사가 통과합니다.text.isEmpty()를 확인하는 테스트용 estimator를 사용해 빈 문자열 계약을 검증해 주세요.수정 예시
- TokenEstimator estimator = text -> expected; + TokenEstimator estimator = text -> { + if (!text.isEmpty()) { + throw new IllegalStateException( + "Token Pilot estimator consumer expected empty input" + ); + } + return expected; + };🤖 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 `@build.gradle` around lines 686 - 690, Update the test estimator around TokenEstimator and its estimate call so it validates the input is empty instead of always returning expected regardless of text. Make the estimator return the expected result only for an empty string, while preserving the existing TokenCountResult assertions that verify the empty-input contract.
🤖 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.
Outside diff comments:
In `@build.gradle`:
- Around line 686-690: Update the test estimator around TokenEstimator and its
estimate call so it validates the input is empty instead of always returning
expected regardless of text. Make the estimator return the expected result only
for an empty string, while preserving the existing TokenCountResult assertions
that verify the empty-input contract.
제안된 lambda 검사는 빈 문자열 계약을 실제로 검증하는 역할이 아니며 #31 에서 검증하는 것이 맞다고 판단하여 반영하지 않았습니다. |
|
검토 결과 실제 TokenEstimator/TokenCountResult 계약과 불변식은 #30 요구사항을 충족하고, 빌드 체크도 통과했습니다. 따라서 기능상 병합 가능한 것으로 판단합니다. 다만 생성된 core consumer의 테스트 estimator가 전달받은 문자열을 무시하고 항상 같은 결과를 반환하므로, 빈 문자열 계약을 직접 검증하지는 못합니다. 병합 후 text.isEmpty()를 확인하는 테스트로 보강해 주세요. AGENTS.md 갱신은 문서 후속 커밋으로 처리하면 됩니다. |
변경 사항
호출 전 token 계산 결과를 Spring AI와 분리된 Core 계약으로 정의했습니다.
TokenEstimator를 framework-independent public interface로 추가했습니다.TokenCountResult가 다음 상태를 상호 배타적으로 표현하도록 구현했습니다.Counted(EXACT)Counted(HEURISTIC)Unavailable(reason)TokenCountScope로TEXT_ONLY와REQUEST를 구분했습니다.TokenCountAccuracy로EXACT와HEURISTIC을 구분했습니다.TokenEstimatorDescriptor에 estimator ID와 version을 보존합니다.TokenizationBasis로 tokenizer compatibility 기준을 표현합니다.TokenCountUnavailableReason을 추가했습니다.배경
호출 후 provider 사용량인
TokenUsage와 호출 전 token 계산 결과는 의미와 생명주기가 다릅니다.단순 token 숫자만 반환하면 다음 내용을 확인할 수 없습니다.
특히
TEXT_ONLY결과가 전체 요청의 admission 근거로 사용되면 실제 request token을 과소평가할 수 있습니다.이번 변경은 token 숫자와 계산 기준을 하나의 immutable result에 보존하고, 모순된 상태가 생성되지 않도록 Core 경계를 정의합니다.
결과 상태별 동작
EXACT
accuracy == EXACT에서 파생합니다.HEURISTIC
UNAVAILABLE
0또는 음수를 unavailable sentinel로 사용하지 않습니다.Scope
TEXT_ONLY
전달된 문자열 내용만 계산한 결과입니다.
다음 요소를 포함했다고 간주하지 않습니다.
따라서
TEXT_ONLY결과만으로 전체 요청이 context window에 들어간다고 판단할 수 없습니다.REQUEST
실제 전송 요청의 tokenizable content와 필요한 framing/headroom을 계산한 결과입니다.
기존
TEXT_ONLY결과의 scope만REQUEST로 변경하는 API는 제공하지 않습니다. 다른 계층이 누락된 구성요소를 계산했다면 새로운REQUEST결과를 생성해야 합니다.범위 메모
이번 PR은 token 계산 결과의 계약과 불변식만 제공합니다.
TokenEstimator구현체가 아직 없으므로 null 문자열과 빈 문자열의 실제 처리 행위는 #31의 producer 테스트에서 고정합니다.TEXT_ONLY지정REQUESTscope의 정당성은 실제 Spring AI 요청 구성요소와 framing을 계산하는 #39에서 검증합니다.이후 과제
HeuristicTokenEstimator구현TokenBudget.check()체크리스트
필수 테스트
Acceptance Criteria
TokenEstimator계약을 제공한다.TokenUsage를 preflight 결과로 재사용하지 않는다.검증
token-pilot-core Java 25 consumer OKCloses #30