Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.example.todayEng.domain.diary.client;

import java.util.List;
import java.util.regex.Pattern;
import org.springframework.stereotype.Component;

@Component
public class AnswerCorrectionLanguageValidator {

private static final Pattern HANGUL = Pattern.compile("[가-힣ㄱ-ㅎㅏ-ㅣ]");
private static final Pattern LATIN = Pattern.compile("[A-Za-z]");

public boolean isKoreanExplanation(String value) {
return value != null && HANGUL.matcher(value).find();
}

public boolean isEnglishText(String value) {
return value != null
&& LATIN.matcher(value).find()
&& !HANGUL.matcher(value).find();
}

public boolean areEnglishExpressions(List<String> values) {
return values != null && values.stream().allMatch(this::isEnglishText);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,16 @@ public class GeminiAnswerCorrectionLlmClient implements AnswerCorrectionLlmClien
private final GeminiProperties properties;
private final AnswerCorrectionPromptFactory promptFactory;
private final ObjectMapper objectMapper;
private final AnswerCorrectionLanguageValidator languageValidator;

public GeminiAnswerCorrectionLlmClient(@Qualifier("geminiRestClient") RestClient restClient,
GeminiProperties properties, AnswerCorrectionPromptFactory promptFactory, ObjectMapper objectMapper) {
GeminiProperties properties, AnswerCorrectionPromptFactory promptFactory, ObjectMapper objectMapper,
AnswerCorrectionLanguageValidator languageValidator) {
this.restClient = restClient;
this.properties = properties;
this.promptFactory = promptFactory;
this.objectMapper = objectMapper;
this.languageValidator = languageValidator;
}

@Override
Expand Down Expand Up @@ -111,7 +114,10 @@ private String extract(GeminiResponse response) {
}

private void validate(QuestionType type, AnswerCorrectionLlmResponse r) {
if (r == null || blank(r.correctedText()) || blank(r.correctionReason()) || r.alternativeExpressions() == null
if (r == null
|| !languageValidator.isEnglishText(r.correctedText())
|| !languageValidator.isKoreanExplanation(r.correctionReason())
|| !languageValidator.areEnglishExpressions(r.alternativeExpressions())
|| (type == QuestionType.MAIN && (r.followUpQuestion() == null
|| blank(r.followUpQuestion().questionText()) || blank(r.followUpQuestion().koreanTranslation())))
|| (type == QuestionType.FOLLOW_UP && r.followUpQuestion() != null)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ public String create(AnswerCorrectionCommand c) {
return """
You are an English reflection-writing coach.
Correct the answer at the user's English level while preserving its original meaning.
Never invent or add facts. Give a concise correction reason and useful alternative expressions.
Never invent or add facts.
Write correctionReason only in Korean.
Keep correctedText and alternativeExpressions in English.
Comment thread
hyeonky0w0 marked this conversation as resolved.
%s
English level: %s
Question: %s
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.example.todayEng.domain.diary.client;

import static org.assertj.core.api.Assertions.assertThat;

import java.util.List;
import org.junit.jupiter.api.Test;

class AnswerCorrectionLanguageValidatorTest {

private final AnswerCorrectionLanguageValidator validator = new AnswerCorrectionLanguageValidator();

@Test
void acceptsExpectedLanguages() {
assertThat(validator.isEnglishText("It was a good day.")).isTrue();
assertThat(validator.isKoreanExplanation("과거형에 맞게 표현을 수정했어요.")).isTrue();
assertThat(validator.areEnglishExpressions(List.of("I had a good day.", "It was enjoyable."))).isTrue();
assertThat(validator.areEnglishExpressions(List.of())).isTrue();
}

@Test
void rejectsLanguageContractViolations() {
assertThat(validator.isEnglishText("좋은 하루였어요.")).isFalse();
assertThat(validator.isEnglishText("It was 좋은 day.")).isFalse();
assertThat(validator.isKoreanExplanation("Changed the sentence to past tense.")).isFalse();
assertThat(validator.areEnglishExpressions(List.of("I had fun.", "즐거웠어요."))).isFalse();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ void mainPromptRequiresGroundedFollowUpAndMeaningPreservation() throws Exception
"It good", EnglishLevel.BEGINNER,
new ObjectMapper().readTree("{\"event\":\"walk\"}")));
assertThat(prompt).contains("preserving its original meaning", "Never invent or add facts",
"Write correctionReason only in Korean",
"Keep correctedText and alternativeExpressions in English",
"exactly one natural follow-up", "BEGINNER", "walk");
}

Expand Down
Loading