Add OpenRouter bot
This commit is contained in:
@@ -9,6 +9,8 @@ import com.petshop.backend.security.AppPrincipal;
|
||||
import com.petshop.backend.security.JwtUtil;
|
||||
import com.petshop.backend.service.ChatRealtimeService;
|
||||
import com.petshop.backend.service.ChatService;
|
||||
import com.petshop.backend.service.OpenRouterAiService;
|
||||
import com.petshop.backend.entity.Conversation;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.messaging.handler.annotation.DestinationVariable;
|
||||
import org.springframework.messaging.handler.annotation.MessageExceptionHandler;
|
||||
@@ -27,6 +29,7 @@ import java.util.Map;
|
||||
public class ChatWebSocketController {
|
||||
|
||||
private final ChatService chatService;
|
||||
private final OpenRouterAiService openRouterAiService;
|
||||
private final ChatRealtimeService chatRealtimeService;
|
||||
private final UserRepository userRepository;
|
||||
private final JwtUtil jwtUtil;
|
||||
@@ -37,8 +40,10 @@ public class ChatWebSocketController {
|
||||
ChatRealtimeService chatRealtimeService,
|
||||
UserRepository userRepository,
|
||||
JwtUtil jwtUtil,
|
||||
WebSocketAuthChannelInterceptor webSocketAuthChannelInterceptor
|
||||
WebSocketAuthChannelInterceptor webSocketAuthChannelInterceptor,
|
||||
OpenRouterAiService openRouterAiService
|
||||
) {
|
||||
this.openRouterAiService = openRouterAiService;
|
||||
this.chatService = chatService;
|
||||
this.chatRealtimeService = chatRealtimeService;
|
||||
this.userRepository = userRepository;
|
||||
@@ -53,6 +58,13 @@ public class ChatWebSocketController {
|
||||
MessageResponse message = chatService.sendMessage(id, user.getId(), user.getRole(), request);
|
||||
chatRealtimeService.publishMessage(id, message);
|
||||
chatRealtimeService.publishConversationUpdate(id);
|
||||
|
||||
if (user.getRole() == User.Role.CUSTOMER) {
|
||||
Conversation conversation = chatService.getConversationEntity(id);
|
||||
if (conversation.getMode() == Conversation.ConversationMode.AUTOMATED) {
|
||||
openRouterAiService.generateAndSendReply(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@MessageExceptionHandler({IllegalArgumentException.class, RuntimeException.class})
|
||||
|
||||
@@ -89,6 +89,12 @@ public class ChatService {
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
|
||||
public Conversation getConversationEntity(Long id) {
|
||||
return conversationRepository.findById(id)
|
||||
.orElseThrow(() -> new ResourceNotFoundException("Conversation not found"));
|
||||
}
|
||||
|
||||
public ConversationResponse getConversation(Long conversationId, Long userId, User.Role role) {
|
||||
Conversation conversation = conversationRepository.findById(conversationId)
|
||||
.orElseThrow(() -> new ResourceNotFoundException("Conversation not found"));
|
||||
@@ -151,6 +157,22 @@ public class ChatService {
|
||||
return toMessageResponse(message);
|
||||
}
|
||||
|
||||
|
||||
@Transactional
|
||||
public MessageResponse saveBotMessage(Long conversationId, String content) {
|
||||
Conversation conversation = conversationRepository.findById(conversationId)
|
||||
.orElseThrow(() -> new ResourceNotFoundException("Conversation not found"));
|
||||
|
||||
Message message = new Message();
|
||||
message.setConversationId(conversationId);
|
||||
message.setSenderId(101L);
|
||||
message.setContent(content);
|
||||
message.setIsRead(false);
|
||||
message = messageRepository.save(message);
|
||||
|
||||
return toMessageResponse(message);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public MessageResponse sendMessageWithAttachment(Long conversationId, Long userId, User.Role role, MultipartFile file, String content) {
|
||||
Conversation conversation = conversationRepository.findById(conversationId)
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
package com.petshop.backend.service;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.petshop.backend.entity.Message;
|
||||
import com.petshop.backend.repository.MessageRepository;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
|
||||
import java.net.URI;
|
||||
import java.net.http.HttpClient;
|
||||
import java.net.http.HttpRequest;
|
||||
import java.net.http.HttpResponse;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Service
|
||||
public class OpenRouterAiService {
|
||||
|
||||
@Value("${openrouter.api-key}")
|
||||
private String apiKey;
|
||||
|
||||
@Value("${openrouter.model}")
|
||||
private String model;
|
||||
private final String openRouterUrl = "https://openrouter.ai/api/v1/chat/completions";
|
||||
|
||||
private final ChatService chatService;
|
||||
private final ChatRealtimeService chatRealtimeService;
|
||||
private final MessageRepository messageRepository;
|
||||
private final ObjectMapper objectMapper;
|
||||
private final HttpClient httpClient;
|
||||
|
||||
public OpenRouterAiService(ChatService chatService, ChatRealtimeService chatRealtimeService, MessageRepository messageRepository, ObjectMapper objectMapper) {
|
||||
this.chatService = chatService;
|
||||
this.chatRealtimeService = chatRealtimeService;
|
||||
this.messageRepository = messageRepository;
|
||||
this.objectMapper = objectMapper;
|
||||
this.httpClient = HttpClient.newHttpClient();
|
||||
}
|
||||
|
||||
public void generateAndSendReply(Long conversationId) {
|
||||
CompletableFuture.runAsync(() -> {
|
||||
try {
|
||||
List<Message> history = messageRepository.findByConversationIdOrderByTimestampAsc(conversationId);
|
||||
|
||||
List<Map<String, String>> messages = history.stream().map(msg -> {
|
||||
String role = (msg.getSenderId() != null && msg.getSenderId() == 101L) ? "assistant" : "user";
|
||||
return Map.of("role", role, "content", msg.getContent() != null ? msg.getContent() : "");
|
||||
}).collect(Collectors.toList());
|
||||
|
||||
messages.add(0, Map.of("role", "system", "content", "You are a helpful pet shop assistant. Provide concise and friendly answers. Do not output markdown, just plain text."));
|
||||
|
||||
Map<String, Object> requestBody = Map.of(
|
||||
"model", model,
|
||||
"messages", messages
|
||||
);
|
||||
|
||||
String requestJson = objectMapper.writeValueAsString(requestBody);
|
||||
|
||||
HttpRequest request = HttpRequest.newBuilder()
|
||||
.uri(URI.create(openRouterUrl))
|
||||
.header("Content-Type", "application/json")
|
||||
.header("Authorization", "Bearer " + apiKey)
|
||||
.POST(HttpRequest.BodyPublishers.ofString(requestJson))
|
||||
.build();
|
||||
|
||||
HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
|
||||
|
||||
if (response.statusCode() == 200) {
|
||||
Map<String, Object> responseMap = objectMapper.readValue(response.body(), Map.class);
|
||||
List<Map<String, Object>> choices = (List<Map<String, Object>>) responseMap.get("choices");
|
||||
if (choices != null && !choices.isEmpty()) {
|
||||
Map<String, Object> choice = choices.get(0);
|
||||
Map<String, String> message = (Map<String, String>) choice.get("message");
|
||||
String replyContent = message.get("content");
|
||||
|
||||
var messageResponse = chatService.saveBotMessage(conversationId, replyContent);
|
||||
chatRealtimeService.publishMessage(conversationId, messageResponse);
|
||||
chatRealtimeService.publishConversationUpdate(conversationId);
|
||||
}
|
||||
} else {
|
||||
System.err.println("OpenRouter API Error: " + response.statusCode() + " " + response.body());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user