diff --git a/aitools/pom.xml b/aitools/pom.xml index 9e2031b..2f461e4 100644 --- a/aitools/pom.xml +++ b/aitools/pom.xml @@ -5,7 +5,7 @@ org.springframework.boot spring-boot-starter-parent - 4.0.1 + 3.5.9 ru.ldeloff @@ -28,12 +28,39 @@ 25 + 1.1.2 + + + + org.springframework.ai + spring-ai-bom + ${spring-ai.version} + pom + import + + + + + org.springframework.ai + spring-ai-starter-model-openai + org.springframework.boot spring-boot-starter-webflux + + org.springdoc + springdoc-openapi-starter-webflux-ui + 2.8.15 + + + + org.telegram + telegrambots + 6.9.7.1 + org.projectlombok @@ -42,16 +69,10 @@ - org.springframework.boot - spring-boot-starter-webflux-test + io.projectreactor + reactor-test test - - - org.springdoc - springdoc-openapi-starter-webflux-ui - 3.0.1 - diff --git a/aitools/src/main/java/ru/ldeloff/aitools/AitoolsApplication.java b/aitools/src/main/java/ru/ldeloff/aitools/AitoolsApplication.java index dae30b5..13b484c 100644 --- a/aitools/src/main/java/ru/ldeloff/aitools/AitoolsApplication.java +++ b/aitools/src/main/java/ru/ldeloff/aitools/AitoolsApplication.java @@ -2,7 +2,9 @@ package ru.ldeloff.aitools; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cache.annotation.EnableCaching; +@EnableCaching @SpringBootApplication public class AitoolsApplication { diff --git a/aitools/src/main/java/ru/ldeloff/aitools/datetime/controller/TimeController.java b/aitools/src/main/java/ru/ldeloff/aitools/externaltools/datetime/controller/TimeController.java similarity index 94% rename from aitools/src/main/java/ru/ldeloff/aitools/datetime/controller/TimeController.java rename to aitools/src/main/java/ru/ldeloff/aitools/externaltools/datetime/controller/TimeController.java index d106c21..a332070 100644 --- a/aitools/src/main/java/ru/ldeloff/aitools/datetime/controller/TimeController.java +++ b/aitools/src/main/java/ru/ldeloff/aitools/externaltools/datetime/controller/TimeController.java @@ -1,4 +1,4 @@ -package ru.ldeloff.aitools.datetime.controller; +package ru.ldeloff.aitools.externaltools.datetime.controller; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; @@ -9,7 +9,7 @@ import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import reactor.core.publisher.Mono; -import ru.ldeloff.aitools.datetime.dto.TimeResponse; +import ru.ldeloff.aitools.externaltools.datetime.dto.TimeResponse; import java.time.ZoneId; import java.time.LocalDateTime; diff --git a/aitools/src/main/java/ru/ldeloff/aitools/datetime/dto/TimeResponse.java b/aitools/src/main/java/ru/ldeloff/aitools/externaltools/datetime/dto/TimeResponse.java similarity index 67% rename from aitools/src/main/java/ru/ldeloff/aitools/datetime/dto/TimeResponse.java rename to aitools/src/main/java/ru/ldeloff/aitools/externaltools/datetime/dto/TimeResponse.java index d2fd6c0..ec17683 100644 --- a/aitools/src/main/java/ru/ldeloff/aitools/datetime/dto/TimeResponse.java +++ b/aitools/src/main/java/ru/ldeloff/aitools/externaltools/datetime/dto/TimeResponse.java @@ -1,4 +1,4 @@ -package ru.ldeloff.aitools.datetime.dto; +package ru.ldeloff.aitools.externaltools.datetime.dto; import lombok.AllArgsConstructor; diff --git a/aitools/src/main/java/ru/ldeloff/aitools/telegrambot/bot/TelegramBot.java b/aitools/src/main/java/ru/ldeloff/aitools/telegrambot/bot/TelegramBot.java new file mode 100644 index 0000000..29d6eab --- /dev/null +++ b/aitools/src/main/java/ru/ldeloff/aitools/telegrambot/bot/TelegramBot.java @@ -0,0 +1,78 @@ +package ru.ldeloff.aitools.telegrambot.bot; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.ai.chat.messages.AssistantMessage; +import org.springframework.ai.chat.messages.Message; +import org.springframework.ai.chat.messages.UserMessage; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; +import org.telegram.telegrambots.bots.TelegramLongPollingBot; +import org.telegram.telegrambots.meta.api.methods.send.SendMessage; +import org.telegram.telegrambots.meta.api.objects.Update; +import org.telegram.telegrambots.meta.exceptions.TelegramApiException; +import ru.ldeloff.aitools.telegrambot.cache.MessageCache; +import ru.ldeloff.aitools.telegrambot.service.LlmService; + +import java.util.List; + +@Slf4j +@Component +@RequiredArgsConstructor +public class TelegramBot extends TelegramLongPollingBot { + + private final MessageCache messageCache; + private final LlmService llmService; + + @Value("${telegram.bot.name}") + private String telegramBotName; + @Value("${telegram.bot.token}") + private String telegramBotToken; + + + @Override + public String getBotUsername() { + return telegramBotName; + } + + @Override + public String getBotToken() { + return telegramBotToken; + } + + @Override + public void onUpdateReceived(Update update) { + log.info(update.toString()); + + if (update.hasMessage()) { + Long chatId = update.getMessage().getChatId(); + if (update.getMessage().hasText()) { + String messageText = update.getMessage().getText(); + if ("/start".equals(messageText)) { + messageCache.clearMessages(chatId); + sendText(chatId, "Начинаем диалог!"); + } + List messageList = new java.util.ArrayList<>(messageCache.getOrInitMessages(chatId).stream().toList()); + messageList.add(new UserMessage(messageText)); + List assistantMessageList = llmService.sendMessages(messageList); + messageList.add(new AssistantMessage(assistantMessageList.getFirst())); + messageCache.saveMessages(chatId, messageList); + sendText(chatId, assistantMessageList.getFirst()); + } else { + sendText(chatId, "Я понимаю только текст!"); + } + + } + } + + public void sendText(Long telegramId, String message){ + SendMessage sm = SendMessage.builder() + .chatId(telegramId.toString()) //Who are we sending a message to + .text(message).build(); //Message content + try { + execute(sm); //Actually sending the message + } catch (TelegramApiException e) { + throw new RuntimeException(e); //Any error will be printed here + } + } +} \ No newline at end of file diff --git a/aitools/src/main/java/ru/ldeloff/aitools/telegrambot/cache/MessageCache.java b/aitools/src/main/java/ru/ldeloff/aitools/telegrambot/cache/MessageCache.java new file mode 100644 index 0000000..bd87c48 --- /dev/null +++ b/aitools/src/main/java/ru/ldeloff/aitools/telegrambot/cache/MessageCache.java @@ -0,0 +1,29 @@ +package ru.ldeloff.aitools.telegrambot.cache; + +import org.springframework.ai.chat.messages.Message; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.cache.annotation.CachePut; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.stereotype.Component; + +import java.util.List; + +@Component +public class MessageCache { + + @Cacheable(cacheNames = "messages", key = "#chatId") + public List getOrInitMessages(Long chatId) { + return List.of(); + } + + @CachePut(cacheNames = "messages", key = "#chatId") + public List saveMessages(Long chatId, List messages) { + return messages; + } + + @CacheEvict(cacheNames = "messages", key = "#chatId") + public List clearMessages(Long chatId) { + return List.of(); + } + +} diff --git a/aitools/src/main/java/ru/ldeloff/aitools/telegrambot/config/TelegramBotConfiguration.java b/aitools/src/main/java/ru/ldeloff/aitools/telegrambot/config/TelegramBotConfiguration.java new file mode 100644 index 0000000..22f5c00 --- /dev/null +++ b/aitools/src/main/java/ru/ldeloff/aitools/telegrambot/config/TelegramBotConfiguration.java @@ -0,0 +1,18 @@ +package ru.ldeloff.aitools.telegrambot.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.telegram.telegrambots.meta.TelegramBotsApi; +import org.telegram.telegrambots.meta.exceptions.TelegramApiException; +import org.telegram.telegrambots.updatesreceivers.DefaultBotSession; +import ru.ldeloff.aitools.telegrambot.bot.TelegramBot; + +@Configuration +public class TelegramBotConfiguration { + @Bean + public TelegramBotsApi telegramBotsApi(TelegramBot telegramBot) throws TelegramApiException { + var api = new TelegramBotsApi(DefaultBotSession.class); + api.registerBot(telegramBot); + return api; + } +} diff --git a/aitools/src/main/java/ru/ldeloff/aitools/telegrambot/service/LlmService.java b/aitools/src/main/java/ru/ldeloff/aitools/telegrambot/service/LlmService.java new file mode 100644 index 0000000..33abd9d --- /dev/null +++ b/aitools/src/main/java/ru/ldeloff/aitools/telegrambot/service/LlmService.java @@ -0,0 +1,32 @@ +package ru.ldeloff.aitools.telegrambot.service; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.ai.chat.messages.Message; +import org.springframework.ai.chat.prompt.Prompt; +import org.springframework.ai.openai.OpenAiChatModel; +import org.springframework.ai.openai.OpenAiChatOptions; +import org.springframework.ai.openai.api.ResponseFormat; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Slf4j +@Service +@RequiredArgsConstructor +public class LlmService { + private final OpenAiChatModel model; + + public List sendMessages(List messages) { + return model.call( + new Prompt( + messages, + OpenAiChatOptions.builder() + .temperature(0.1) + .responseFormat(ResponseFormat.builder().type(ResponseFormat.Type.TEXT).build()) + .build() + + ) + ).getResults().stream().map(x -> x.getOutput().getText()).toList(); + } +} diff --git a/aitools/src/main/resources/application.properties b/aitools/src/main/resources/application.properties deleted file mode 100644 index fb3d424..0000000 --- a/aitools/src/main/resources/application.properties +++ /dev/null @@ -1,3 +0,0 @@ -spring.application.name=aitools - -springdoc.api-docs.path=/api-docs diff --git a/aitools/src/main/resources/application.yaml b/aitools/src/main/resources/application.yaml new file mode 100644 index 0000000..27e9f82 --- /dev/null +++ b/aitools/src/main/resources/application.yaml @@ -0,0 +1,25 @@ +spring: + application: + name: aitools + ai: + openai: + api-key: "no-key-needed" # OpenWebUI не требует ключа, но поле обязательно + base-url: http://192.168.10.110:8080 # Путь к API OpenWebUI + chat: + options: +# model: "large/Qwen3-Next-80B-A3B-Thinking-UD-Q8_K_XL" + model: "large/Qwen3-Next-80B-A3B-Instruct-UD-Q8_K_XL" + client: + connect-timeout: 300s + read-timeout: 300s + write-timeout: 300s + + +springdoc: + api-docs: + path: /api-docs + +telegram: + bot: + name: ${TELEGRAM_BOT_NAME} + token: ${TELEGRAM_BOT_TOKEN} \ No newline at end of file diff --git a/aitools/src/test/java/ru/ldeloff/aitools/AitoolsApplicationTests.java b/aitools/src/test/java/ru/ldeloff/aitools/AitoolsApplicationTests.java deleted file mode 100644 index 7ee1edf..0000000 --- a/aitools/src/test/java/ru/ldeloff/aitools/AitoolsApplicationTests.java +++ /dev/null @@ -1,13 +0,0 @@ -package ru.ldeloff.aitools; - -import org.junit.jupiter.api.Test; -import org.springframework.boot.test.context.SpringBootTest; - -@SpringBootTest -class AitoolsApplicationTests { - - @Test - void contextLoads() { - } - -}