[Fix] SOLUTION 기능 지원 누락 수정 및 AI 라우터와 전처리 코드 수정#203
Conversation
📝 WalkthroughWalkthroughChatServiceImpl의 SUPPORTED_FEATURES 컬렉션에 "Solution", "SOLUTION", "VARIATION", "GRAPH", "CreateGraph"를 추가하여 기능 검증 범위를 확대했습니다. DB 툴코드(대문자)와 AI 기능명(PascalCase) 모두 지원하도록 개선했습니다. Changes
Estimated code review effort🎯 1 (Trivial) | ⏱️ ~3 minutes Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 3❌ Failed checks (1 warning, 2 inconclusive)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches
🧪 Generate unit tests (beta)
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.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/main/java/com/proovy/domain/conversation/service/ChatServiceImpl.java (1)
288-303:⚠️ Potential issue | 🟠 Major대문자 기능명(
"SOLUTION"등)으로 크레딧이 잘못된 금액 차감됨
validateFeatures를 통과한 대문자 feature명("SOLUTION"등)이CreditUseService.useCredit()의featureName으로 전달됩니다.FEATURE_BASE_COST맵에는 PascalCase 키만 있으므로("Solution"등), 정확한 문자열 매칭에 실패합니다. 예외를 발생시키지 않고 기본 비용 5를 반환하여 예상 비용보다 훨씬 적은 크레딧이 차감됩니다("SOLUTION"의 경우 20이 아닌 5로 차감). 줄 297에서 응답을 검증하지 않아 이 오류가 감지되지 않습니다.
validateFeatures에서 feature명을 정규화(대문자 → PascalCase)하거나,CreditUseService에서 대소문자 무시 매칭을 지원하도록 보완이 필요합니다.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/main/java/com/proovy/domain/conversation/service/ChatServiceImpl.java` around lines 288 - 303, The chosen-features loop passes raw feature strings (e.g., "SOLUTION") to CreditUseService.useCredit causing FEATURE_BASE_COST (which uses PascalCase keys like "Solution") to miss and fall back to default cost; normalize feature names to PascalCase before creating CreditUseRequest (or alternatively make CreditUseService do case-insensitive lookup against FEATURE_BASE_COST) so the featureName sent to useCredit matches the map keys—update the code path that builds CreditUseRequest in ChatServiceImpl (or the validateFeatures normalization step) to convert incoming feature tokens to PascalCase prior to calling creditUseService.useCredit.
🧹 Nitpick comments (1)
src/main/java/com/proovy/domain/conversation/service/ChatServiceImpl.java (1)
532-539:validateFeatures의 대소문자 구분 허용 방식 — 유지보수 취약성새 기능이 추가될 때마다 AI 기능명(PascalCase)과 DB toolCode(대문자) 두 가지 변형을
SUPPORTED_FEATURES에 수동으로 등록해야 하는 구조는 누락 위험이 높습니다 (이번"VARIANT"누락이 그 예입니다).TreeSet(String.CASE_INSENSITIVE_ORDER)를 사용하면 각 기능명을 한 번만 등록하고 대소문자를 무시한 룩업이 가능합니다.♻️ 리팩터링 제안
- // 지원되는 기능 목록 - DB toolCode(대문자)와 AI feature명(PascalCase) 모두 허용 - private static final Set<String> SUPPORTED_FEATURES = Set.of( - "Solve", "Check", "Explain", "Variant", "Practice", - "Solution", "SOLUTION", - "VARIATION", - "GRAPH", "CreateGraph" - ); + // 지원되는 기능 목록 (대소문자 무시 매칭) + private static final Set<String> SUPPORTED_FEATURES; + static { + Set<String> s = new TreeSet<>(String.CASE_INSENSITIVE_ORDER); + s.addAll(List.of("Solve", "Check", "Explain", "Variant", "Practice", + "Solution", "Graph", "CreateGraph")); + SUPPORTED_FEATURES = Collections.unmodifiableSet(s); + }
validateFeatures는 변경 없이SUPPORTED_FEATURES.contains(feature)를 그대로 사용할 수 있으며,TreeSet(String.CASE_INSENSITIVE_ORDER)덕분에"SOLUTION","solution","Solution"모두 허용됩니다.주의: 이 접근 방식을 채택하면 현재 AI 라우터에서 수행하는 명시적 대소문자 정규화에 의존하는 다운스트림 코드가 여전히 올바르게 작동하는지 확인해야 합니다.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/main/java/com/proovy/domain/conversation/service/ChatServiceImpl.java` around lines 532 - 539, The current validateFeatures method relies on SUPPORTED_FEATURES.contains(feature) but SUPPORTED_FEATURES is case-sensitive, forcing duplicate entries for different capitalizations; change the SUPPORTED_FEATURES collection initialization to a case-insensitive set (e.g., new TreeSet<>(String.CASE_INSENSITIVE_ORDER) or equivalent) so validateFeatures can remain unchanged and lookups accept "SOLUTION"/"solution"/"Solution"; after changing SUPPORTED_FEATURES, run a quick check of downstream code that assumes explicit normalization (AI router or any code that expected specific casing) to ensure behavior remains correct.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/main/java/com/proovy/domain/conversation/service/ChatServiceImpl.java`:
- Around line 70-76: SUPPORTED_FEATURES contains uppercase variants ("SOLUTION",
"VARIATION", "GRAPH") and "Practice", but FEATURE_BASE_COST lookups are
case-sensitive so CreditUseService.useCredit() can charge the default 5 instead
of the intended value; fix by normalizing feature names before cost lookup
(e.g., map incoming feature via a canonicalizer like feature =
normalizeFeature(feature) or feature =
StringUtils.capitalize(feature.toLowerCase())/toLowerCase()) or make
FEATURE_BASE_COST case-insensitive by populating both variants (add entries for
"SOLUTION", "VARIATION", "GRAPH" and "Practice") and ensure ToolDataInitializer
and any registration logic use the same canonical form so
CreditUseService.useCredit() and FEATURE_BASE_COST use the same symbol set.
---
Outside diff comments:
In `@src/main/java/com/proovy/domain/conversation/service/ChatServiceImpl.java`:
- Around line 288-303: The chosen-features loop passes raw feature strings
(e.g., "SOLUTION") to CreditUseService.useCredit causing FEATURE_BASE_COST
(which uses PascalCase keys like "Solution") to miss and fall back to default
cost; normalize feature names to PascalCase before creating CreditUseRequest (or
alternatively make CreditUseService do case-insensitive lookup against
FEATURE_BASE_COST) so the featureName sent to useCredit matches the map
keys—update the code path that builds CreditUseRequest in ChatServiceImpl (or
the validateFeatures normalization step) to convert incoming feature tokens to
PascalCase prior to calling creditUseService.useCredit.
---
Nitpick comments:
In `@src/main/java/com/proovy/domain/conversation/service/ChatServiceImpl.java`:
- Around line 532-539: The current validateFeatures method relies on
SUPPORTED_FEATURES.contains(feature) but SUPPORTED_FEATURES is case-sensitive,
forcing duplicate entries for different capitalizations; change the
SUPPORTED_FEATURES collection initialization to a case-insensitive set (e.g.,
new TreeSet<>(String.CASE_INSENSITIVE_ORDER) or equivalent) so validateFeatures
can remain unchanged and lookups accept "SOLUTION"/"solution"/"Solution"; after
changing SUPPORTED_FEATURES, run a quick check of downstream code that assumes
explicit normalization (AI router or any code that expected specific casing) to
ensure behavior remains correct.
| // 지원되는 기능 목록 - DB toolCode(대문자)와 AI feature명(PascalCase) 모두 허용 | ||
| private static final Set<String> SUPPORTED_FEATURES = Set.of( | ||
| "Solve", "Check", "Explain", "Variant", "Practice" | ||
| "Solve", "Check", "Explain", "Variant", "Practice", | ||
| "Solution", "SOLUTION", | ||
| "VARIATION", | ||
| "GRAPH", "CreateGraph" | ||
| ); |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# Search for VARIATION, VARIANT patterns and related feature code
rg -n --type=java 'VARIATION|VARIANT' -B2 -A2Repository: Team-Proovy/Proovy-server
Length of output: 1794
🏁 Script executed:
# Look at the ChatServiceImpl file around the SUPPORTED_FEATURES
sed -n '65,85p' src/main/java/com/proovy/domain/conversation/service/ChatServiceImpl.javaRepository: Team-Proovy/Proovy-server
Length of output: 915
🏁 Script executed:
# Check how validateFeatures and credit deduction use SUPPORTED_FEATURES
rg -n --type=java 'validateFeatures|SUPPORTED_FEATURES' src/main/java/com/proovy/domain/conversation/service/ChatServiceImpl.java -B1 -A3Repository: Team-Proovy/Proovy-server
Length of output: 876
🏁 Script executed:
# Search for toolCode references in entity definitions or migrations
find . -type f \( -name "*.java" -o -name "*.sql" \) | xargs rg -l 'toolCode|TOOL_CODE' 2>/dev/null | head -20Repository: Team-Proovy/Proovy-server
Length of output: 901
🏁 Script executed:
# Check Tool entity definition
cat src/main/java/com/proovy/global/tool/entity/Tool.javaRepository: Team-Proovy/Proovy-server
Length of output: 1775
🏁 Script executed:
# Check the full ToolDataInitializer to see all tool initializations
cat src/main/java/com/proovy/global/tool/config/ToolDataInitializer.javaRepository: Team-Proovy/Proovy-server
Length of output: 2117
🏁 Script executed:
# Search for all toolCode initializations to see the complete list
rg -n 'toolCode\(' src/main/java/com/proovy/global/tool/config/ToolDataInitializer.java -B1 -A1Repository: Team-Proovy/Proovy-server
Length of output: 457
🏁 Script executed:
# Check if there are migrations that define toolCode values
find . -type f -name "*.sql" | xargs rg 'SOLVE|CHECK|EXPLAIN|VARIANT|PRACTICE|SOLUTION|GRAPH' 2>/dev/null | head -30Repository: Team-Proovy/Proovy-server
Length of output: 51
🏁 Script executed:
# Check how features are used in credit deduction and feature validation
sed -n '280,310p' src/main/java/com/proovy/domain/conversation/service/ChatServiceImpl.javaRepository: Team-Proovy/Proovy-server
Length of output: 1836
🏁 Script executed:
# Search for where feature names map to tools/toolCodes
rg -n 'featureName|chosenFeatures' src/main/java/com/proovy/domain/conversation/service/ChatServiceImpl.java -B2 -A2Repository: Team-Proovy/Proovy-server
Length of output: 673
🏁 Script executed:
# Check if there's any normalization or conversion between feature names and toolCodes
rg -n 'toLowerCase|toUpperCase|normalize' src/main/java/com/proovy/domain/conversation/service/ChatServiceImpl.javaRepository: Team-Proovy/Proovy-server
Length of output: 51
🏁 Script executed:
# Look for the AI router or feature name conversion logic
rg -n 'Solve|Check|Explain|Variant|Practice' src/main/java/com/proovy/domain/conversation/service/ChatServiceImpl.javaRepository: Team-Proovy/Proovy-server
Length of output: 135
🏁 Script executed:
# Search for AI feature mapping or conversion logic
rg -n '"Solve"|"Check"|"Explain"|"Variant"|"Practice"' --type=java -B2 -A2Repository: Team-Proovy/Proovy-server
Length of output: 4339
🏁 Script executed:
# Check if there are any tests or documentation about supported features
find . -type f \( -name "*Test.java" -o -name "*.md" \) | xargs rg -l 'SUPPORTED_FEATURES|feature' 2>/dev/null | head -10Repository: Team-Proovy/Proovy-server
Length of output: 51
🏁 Script executed:
# Look at the complete MessageTool entity to understand tool usage
cat src/main/java/com/proovy/domain/conversation/entity/MessageTool.javaRepository: Team-Proovy/Proovy-server
Length of output: 832
🏁 Script executed:
# Check if there are any other places where features are validated or mapped
rg -n 'validateFeatures|chosenFeatures' --type=java -B3 -A3Repository: Team-Proovy/Proovy-server
Length of output: 4366
🏁 Script executed:
# Check CreditUseService to see how it looks up feature cost
sed -n '25,50p' src/main/java/com/proovy/domain/credit/service/CreditUseService.javaRepository: Team-Proovy/Proovy-server
Length of output: 770
🏁 Script executed:
# Search for where the feature name gets passed to credit service and how it's handled
rg -n 'getFeatureCost|FEATURE_BASE_COST' src/main/java/com/proovy/domain/credit/service/CreditUseService.java -B2 -A5Repository: Team-Proovy/Proovy-server
Length of output: 1241
🏁 Script executed:
# Check if there's any normalization when passing feature to credit service
sed -n '285,310p' src/main/java/com/proovy/domain/conversation/service/ChatServiceImpl.javaRepository: Team-Proovy/Proovy-server
Length of output: 1508
🏁 Script executed:
# Look at how "Practice" feature is handled (it's in SUPPORTED_FEATURES but not in some mappings)
rg -n 'Practice' --type=javaRepository: Team-Proovy/Proovy-server
Length of output: 209
대문자 기능명의 크레딧 차감 비용 매핑 누락
현재 코드는 "SOLUTION", "VARIATION", "GRAPH" 같은 대문자 기능명을 SUPPORTED_FEATURES에 추가했으나, 이들 기능이 실제로 크레딧 차감 시 비용 매핑에서 빠져 있습니다.
문제 상황:
-
대문자 기능명 비용 매핑 누락
- 클라이언트가
"SOLUTION"전송 → 검증 통과 ✓ - 크레딧 차감 시
CreditUseService.useCredit()호출 (line 293) FEATURE_BASE_COST맵에는"Solution"만 있고"SOLUTION"은 없음getOrDefault()호출로 기본값 5 크레딧으로 처리 (line 161, silent fallback)- 실제 비용(
"Solution": 20 크레딧)과 다른 금액 차감
- 클라이언트가
-
"Practice" 기능의 비용 매핑 누락
SUPPORTED_FEATURES에"Practice"가 있음 (line 72)FEATURE_BASE_COST에는 없음 (line 28-34)"Practice"기능 사용 시 기본값 5 크레딧으로 차감
-
DB 도구 불일치
ToolDataInitializer는 3개 도구만 생성: GRAPH, SOLUTION, VARIATION- "Solve", "Check", "Explain", "Variant", "Practice"는 DB 도구 등록 없음
수정 방법:
대문자 기능명이 Spring 검증 단계에서 도착하면, 크레딧 차감 전에 정규화된 형태로 변환하거나, FEATURE_BASE_COST에 모든 기능명 변형(대문자 포함)을 추가하세요.
private static final Map<String, Integer> FEATURE_BASE_COST = Map.of(
"Solve", 10,
"Explain", 5,
"CreateGraph", 5,
"Variant", 5,
"Solution", 20,
"Check", 3,
+ "SOLUTION", 20,
+ "VARIATION", 5,
+ "GRAPH", 5,
+ "Practice", 5 // 누락된 기능
);또는 검증 후 정규화: feature.toLowerCase() 또는 case-insensitive 비용 조회 로직 추가.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/main/java/com/proovy/domain/conversation/service/ChatServiceImpl.java`
around lines 70 - 76, SUPPORTED_FEATURES contains uppercase variants
("SOLUTION", "VARIATION", "GRAPH") and "Practice", but FEATURE_BASE_COST lookups
are case-sensitive so CreditUseService.useCredit() can charge the default 5
instead of the intended value; fix by normalizing feature names before cost
lookup (e.g., map incoming feature via a canonicalizer like feature =
normalizeFeature(feature) or feature =
StringUtils.capitalize(feature.toLowerCase())/toLowerCase()) or make
FEATURE_BASE_COST case-insensitive by populating both variants (add entries for
"SOLUTION", "VARIATION", "GRAPH" and "Practice") and ensure ToolDataInitializer
and any registration logic use the same canonical form so
CreditUseService.useCredit() and FEATURE_BASE_COST use the same symbol set.
📌 관련 이슈
🏷️ PR 타입
📝 작업 내용
SUPPORTED_FEATURES에 추가하여 400 에러 수정📸 스크린샷
✅ 체크리스트
📎 기타 참고사항
Summary by CodeRabbit
릴리스 노트