From 5ffe4656709a9807c39f1b851d432bd42ee7188f Mon Sep 17 00:00:00 2001 From: Rashidi Zin Date: Mon, 30 Oct 2023 23:56:44 +0700 Subject: [PATCH] Change to history --- .../boot/langchain4j/history/Historian.java | 20 +++++++ .../HistorianConfiguration.java} | 18 +++--- .../langchain4j/history/HistorianTool.java | 22 +++++++ .../boot/langchain4j/history/History.java | 54 +++++++++++++++++ .../boot/langchain4j/translate/Translate.java | 14 ----- .../translate/TranslationService.java | 26 -------- .../src/main/resources/application.properties | 10 ++-- .../HistorianTests.java} | 59 ++++++++++--------- 8 files changed, 143 insertions(+), 80 deletions(-) create mode 100644 langchain4j/src/main/java/zin/rashidi/boot/langchain4j/history/Historian.java rename langchain4j/src/main/java/zin/rashidi/boot/langchain4j/{translate/TranslationConfiguration.java => history/HistorianConfiguration.java} (72%) create mode 100644 langchain4j/src/main/java/zin/rashidi/boot/langchain4j/history/HistorianTool.java create mode 100644 langchain4j/src/main/java/zin/rashidi/boot/langchain4j/history/History.java delete mode 100644 langchain4j/src/main/java/zin/rashidi/boot/langchain4j/translate/Translate.java delete mode 100644 langchain4j/src/main/java/zin/rashidi/boot/langchain4j/translate/TranslationService.java rename langchain4j/src/test/java/zin/rashidi/boot/langchain4j/{translate/TranslationServiceTests.java => history/HistorianTests.java} (54%) diff --git a/langchain4j/src/main/java/zin/rashidi/boot/langchain4j/history/Historian.java b/langchain4j/src/main/java/zin/rashidi/boot/langchain4j/history/Historian.java new file mode 100644 index 00000000..d21d4760 --- /dev/null +++ b/langchain4j/src/main/java/zin/rashidi/boot/langchain4j/history/Historian.java @@ -0,0 +1,20 @@ +package zin.rashidi.boot.langchain4j.history; + +import dev.langchain4j.service.SystemMessage; +import dev.langchain4j.service.UserMessage; +import dev.langchain4j.service.V; + +/** + * @author Rashidi Zin + */ +interface Historian { + + @SystemMessage(""" + You are a historian who is an expert for {{country}}. + Given provided year is supported, you will provide historical events that occurred within the year. + You will also include detail about the event. + """) + @UserMessage("{{year}}") + History chat(@V("country") String country, @V("year") int year); + +} diff --git a/langchain4j/src/main/java/zin/rashidi/boot/langchain4j/translate/TranslationConfiguration.java b/langchain4j/src/main/java/zin/rashidi/boot/langchain4j/history/HistorianConfiguration.java similarity index 72% rename from langchain4j/src/main/java/zin/rashidi/boot/langchain4j/translate/TranslationConfiguration.java rename to langchain4j/src/main/java/zin/rashidi/boot/langchain4j/history/HistorianConfiguration.java index 71ef9c39..81538934 100644 --- a/langchain4j/src/main/java/zin/rashidi/boot/langchain4j/translate/TranslationConfiguration.java +++ b/langchain4j/src/main/java/zin/rashidi/boot/langchain4j/history/HistorianConfiguration.java @@ -1,6 +1,8 @@ -package zin.rashidi.boot.langchain4j.translate; +package zin.rashidi.boot.langchain4j.history; import dev.langchain4j.data.segment.TextSegment; +import dev.langchain4j.memory.ChatMemory; +import dev.langchain4j.memory.chat.MessageWindowChatMemory; import dev.langchain4j.model.chat.ChatLanguageModel; import dev.langchain4j.model.embedding.AllMiniLmL6V2EmbeddingModel; import dev.langchain4j.retriever.EmbeddingStoreRetriever; @@ -8,7 +10,6 @@ import dev.langchain4j.service.AiServices; import dev.langchain4j.store.embedding.EmbeddingStore; import dev.langchain4j.store.embedding.elasticsearch.ElasticsearchEmbeddingStore; -import org.springframework.boot.autoconfigure.elasticsearch.ElasticsearchProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.env.Environment; @@ -19,14 +20,15 @@ * @author Rashidi Zin */ @Configuration -class TranslationConfiguration { +class HistorianConfiguration { @Bean - TranslationService translateService(ChatLanguageModel chatLanguageModel, Retriever retriever) { - return AiServices.builder(TranslationService.class) - .chatLanguageModel(chatLanguageModel) - .chatMemory(withMaxMessages(20)) + Historian historian(ChatLanguageModel model, Retriever retriever, HistorianTool tool) { + return AiServices.builder(Historian.class) + .chatLanguageModel(model) + .chatMemory(withMaxMessages(10)) .retriever(retriever) + .tools(tool) .build(); } @@ -39,7 +41,7 @@ Retriever retriever(EmbeddingStore embeddingStore) { EmbeddingStore embeddingStore(Environment environment) { return ElasticsearchEmbeddingStore.builder() .serverUrl(environment.getProperty("app.elasticsearch.uri")) - .indexName("translation") + .indexName("history") .build(); } diff --git a/langchain4j/src/main/java/zin/rashidi/boot/langchain4j/history/HistorianTool.java b/langchain4j/src/main/java/zin/rashidi/boot/langchain4j/history/HistorianTool.java new file mode 100644 index 00000000..04357f9c --- /dev/null +++ b/langchain4j/src/main/java/zin/rashidi/boot/langchain4j/history/HistorianTool.java @@ -0,0 +1,22 @@ +package zin.rashidi.boot.langchain4j.history; + +import dev.langchain4j.agent.tool.P; +import dev.langchain4j.agent.tool.Tool; +import org.springframework.stereotype.Component; +import org.springframework.util.Assert; + +import java.time.LocalDate; +import java.time.Year; + +/** + * @author Rashidi Zin + */ +@Component +class HistorianTool { + + @Tool("Validate year is supported") + public void assertYear(int year) { + Assert.isTrue(year < 2021, "Year must be less than 2021"); + } + +} diff --git a/langchain4j/src/main/java/zin/rashidi/boot/langchain4j/history/History.java b/langchain4j/src/main/java/zin/rashidi/boot/langchain4j/history/History.java new file mode 100644 index 00000000..27bd4182 --- /dev/null +++ b/langchain4j/src/main/java/zin/rashidi/boot/langchain4j/history/History.java @@ -0,0 +1,54 @@ +package zin.rashidi.boot.langchain4j.history; + +/** + * @author Rashidi Zin + */ +class History { + + private final String country; + private final int year; + private String person; + private String event; + private String error; + + public History(String country, int year) { + this.country = country; + this.year = year; + } + + public String country() { + return country; + } + + public int year() { + return year; + } + + public String person() { + return person; + } + + public History person(String person) { + this.person = person; + return this; + } + + public String event() { + return event; + } + + public History event(String event) { + this.event = event; + return this; + } + + public String error() { + return error; + } + + public History error(String error) { + this.error = error; + return this; + } + +} diff --git a/langchain4j/src/main/java/zin/rashidi/boot/langchain4j/translate/Translate.java b/langchain4j/src/main/java/zin/rashidi/boot/langchain4j/translate/Translate.java deleted file mode 100644 index c3189a42..00000000 --- a/langchain4j/src/main/java/zin/rashidi/boot/langchain4j/translate/Translate.java +++ /dev/null @@ -1,14 +0,0 @@ -package zin.rashidi.boot.langchain4j.translate; - -import java.util.List; - -/** - * @author Rashidi Zin - */ -record Translate(Detail language, Text text) { - - record Text(String source, String target, List breakdowns) {} - - record Detail(String source, String target) {} - -} diff --git a/langchain4j/src/main/java/zin/rashidi/boot/langchain4j/translate/TranslationService.java b/langchain4j/src/main/java/zin/rashidi/boot/langchain4j/translate/TranslationService.java deleted file mode 100644 index 619fb21e..00000000 --- a/langchain4j/src/main/java/zin/rashidi/boot/langchain4j/translate/TranslationService.java +++ /dev/null @@ -1,26 +0,0 @@ -package zin.rashidi.boot.langchain4j.translate; - -import dev.langchain4j.service.SystemMessage; -import dev.langchain4j.service.UserMessage; -import dev.langchain4j.service.V; - -/** - * @author Rashidi Zin - */ -interface TranslationService { - - @SystemMessage({ - "You are a translator for {{sourceLanguage}} to {{targetLanguage}}.", - """ - Your response should be in JSON format which includes the following fields: - - language.source - The source language of the text. - - language.target - The target language of the text. - - text.source - The source text. - - text.target - The target text. - - text.breakdowns - The breakdowns of the source and target text. - """ - }) - @UserMessage("Translate this text: {{text}}") - Translate translate(@V("sourceLanguage") String sourceLanguage, @V("targetLanguage") String targetLanguage, @V("text") String text); - -} diff --git a/langchain4j/src/main/resources/application.properties b/langchain4j/src/main/resources/application.properties index 9153b2c4..bc51c548 100644 --- a/langchain4j/src/main/resources/application.properties +++ b/langchain4j/src/main/resources/application.properties @@ -1,8 +1,8 @@ langchain4j.chat-model.provider=openai langchain4j.chat-model.openai.api-key=demo -langchain4j.chat-model.openai.model-name=gpt-3.5-turbo -langchain4j.chat-model.openai.temperature=0.0 -#langchain4j.chat-model.openai.top-p=1.0 +langchain4j.chat-model.openai.model-name=gpt-4 +langchain4j.chat-model.openai.temperature=0 +langchain4j.chat-model.openai.top-p=1.0 #langchain4j.chat-model.openai.max-tokens=100 #langchain4j.chat-model.openai.presence-penalty=0.0 #langchain4j.chat-model.openai.frequency-penalty=0.0 @@ -11,5 +11,5 @@ langchain4j.chat-model.openai.timeout=PT60S #langchain4j.chat-model.openai.log-requests=true #langchain4j.chat-model.openai.log-responses=true -logging.level.dev.langchain4j=ERROR -logging.level.dev.ai4j.openai4j=ERROR \ No newline at end of file +logging.level.dev.langchain4j=DEBUG +logging.level.dev.ai4j.openai4j=DEBUG \ No newline at end of file diff --git a/langchain4j/src/test/java/zin/rashidi/boot/langchain4j/translate/TranslationServiceTests.java b/langchain4j/src/test/java/zin/rashidi/boot/langchain4j/history/HistorianTests.java similarity index 54% rename from langchain4j/src/test/java/zin/rashidi/boot/langchain4j/translate/TranslationServiceTests.java rename to langchain4j/src/test/java/zin/rashidi/boot/langchain4j/history/HistorianTests.java index d0f8d36c..075a9d93 100644 --- a/langchain4j/src/test/java/zin/rashidi/boot/langchain4j/translate/TranslationServiceTests.java +++ b/langchain4j/src/test/java/zin/rashidi/boot/langchain4j/history/HistorianTests.java @@ -1,9 +1,4 @@ -package zin.rashidi.boot.langchain4j.translate; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.groups.Tuple.tuple; - -import java.io.IOException; +package zin.rashidi.boot.langchain4j.history; import org.apache.http.HttpHost; import org.elasticsearch.client.Request; @@ -20,12 +15,18 @@ import org.testcontainers.junit.jupiter.Testcontainers; import org.testcontainers.utility.DockerImageName; +import java.io.IOException; +import java.time.LocalDate; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + /** * @author Rashidi Zin */ @Testcontainers @SpringBootTest -class TranslationServiceTests { +class HistorianTests { @Container private static final ElasticsearchContainer elastic = new ElasticsearchContainer( @@ -41,35 +42,39 @@ static void properties(DynamicPropertyRegistry registry) { @BeforeAll static void createIndex() throws IOException { try (var client = RestClient.builder(HttpHost.create(elastic.getHttpHostAddress())).build()) { - client.performRequest(new Request("PUT", "/translation")); + client.performRequest(new Request("PUT", "/history")); } } @Autowired - private TranslationService service; + private Historian historian; @Test - @DisplayName("When I request to translate a phrase from Spanish to English Then the translation should consists of source and target language, translated text, and word breakdowns") - void translate() { - var result = service.translate("spanish", "english", "Yo soy un salsero"); + @DisplayName("When I ask the Historian about the history of Malaysia in 1957, Then I should get information about Hari Merdeka") + void chat() { + var message = historian.chat("Malaysia", 1957); - assertThat(result).satisfies(translate -> { + assertThat(message) + .extracting("country", "year", "person") + .containsExactly("Malaysia", 1957, "Tunku Abdul Rahman"); - assertThat(translate) - .extracting("language.source", "language.target", "text.source", "text.target") - .containsExactly("es", "en", "Yo soy un salsero", "I am a salsa dancer"); + assertThat(message) + .extracting("event").asString() + .contains("Hari Merdeka"); + } + + @Test + @DisplayName("When I ask the Historian about event after 2021, Then an error message should be returned") + void unsupportedYear() { + var message = historian.chat("Malaysia", 2022); - assertThat(translate) - .extracting("text.breakdowns").asList() - .extracting("source", "target") - .containsExactly( - tuple("Yo", "I"), - tuple("soy", "am"), - tuple("un", "a"), - tuple("salsero", "salsa dancer") - ); - }); + assertThat(message) + .extracting("country", "year", "error") + .containsExactly("Malaysia", 2022, "Year must be less than 2021"); + assertThat(message) + .extracting("person", "event").asString() + .containsWhitespaces(); } -} +} \ No newline at end of file