diff --git a/aem/core/src/main/java/com/composum/ai/aem/core/impl/autotranslate/AutoPageTranslateServiceImpl.java b/aem/core/src/main/java/com/composum/ai/aem/core/impl/autotranslate/AutoPageTranslateServiceImpl.java index 7c08e79e..239491f7 100644 --- a/aem/core/src/main/java/com/composum/ai/aem/core/impl/autotranslate/AutoPageTranslateServiceImpl.java +++ b/aem/core/src/main/java/com/composum/ai/aem/core/impl/autotranslate/AutoPageTranslateServiceImpl.java @@ -1,5 +1,8 @@ package com.composum.ai.aem.core.impl.autotranslate; +import static com.composum.ai.backend.base.service.chat.impl.GPTTranslationServiceImpl.LASTID; +import static com.composum.ai.backend.base.service.chat.impl.GPTTranslationServiceImpl.MULTITRANSLATION_SEPARATOR_END; +import static com.composum.ai.backend.base.service.chat.impl.GPTTranslationServiceImpl.MULTITRANSLATION_SEPARATOR_START; import static java.util.Objects.requireNonNull; import static org.apache.commons.lang3.StringUtils.isNotBlank; @@ -155,17 +158,17 @@ public Stats translateLiveCopy(@Nonnull Resource resource, additionalInstructions.replaceAll(MARKER_DEBUG_ADDITIONAL_INSTRUCTIONS, "")); } - // We also insert texts that are already translated since they might guide the translation process + configuration = maybeIncludeAlreadyTranslatedTextAsExample(propertiesToTranslate, autoTranslateCaConfig, configuration); + + propertiesToTranslate = reducePropertiesToTranslate(propertiesToTranslate, autoTranslateCaConfig); List valuesToTranslate = propertiesToTranslate.stream() - .filter(p -> autoTranslateConfigService.includeAlreadyTranslatedValues() || !p.isAlreadyCorrectlyTranslated) .map(PropertyToTranslate::getSourceValue) .collect(Collectors.toList()); List translatedValues = translationService.fragmentedTranslation(valuesToTranslate, languageName, configuration, Collections.singletonList(GPTResponseCheck.KEEP_HREF_TRANSLATION_CHECK)); - translatedValues = remapPaths(translatedValues, relationship.getLiveCopy().getBlueprintPath(), relationship.getLiveCopy().getPath() - ); + translatedValues = remapPaths(translatedValues, relationship.getLiveCopy().getBlueprintPath(), relationship.getLiveCopy().getPath()); Map relationships = new HashMap<>(); @@ -229,6 +232,85 @@ public Stats translateLiveCopy(@Nonnull Resource resource, return stats; } + /** + * Collects the values we need to translate. + * If configured, we also insert texts that are already translated since they might guide the translation process. + */ + protected List reducePropertiesToTranslate(List propertiesToTranslate, AutoTranslateCaConfig autoTranslateCaConfig) { + boolean includeFullPageInRetranslation = autoTranslateConfigService.includeFullPageInRetranslation() + || trueTristateCaConfig(autoTranslateCaConfig.includeFullPageInRetranslation()); + boolean[] includeIndizes = new boolean[propertiesToTranslate.size()]; + for (int i = 0; i < propertiesToTranslate.size(); i++) { + includeIndizes[i] = includeFullPageInRetranslation || !propertiesToTranslate.get(i).isAlreadyCorrectlyTranslated; + } + + expandSelection(includeIndizes, 2); + + List reducedProps = new ArrayList<>(); + for (int i = 0; i < propertiesToTranslate.size(); i++) { + if (includeIndizes[i]) { + reducedProps.add(propertiesToTranslate.get(i)); + } + } + return reducedProps; + } + + /** + * Also include 2 items before those already set, and 2 items after those already set, to have some context. + */ + protected static void expandSelection(boolean[] includeIndizes, int selectRange) { + int lastSetIndex = Integer.MIN_VALUE; + for (int i = 0; i < includeIndizes.length; i++) { + if (includeIndizes[i]) { + lastSetIndex = i; + } else if (i <= lastSetIndex + selectRange) { + includeIndizes[i] = true; + } + } + lastSetIndex = Integer.MAX_VALUE; + for (int i = includeIndizes.length - 1; i >= 0; i--) { + if (includeIndizes[i]) { + lastSetIndex = i; + } else if (i >= lastSetIndex - selectRange) { + includeIndizes[i] = true; + } + } + } + + /** + * If configured, we include the already translated parts of the page as example. + */ + protected GPTConfiguration maybeIncludeAlreadyTranslatedTextAsExample( + List propertiesToTranslate, + AutoTranslateCaConfig autoTranslateCaConfig, GPTConfiguration configuration) { + boolean includeExistingTranslationsInRetranslation = + autoTranslateConfigService.includeExistingTranslationsInRetranslation() || + trueTristateCaConfig(autoTranslateCaConfig.includeExistingTranslationsInRetranslation()); + + String alreadyTranslatedText = propertiesToTranslate.stream() + .filter(p -> p.isAlreadyCorrectlyTranslated) + .map(PropertyToTranslate::getTargetValue) + .collect(Collectors.joining("\n")); + + if (includeExistingTranslationsInRetranslation && StringUtils.isNotBlank(alreadyTranslatedText)) { + configuration = configuration.merge(GPTConfiguration.ofContext( + "Retrieve the result of a previous translation of parts of the text. You don't need to translate this - this is just contextual information and you can draw on that for translation examples and context of the translation that is done later.", + // we have to follow the final format or that is confusing for the AI + MULTITRANSLATION_SEPARATOR_START + LASTID + MULTITRANSLATION_SEPARATOR_END + + alreadyTranslatedText + + MULTITRANSLATION_SEPARATOR_START + LASTID + MULTITRANSLATION_SEPARATOR_END + )); + } + return configuration; + } + + /** + * Is counted as true if there is a true value in the array. + */ + protected boolean trueTristateCaConfig(boolean[] value) { + return value != null && Arrays.asList(value).contains(true); + } + /** * Checks whether there are href="path" in the translatedValues where path is within blueprintPath * and replaces those with the according path in the live copy. diff --git a/aem/core/src/main/java/com/composum/ai/aem/core/impl/autotranslate/AutoTranslateCaConfig.java b/aem/core/src/main/java/com/composum/ai/aem/core/impl/autotranslate/AutoTranslateCaConfig.java index 402d44fa..89f15c04 100644 --- a/aem/core/src/main/java/com/composum/ai/aem/core/impl/autotranslate/AutoTranslateCaConfig.java +++ b/aem/core/src/main/java/com/composum/ai/aem/core/impl/autotranslate/AutoTranslateCaConfig.java @@ -21,4 +21,17 @@ @Property(label = "Rules that give additional instructions for translation if certain words or phrases are present in the page.") AutoTranslateRuleConfig[] rules() default {}; + @Property(label = "Include Full Page during Retranslation", + description = "If true we do not only provide changed texts to the AI during re-translating a page with some changes," + + "but give the entire page to provide better context. That is a bit slower and a bit more expensive, but likely" + + "improves the result. This overrides the default from OSGI configuration.") + boolean[] includeFullPageInRetranslation(); + + @Property(label = "Include Existing Translations in Retranslation", + description = "If true, when retranslating a page with some changes we provide" + + "the existing translations of that page to the AI as well as additional context with examples. " + + "That is a bit slower and a bit more expensive, but likely improves the result." + + "This overrides the default from OSGI configuration.") + boolean[] includeExistingTranslationsInRetranslation() default true; + } diff --git a/aem/core/src/main/java/com/composum/ai/aem/core/impl/autotranslate/AutoTranslateConfig.java b/aem/core/src/main/java/com/composum/ai/aem/core/impl/autotranslate/AutoTranslateConfig.java index 47cad319..2fbea211 100644 --- a/aem/core/src/main/java/com/composum/ai/aem/core/impl/autotranslate/AutoTranslateConfig.java +++ b/aem/core/src/main/java/com/composum/ai/aem/core/impl/autotranslate/AutoTranslateConfig.java @@ -45,11 +45,16 @@ description = "If true, the translator will use the 'high-intelligence model' (see OpenAI config) for translation. Default: true.") boolean useHighIntelligenceModel() default true; - @AttributeDefinition(name = "Include Already Translated Values", - description = "If a page is re-translated with only a few modified texts: " + - "If true we include the source texts that do not have to be translated, too, " + - "to provide better context to the translation; otherwise " + - "we only include the texts that have to be translated.") - boolean includeAlreadyTranslatedValues() default true; + @AttributeDefinition(name = "Include Full Page during Retranslation", + description = "If true we do not only provide changed texts to the AI during re-translating a page with some changes," + + "but give the entire page to provide better context. That is a bit slower and a bit more expensive, but likely" + + "improves the result.") + boolean includeFullPageInRetranslation() default true; + + @AttributeDefinition(name = "Include Existing Translations in Retranslation", + description = "If true, when retranslating a page with some changes we provide" + + "the existing translations of that page to the AI as well as additional context with examples. " + + "That is a bit slower and a bit more expensive, but likely improves the result.") + boolean includeExistingTranslationsInRetranslation() default true; } diff --git a/aem/core/src/main/java/com/composum/ai/aem/core/impl/autotranslate/AutoTranslateConfigService.java b/aem/core/src/main/java/com/composum/ai/aem/core/impl/autotranslate/AutoTranslateConfigService.java index f862a018..7794c7db 100644 --- a/aem/core/src/main/java/com/composum/ai/aem/core/impl/autotranslate/AutoTranslateConfigService.java +++ b/aem/core/src/main/java/com/composum/ai/aem/core/impl/autotranslate/AutoTranslateConfigService.java @@ -31,11 +31,17 @@ public interface AutoTranslateConfigService { List translateableAttributes(@Nullable Resource resource); /** - * If a page is re-translated with only a few modified texts: - * If true we include the source texts that do not have to be translated, too, - * to provide better context to the translation; otherwise - * we only include the texts that have to be translated. + * If true, we do not only provide changed texts to the AI during re-translating a page with some changes, + * but give the entire page to provide better context. + * That is a bit slower and a bit more expensive, but likely improves the result. */ - boolean includeAlreadyTranslatedValues(); + boolean includeFullPageInRetranslation(); + + /** + * If true, we when retranslating a page with some changes we provide the existing translations of that page + * to the AI as well as additional context with examples. + * That is a bit slower and a bit more expensive, but likely improves the result." + */ + boolean includeExistingTranslationsInRetranslation(); } diff --git a/aem/core/src/main/java/com/composum/ai/aem/core/impl/autotranslate/AutoTranslateConfigServiceImpl.java b/aem/core/src/main/java/com/composum/ai/aem/core/impl/autotranslate/AutoTranslateConfigServiceImpl.java index 65c6cb70..2f30fbfa 100644 --- a/aem/core/src/main/java/com/composum/ai/aem/core/impl/autotranslate/AutoTranslateConfigServiceImpl.java +++ b/aem/core/src/main/java/com/composum/ai/aem/core/impl/autotranslate/AutoTranslateConfigServiceImpl.java @@ -168,8 +168,13 @@ public List translateableAttributes(@Nullable Resource resource) { } @Override - public boolean includeAlreadyTranslatedValues() { - return config == null || config.includeAlreadyTranslatedValues(); + public boolean includeFullPageInRetranslation() { + return config == null || config.includeFullPageInRetranslation(); + } + + @Override + public boolean includeExistingTranslationsInRetranslation() { + return config == null || config.includeExistingTranslationsInRetranslation(); } /** diff --git a/aem/core/src/test/java/com/composum/ai/aem/core/impl/autotranslate/AutoPageTranslateServiceImplTest.java b/aem/core/src/test/java/com/composum/ai/aem/core/impl/autotranslate/AutoPageTranslateServiceImplTest.java index 3605f5e0..ceee4920 100644 --- a/aem/core/src/test/java/com/composum/ai/aem/core/impl/autotranslate/AutoPageTranslateServiceImplTest.java +++ b/aem/core/src/test/java/com/composum/ai/aem/core/impl/autotranslate/AutoPageTranslateServiceImplTest.java @@ -2,6 +2,7 @@ import static com.composum.ai.aem.core.impl.autotranslate.AutoPageTranslateServiceImpl.compileContentPattern; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.any; @@ -11,6 +12,7 @@ import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; +import java.util.Arrays; import java.util.List; import java.util.regex.Pattern; import java.util.stream.Collectors; @@ -166,4 +168,47 @@ public void testRemapPaths() { service.remapPaths((String) null, "/content/blueprint", "/content/livecopy")); } + @Test + public void expandSelection_includesContextBeforeAndAfter() { + boolean[] includeIndizes = {false, false, true, false, false}; + AutoPageTranslateServiceImpl.expandSelection(includeIndizes, 2); + assertArrayEquals(new boolean[]{true, true, true, true, true}, includeIndizes); + } + + @Test + public void expandSelection_noInitialSelection() { + boolean[] includeIndizes = {false, false, false, false, false}; + AutoPageTranslateServiceImpl.expandSelection(includeIndizes, 2); + assertArrayEquals(new boolean[]{false, false, false, false, false}, includeIndizes); + } + + @Test + public void expandSelection_singleSelectionAtStart() { + boolean[] includeIndizes = {true, false, false, false, false}; + AutoPageTranslateServiceImpl.expandSelection(includeIndizes, 2); + assertArrayEquals(new boolean[]{true, true, true, false, false}, includeIndizes); + } + + @Test + public void expandSelection_singleSelectionAtEnd() { + boolean[] includeIndizes = {false, false, false, false, true}; + AutoPageTranslateServiceImpl.expandSelection(includeIndizes, 2); + assertArrayEquals(new boolean[]{false, false, true, true, true}, includeIndizes); + } + + @Test + public void expandSelection_multipleSelections() { + boolean[] includeIndizes = {false, true, false, true, false}; + AutoPageTranslateServiceImpl.expandSelection(includeIndizes, 2); + assertArrayEquals(new boolean[]{true, true, true, true, true}, includeIndizes); + } + + @Test + public void expandSelection_long() { + boolean[] includeIndizes = {false, false, false, true, false, true, false, false, false}; + AutoPageTranslateServiceImpl.expandSelection(includeIndizes, 2); + assertArrayEquals(new boolean[]{false, true, true, true, true, true, true, true, false}, includeIndizes); + } + + } diff --git a/backend/base/src/main/java/com/composum/ai/backend/base/service/chat/GPTConfiguration.java b/backend/base/src/main/java/com/composum/ai/backend/base/service/chat/GPTConfiguration.java index 9c310a37..de180b80 100644 --- a/backend/base/src/main/java/com/composum/ai/backend/base/service/chat/GPTConfiguration.java +++ b/backend/base/src/main/java/com/composum/ai/backend/base/service/chat/GPTConfiguration.java @@ -1,5 +1,8 @@ package com.composum.ai.backend.base.service.chat; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; import java.util.Objects; import javax.annotation.Nonnull; @@ -52,9 +55,12 @@ public enum Mode { private final Boolean debug; - private Double temperature; + private final Double temperature; + + private final Integer seed; + + private final List contexts; - private Integer seed; public GPTConfiguration(@Nullable String apiKey, @Nullable String organizationId, @Nullable AnswerType answerType) { this(apiKey, organizationId, answerType, null); @@ -81,6 +87,10 @@ public GPTConfiguration(@Nullable String apiKey, @Nullable String organizationId } public GPTConfiguration(@Nullable String apiKey, @Nullable String organizationId, @Nullable AnswerType answerType, @Nullable String additionalInstructions, @Nullable Mode mode, @Nullable Boolean highIntelligenceNeeded, @Nullable Boolean debug, @Nullable Double temperature, @Nullable Integer seed) { + this(apiKey, organizationId, answerType, additionalInstructions, mode, highIntelligenceNeeded, debug, temperature, seed, null); + } + + public GPTConfiguration(@Nullable String apiKey, @Nullable String organizationId, @Nullable AnswerType answerType, @Nullable String additionalInstructions, @Nullable Mode mode, @Nullable Boolean highIntelligenceNeeded, @Nullable Boolean debug, @Nullable Double temperature, @Nullable Integer seed, @Nullable List contexts) { this.apiKey = apiKey; this.answerType = answerType; this.organizationId = organizationId; @@ -90,6 +100,7 @@ public GPTConfiguration(@Nullable String apiKey, @Nullable String organizationId this.debug = debug; this.temperature = temperature; this.seed = seed; + this.contexts = contexts; } /** @@ -209,7 +220,29 @@ public GPTConfiguration merge(@Nullable GPTConfiguration other, boolean override } Double temperature = this.temperature != null ? this.temperature : other.temperature; Integer seed = this.seed != null ? this.seed : other.seed; - return new GPTConfiguration(apiKey, organizationId, answerType, additionalInstructions, mode, highIntelligenceNeeded, debug, temperature, seed); + List contextInfos = new ArrayList<>(); + if (this.contexts != null) { + contextInfos.addAll(this.contexts); + } + if (other.contexts != null) { + contextInfos.addAll(other.contexts); + } + contextInfos = contextInfos.isEmpty() ? null : Collections.unmodifiableList(contextInfos); + return new GPTConfiguration(apiKey, organizationId, answerType, additionalInstructions, mode, highIntelligenceNeeded, debug, temperature, seed, contextInfos); + } + + /** + * Additional context information to provide to the AI. Not actual instructions, just background information. + */ + public List getContexts() { + return contexts; + } + + /** + * Returns a copy with the contexts replaced. + */ + public GPTConfiguration replaceContexts(List newContexts) { + return new GPTConfiguration(apiKey, organizationId, answerType, additionalInstructions, mode, highIntelligenceNeeded, debug, temperature, seed, newContexts); } /** @@ -233,6 +266,16 @@ public static GPTConfiguration ofAdditionalInstructions(@Nullable String additio return new GPTConfiguration(null, null, null, additionalInstructions); } + @Nonnull + public static GPTConfiguration ofContexts(@Nullable List contexts) { + return new GPTConfiguration(null, null, null, null, null, null, null, null, null, contexts); + } + + @Nonnull + public static GPTConfiguration ofContext(@Nonnull String usermsg, @Nonnull String assistantmsg) { + return ofContexts(Collections.singletonList(new GPTContextInfo(usermsg, assistantmsg))); + } + @Override public String toString() { StringBuilder sb = new StringBuilder("GPTConfiguration{"); @@ -263,6 +306,9 @@ public String toString() { if (seed != null) { sb.append(", seed=").append(seed); } + if (contexts != null) { + sb.append(", contexts=").append(contexts); + } return sb.append('}').toString(); } @@ -297,4 +343,50 @@ public boolean equals(Object o) { public int hashCode() { return Objects.hash(getApiKey(), getAnswerType()); } + + + /** + * Gives the conversation a history - additional assistant - user message pair. + */ + public static class GPTContextInfo { + + private final String userMsg; + private final String assistantMsg; + + public GPTContextInfo(String title, String assistantMsg) { + this.userMsg = title; + this.assistantMsg = assistantMsg; + } + + public String getTitle() { + return userMsg; + } + + public String getText() { + return assistantMsg; + } + + @Override + public String toString() { + return "GPTContextInfo{" + + "userMsg='" + userMsg + '\'' + + ", assistantMsg='" + assistantMsg + '\'' + + '}'; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof GPTContextInfo)) return false; + GPTContextInfo that = (GPTContextInfo) o; + return Objects.equals(getTitle(), that.getTitle()) && Objects.equals(getText(), that.getText()); + } + + @Override + public int hashCode() { + return Objects.hash(getTitle(), getText()); + } + + } + } diff --git a/backend/base/src/main/java/com/composum/ai/backend/base/service/chat/impl/GPTTranslationServiceImpl.java b/backend/base/src/main/java/com/composum/ai/backend/base/service/chat/impl/GPTTranslationServiceImpl.java index 1b80e845..695ff0c4 100644 --- a/backend/base/src/main/java/com/composum/ai/backend/base/service/chat/impl/GPTTranslationServiceImpl.java +++ b/backend/base/src/main/java/com/composum/ai/backend/base/service/chat/impl/GPTTranslationServiceImpl.java @@ -37,8 +37,8 @@ import com.composum.ai.backend.base.service.chat.GPTChatRequest; import com.composum.ai.backend.base.service.chat.GPTCompletionCallback; import com.composum.ai.backend.base.service.chat.GPTConfiguration; -import com.composum.ai.backend.base.service.chat.GPTContentCreationService; import com.composum.ai.backend.base.service.chat.GPTFinishReason; +import com.composum.ai.backend.base.service.chat.GPTMessageRole; import com.composum.ai.backend.base.service.chat.GPTResponseCheck; import com.composum.ai.backend.base.service.chat.GPTTranslationService; @@ -132,12 +132,12 @@ public void streamingSingleTranslation(@Nonnull String text, @Nonnull String sou /** * Start of separator like `%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 573472 %%%%%%%%%%%%%%%%` . */ - protected static final String MULTITRANSLATION_SEPARATOR_START = "\n%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% "; + public static final String MULTITRANSLATION_SEPARATOR_START = "\n%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% "; /** * End of separator like `573472 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%` . */ - protected static final String MULTITRANSLATION_SEPARATOR_END = " %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n"; + public static final String MULTITRANSLATION_SEPARATOR_END = " %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n"; /** * Regexp matching separator like `%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 573472 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%` (group "id" matches the number). @@ -146,7 +146,7 @@ public void streamingSingleTranslation(@Nonnull String text, @Nonnull String sou */ protected static final Pattern MULTITRANSLATION_SEPARATOR_PATTERN = Pattern.compile("(?\\d{6}) %{20,40}(?!%)"); - protected static final String LASTID = "424242"; + public static final String LASTID = "424242"; protected static final Pattern PATTERN_HAS_LETTER = Pattern.compile("\\p{L}"); @@ -237,12 +237,15 @@ protected List fragmentedTranslationDivideAndConquer(@Nonnull List firstHalf = texts.subList(0, half); List secondHalf = texts.subList(half, texts.size()); List result = new ArrayList<>(); - result.addAll(fragmentedTranslationDivideAndConquer(firstHalf, targetLanguage, configuration, permittedRetries, translationChecks)); - result.addAll(fragmentedTranslationDivideAndConquer(secondHalf, targetLanguage, configuration, permittedRetries, translationChecks)); + result.addAll(fragmentedTranslationDivideAndConquer(firstHalf, targetLanguage, cleanedConfiguration, permittedRetries, translationChecks)); + result.addAll(fragmentedTranslationDivideAndConquer(secondHalf, targetLanguage, cleanedConfiguration, permittedRetries, translationChecks)); return result; } @@ -258,7 +261,7 @@ protected List fragmentedTranslation(@Nonnull List texts, @Nonnu String response = singleTranslation(joinedtexts, null, targetLanguage, configuration); String responseProblems; - while((responseProblems = GPTResponseCheck.collectResponseProblems(translationChecks, joinedtexts, response)) != null) { + while ((responseProblems = GPTResponseCheck.collectResponseProblems(translationChecks, joinedtexts, response)) != null) { if (permittedRetries.decrementAndGet() <= 0) { LOG.error("Too many retries with response problems for fragmented translation, to {} found problems {} text {}", targetLanguage, responseProblems, texts); throw new GPTException("Too many retries for fragmented translation, response problems: " + responseProblems); @@ -340,7 +343,18 @@ private GPTChatRequest makeRequest(String text, String sourceLanguage, String ta parameters.put("targetlanguage", targetLanguage); parameters.put("addition", addition); List messages = template.getMessages(parameters); + if (configuration.getContexts() != null && !configuration.getContexts().isEmpty()) { + int start = messages.get(0).getRole() == GPTMessageRole.SYSTEM ? 1 : 0; + for (int i = configuration.getContexts().size() - 1; i >= 0; i--) { + GPTConfiguration.GPTContextInfo context = configuration.getContexts().get(i); + GPTChatMessage contextMessage = new GPTChatMessage(GPTMessageRole.USER, context.getTitle()); + messages.add(start, contextMessage); + contextMessage = new GPTChatMessage(GPTMessageRole.ASSISTANT, context.getText()); + messages.add(start + 1, contextMessage); + } + } request.addMessages(messages); + // set request.setMaxTokens to about 3 times the number of tokens in the text to translate // since that seems a generous limit for the translation, but gives a leeway for error messages. // this usually quite an overestimation, but that's better than underestimating in this context. diff --git a/backend/base/src/main/resources/chattemplates/chatgpt/singletranslation.txt b/backend/base/src/main/resources/chattemplates/chatgpt/singletranslation.txt index 064cc966..1e665fa0 100644 --- a/backend/base/src/main/resources/chattemplates/chatgpt/singletranslation.txt +++ b/backend/base/src/main/resources/chattemplates/chatgpt/singletranslation.txt @@ -8,6 +8,6 @@ Print the original text you have to translate exactly without any comments. ---------- assistant ---------- ${sourcephrase} ---------- user ---------- -Print the original text translated into ${targetlanguage}. +Translate the original text you just printed into ${targetlanguage}. ${addition}