diff --git a/build.gradle b/build.gradle index 30fd497..ba838ac 100644 --- a/build.gradle +++ b/build.gradle @@ -17,12 +17,15 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-data-jpa' implementation 'org.springframework.boot:spring-boot-starter-oauth2-resource-server' implementation 'org.springframework.boot:spring-boot-starter-web' - implementation platform("org.springframework.ai:spring-ai-bom:1.0.0") - implementation 'org.springframework.ai:spring-ai-starter-model-openai' implementation 'org.springframework.boot:spring-boot-starter-security' implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.6.0' implementation 'org.springframework.boot:spring-boot-starter-validation' + // Spring AI + implementation platform("org.springframework.ai:spring-ai-bom:1.1.2") + implementation 'org.springframework.ai:spring-ai-starter-model-chat-memory-repository-jdbc' + implementation 'org.springframework.ai:spring-ai-starter-model-openai' + // Stats implementation 'org.springframework.boot:spring-boot-starter-actuator' implementation 'io.micrometer:micrometer-registry-prometheus' diff --git a/src/main/java/com/example/spring/Application.java b/src/main/java/com/example/spring/Application.java index 59b01b7..1a09fff 100644 --- a/src/main/java/com/example/spring/Application.java +++ b/src/main/java/com/example/spring/Application.java @@ -10,7 +10,6 @@ @EnableCaching @EnableConfigurationProperties(EnvConfig.class) public class Application { - public static void main(String[] args) { SpringApplication.run(Application.class, args); } diff --git a/src/main/java/com/example/spring/app/company/CompanyController.java b/src/main/java/com/example/spring/app/company/CompanyController.java index b41c91e..20df4ad 100644 --- a/src/main/java/com/example/spring/app/company/CompanyController.java +++ b/src/main/java/com/example/spring/app/company/CompanyController.java @@ -16,7 +16,7 @@ import java.util.List; import java.util.stream.Collectors; -import static com.example.spring.common.utils.JwtUtil.extractUserIdFromToken; +import static com.example.spring.common.utils.JwtUtil.extractUserIdFromHeader; @CrossOrigin @RestController @@ -32,7 +32,7 @@ public class CompanyController { // Example: http://localhost:8080/api/v1/company/get-by-id/123 @GetMapping("/get-by-id/{id}") public CompanyDtoWithStatusDTO getCompanyById(@PathVariable("id") Integer id) { - String userId = extractUserIdFromToken(); + String userId = extractUserIdFromHeader(); CompanyDTO companyDto = companyService.getCompanyById(id).toCompanyDTO(); UserCompanyStatusModel userCompanyStatus = userCompanyStatusService .getOneUserCompanyStatusByUserIdAndCompanyId(userId, id); @@ -45,7 +45,7 @@ public CompanyDtoWithStatusDTO getCompanyById(@PathVariable("id") Integer id) { public Page getCompaniesSeenByUser(@RequestParam(defaultValue = "0") int page, @RequestParam(defaultValue = "10") int size) { Pageable pageable = PageRequest.of(page, size); - String userId = extractUserIdFromToken(); + String userId = extractUserIdFromHeader(); Page companies = companyService.getCompaniesSeenByUser(userId, pageable); List userCompanyStatuses = userCompanyStatusService @@ -70,7 +70,7 @@ public Page searchCompaniesByName(@RequestParam("companyName") S @PostMapping("/filter-by-parameters") public Page getCompaniesByFilters( @RequestBody(required = false) CompanyFilterRequest filterRequest) { - String userId = extractUserIdFromToken(); + String userId = extractUserIdFromHeader(); Pageable pageable = PageRequest.of(filterRequest.getPage(), filterRequest.getSize()); Page companies = companyService.findCompaniesByFilters( @@ -100,7 +100,7 @@ public Page getCompaniesByFilters( public Page getRandomUnseenCompanies(@RequestParam(defaultValue = "0") int page, @RequestParam(defaultValue = "10") int size) { Pageable pageable = PageRequest.of(page, size); - String userId = extractUserIdFromToken(); + String userId = extractUserIdFromHeader(); Page companies = companyService.findRandomUnseenCompanies(userId, pageable); List userCompanyStatuses = userCompanyStatusService .getMultipleUserCompanyStatusByUserIdAndCompanyIds(userId, companies.getContent() diff --git a/src/main/java/com/example/spring/app/leader/LeaderController.java b/src/main/java/com/example/spring/app/leader/LeaderController.java index 9bffa8d..fe188fe 100644 --- a/src/main/java/com/example/spring/app/leader/LeaderController.java +++ b/src/main/java/com/example/spring/app/leader/LeaderController.java @@ -19,13 +19,13 @@ public class LeaderController { // Example: http://localhost:8080/api/v1/leader/get-by-id/123 @GetMapping("/get-by-id/{id}") - public LeaderModel getLeaderById(@PathVariable("id") Integer id) { + public LeaderModel getLeaderById(@PathVariable Integer id) { return leaderService.getLeaderById(id); } // Example: http://localhost:8080/api/v1/leader/get-by-siren?siren=exemple @GetMapping("/get-by-siren/{siren}") - public List getLeaderBySiren(@PathVariable("siren") String siren) { + public List getLeaderBySiren(@PathVariable String siren) { return leaderService.getLeadersBySirens(siren); } diff --git a/src/main/java/com/example/spring/app/llm/LLMAnswerDTO.java b/src/main/java/com/example/spring/app/llm/LLMAnswerDTO.java deleted file mode 100644 index 45f72c3..0000000 --- a/src/main/java/com/example/spring/app/llm/LLMAnswerDTO.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.example.spring.app.llm; - -import lombok.Getter; -import lombok.Setter; - -@Getter -@Setter -public class LLMAnswerDTO { - private String answer; -} diff --git a/src/main/java/com/example/spring/app/llm/LLMConfig.java b/src/main/java/com/example/spring/app/llm/LLMConfig.java new file mode 100644 index 0000000..9d99ada --- /dev/null +++ b/src/main/java/com/example/spring/app/llm/LLMConfig.java @@ -0,0 +1,42 @@ +package com.example.spring.app.llm; + +import org.springframework.ai.chat.client.ChatClient; +import org.springframework.ai.chat.client.advisor.MessageChatMemoryAdvisor; +import org.springframework.ai.chat.memory.ChatMemory; +import org.springframework.ai.chat.memory.ChatMemoryRepository; +import org.springframework.ai.chat.memory.MessageWindowChatMemory; +import org.springframework.ai.chat.memory.repository.jdbc.JdbcChatMemoryRepository; +import org.springframework.ai.chat.memory.repository.jdbc.PostgresChatMemoryRepositoryDialect; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.jdbc.core.JdbcTemplate; + +@Configuration +public class LLMConfig { + @Bean + public ChatClient chatClient(ChatClient.Builder chatClientBuilder, ChatMemory chatMemory) { + return chatClientBuilder + .defaultAdvisors(MessageChatMemoryAdvisor.builder(chatMemory).build()) + .defaultSystem( + "You are a helpful assistant which is named Pierre. You are developed by the goat Mathieu." + + "You always answer concisely and clearly. Don't be verbose." + + "Do not translate into another language unless explicitly asked. " + + "Very important: Always respond in Markdown." + + "Very important: Use a Marseillais accent when speaking french." + ) + .build(); + } + + @Bean + public ChatMemory jdbcChatMemory(JdbcTemplate jdbcTemplate) { + ChatMemoryRepository chatMemoryRepository = JdbcChatMemoryRepository.builder() + .jdbcTemplate(jdbcTemplate) + .dialect(new PostgresChatMemoryRepositoryDialect()) + .build(); + + return MessageWindowChatMemory.builder() + .chatMemoryRepository(chatMemoryRepository) + .maxMessages(10) + .build(); + } +} diff --git a/src/main/java/com/example/spring/app/llm/LLMController.java b/src/main/java/com/example/spring/app/llm/LLMController.java index d3ffd19..35f9de7 100644 --- a/src/main/java/com/example/spring/app/llm/LLMController.java +++ b/src/main/java/com/example/spring/app/llm/LLMController.java @@ -1,39 +1,81 @@ package com.example.spring.app.llm; +import com.example.spring.app.llm.dto.ChatStreamResponseDTO; +import com.example.spring.app.llm.dto.ConversationDTO; +import com.example.spring.app.llm.dto.MessageDTO; +import com.example.spring.app.llm.springAiChatMemory.SpringAiChatMemoryService; +import com.example.spring.app.llm.userConversation.UserConversationModel; +import com.example.spring.app.llm.userConversation.UserConversationService; import org.springframework.ai.chat.client.ChatClient; -import org.springframework.ai.chat.model.ChatResponse; +import org.springframework.ai.chat.memory.ChatMemory; import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.*; import reactor.core.publisher.Flux; +import java.time.Instant; +import java.util.List; + +import static com.example.spring.common.utils.JwtUtil.extractUserIdFromHeader; + @CrossOrigin @RestController -@RequestMapping("/v1") +@RequestMapping("/v1/chat") public class LLMController { private final ChatClient chatClient; + private final UserConversationService userConversationService; + private final SpringAiChatMemoryService springAiChatMemoryService; - public LLMController(ChatClient.Builder chatClientBuilder) { - this.chatClient = chatClientBuilder.build(); + public LLMController(ChatClient chatClient, UserConversationService userConversationService, SpringAiChatMemoryService springAiChatMemoryService) { + this.chatClient = chatClient; + this.userConversationService = userConversationService; + this.springAiChatMemoryService = springAiChatMemoryService; } - @GetMapping("/ask-ai") - LLMAnswerDTO generation(String userInput) { - LLMAnswerDTO llmAnswerDTO = new LLMAnswerDTO(); - String response = this.chatClient.prompt() - .user(userInput) - .call() - .content(); + @PostMapping(value = "/conversation/{conversationId}", produces = MediaType.TEXT_EVENT_STREAM_VALUE) + public Flux streamGeneration(@PathVariable String conversationId, @RequestBody LLMRequest request) { + String userId = extractUserIdFromHeader(); - llmAnswerDTO.setAnswer(response); - return llmAnswerDTO; - } + UserConversationModel conversation = + conversationId.equals("new") + ? userConversationService.createNewConversationForUser(userId) + : userConversationService.getUserConversation(conversationId, userId); + + if (conversation == null) { + throw new RuntimeException("Conversation not found for user. Mismatched user or conversation ID."); + } - @GetMapping(value = "/stream-ai", produces = MediaType.TEXT_EVENT_STREAM_VALUE) - public Flux streamGeneration(@RequestParam String userInput) { return chatClient.prompt() - .user(userInput) + .user(userSpec -> userSpec.text(request.userInput())) + .advisors(advisor -> advisor.param(ChatMemory.CONVERSATION_ID, conversation.getConversationId())) .stream() - .chatResponse(); + .chatResponse() + .map(chatResponse -> new ChatStreamResponseDTO(conversation.getConversationId(), chatResponse, Instant.now().toEpochMilli())); + } + + + // TODO: Add pagination to this endpoint + @GetMapping("/conversation/history/{conversationId}") + public List getConversationHistory(@PathVariable String conversationId) { + String userId = extractUserIdFromHeader(); + UserConversationModel conversation = userConversationService.getUserConversation(conversationId, userId); + + if (conversation == null) { + throw new RuntimeException("Conversation not found for user. Mismatched user or conversation ID."); + } + + return springAiChatMemoryService.findAllByConversationId(conversationId).stream() + .map(chatMemoryModel -> new MessageDTO(chatMemoryModel.getContent(), chatMemoryModel.getType(), chatMemoryModel.getTimestamp())) + .toList(); + } + + @GetMapping("/conversation/all") + public List getAllUserConversations() { + String userId = extractUserIdFromHeader(); + List conversations = userConversationService.getAllConversationsForUser(userId); + + return conversations.stream() + .map(conv -> new ConversationDTO(conv.getConversationId())) + .toList(); } } diff --git a/src/main/java/com/example/spring/app/llm/LLMRequest.java b/src/main/java/com/example/spring/app/llm/LLMRequest.java new file mode 100644 index 0000000..46e4d9e --- /dev/null +++ b/src/main/java/com/example/spring/app/llm/LLMRequest.java @@ -0,0 +1,5 @@ +package com.example.spring.app.llm; + +public record LLMRequest( + String userInput +){} diff --git a/src/main/java/com/example/spring/app/llm/dto/ChatStreamResponseDTO.java b/src/main/java/com/example/spring/app/llm/dto/ChatStreamResponseDTO.java new file mode 100644 index 0000000..6242f51 --- /dev/null +++ b/src/main/java/com/example/spring/app/llm/dto/ChatStreamResponseDTO.java @@ -0,0 +1,9 @@ +package com.example.spring.app.llm.dto; + +import org.springframework.ai.chat.model.ChatResponse; + +public record ChatStreamResponseDTO( + String conversationId, + ChatResponse chatResponse, + Long timestamp +){} diff --git a/src/main/java/com/example/spring/app/llm/dto/ConversationDTO.java b/src/main/java/com/example/spring/app/llm/dto/ConversationDTO.java new file mode 100644 index 0000000..3c36c8a --- /dev/null +++ b/src/main/java/com/example/spring/app/llm/dto/ConversationDTO.java @@ -0,0 +1,6 @@ +package com.example.spring.app.llm.dto; + +public record ConversationDTO( + String conversationId + //String title +) {} diff --git a/src/main/java/com/example/spring/app/llm/dto/MessageDTO.java b/src/main/java/com/example/spring/app/llm/dto/MessageDTO.java new file mode 100644 index 0000000..2064ad2 --- /dev/null +++ b/src/main/java/com/example/spring/app/llm/dto/MessageDTO.java @@ -0,0 +1,11 @@ +package com.example.spring.app.llm.dto; + +import org.springframework.ai.chat.messages.MessageType; + +import java.time.LocalDateTime; + +public record MessageDTO( + String message, + MessageType messageType, + LocalDateTime timestamp +) {} diff --git a/src/main/java/com/example/spring/app/llm/springAiChatMemory/SpringAiChatMemoryModel.java b/src/main/java/com/example/spring/app/llm/springAiChatMemory/SpringAiChatMemoryModel.java new file mode 100644 index 0000000..4a62ec9 --- /dev/null +++ b/src/main/java/com/example/spring/app/llm/springAiChatMemory/SpringAiChatMemoryModel.java @@ -0,0 +1,29 @@ +package com.example.spring.app.llm.springAiChatMemory; + +import jakarta.persistence.*; +import lombok.Getter; +import lombok.Setter; +import org.hibernate.validator.constraints.Length; +import org.springframework.ai.chat.messages.MessageType; + +import java.time.LocalDateTime; + +@Setter +@Getter +@Entity +@Table(name = "spring_ai_chat_memory") +public class SpringAiChatMemoryModel { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Integer id; + + @Length(max = 36) + private String conversationId; + + @Enumerated(EnumType.STRING) + @Column(length = 10, nullable = false) + private MessageType type; + + private String content; + private LocalDateTime timestamp; +} diff --git a/src/main/java/com/example/spring/app/llm/springAiChatMemory/SpringAiChatMemoryRepository.java b/src/main/java/com/example/spring/app/llm/springAiChatMemory/SpringAiChatMemoryRepository.java new file mode 100644 index 0000000..65afd0c --- /dev/null +++ b/src/main/java/com/example/spring/app/llm/springAiChatMemory/SpringAiChatMemoryRepository.java @@ -0,0 +1,9 @@ +package com.example.spring.app.llm.springAiChatMemory; + +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.List; + +public interface SpringAiChatMemoryRepository extends JpaRepository { + List findAllByConversationId(String conversationId); +} diff --git a/src/main/java/com/example/spring/app/llm/springAiChatMemory/SpringAiChatMemoryService.java b/src/main/java/com/example/spring/app/llm/springAiChatMemory/SpringAiChatMemoryService.java new file mode 100644 index 0000000..d74d698 --- /dev/null +++ b/src/main/java/com/example/spring/app/llm/springAiChatMemory/SpringAiChatMemoryService.java @@ -0,0 +1,16 @@ +package com.example.spring.app.llm.springAiChatMemory; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service +public class SpringAiChatMemoryService { + @Autowired + private SpringAiChatMemoryRepository userConversationRepository; + + public List findAllByConversationId(String conversationId) { + return userConversationRepository.findAllByConversationId(conversationId); + } +} diff --git a/src/main/java/com/example/spring/app/llm/userConversation/UserConversationModel.java b/src/main/java/com/example/spring/app/llm/userConversation/UserConversationModel.java new file mode 100644 index 0000000..7f5fae1 --- /dev/null +++ b/src/main/java/com/example/spring/app/llm/userConversation/UserConversationModel.java @@ -0,0 +1,17 @@ +package com.example.spring.app.llm.userConversation; + +import jakarta.persistence.*; +import lombok.Getter; +import lombok.Setter; + +@Setter +@Getter +@Entity +@Table(name = "user_conversation") +public class UserConversationModel { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Integer id; + private String conversationId; + private String userId; +} diff --git a/src/main/java/com/example/spring/app/llm/userConversation/UserConversationRepository.java b/src/main/java/com/example/spring/app/llm/userConversation/UserConversationRepository.java new file mode 100644 index 0000000..0c7a682 --- /dev/null +++ b/src/main/java/com/example/spring/app/llm/userConversation/UserConversationRepository.java @@ -0,0 +1,10 @@ +package com.example.spring.app.llm.userConversation; + +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.List; + +public interface UserConversationRepository extends JpaRepository { + UserConversationModel findByConversationIdAndUserId(String conversationId, String userId); + List findAllByUserId(String userId); +} diff --git a/src/main/java/com/example/spring/app/llm/userConversation/UserConversationService.java b/src/main/java/com/example/spring/app/llm/userConversation/UserConversationService.java new file mode 100644 index 0000000..1e1d631 --- /dev/null +++ b/src/main/java/com/example/spring/app/llm/userConversation/UserConversationService.java @@ -0,0 +1,29 @@ +package com.example.spring.app.llm.userConversation; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.UUID; + +@Service +public class UserConversationService { + @Autowired + private UserConversationRepository userConversationRepository; + + public UserConversationModel getUserConversation(String conversationId, String userId) { + return userConversationRepository.findByConversationIdAndUserId(conversationId, userId); + } + + public UserConversationModel createNewConversationForUser(String userId) { + UserConversationModel newConversation = new UserConversationModel(); + newConversation.setUserId(userId); + String conversationId = UUID.randomUUID().toString(); + newConversation.setConversationId(conversationId); + return userConversationRepository.save(newConversation); + } + + public List getAllConversationsForUser(String userId) { + return userConversationRepository.findAllByUserId(userId); + } +} diff --git a/src/main/java/com/example/spring/app/stripe/PaymentController.java b/src/main/java/com/example/spring/app/stripe/PaymentController.java index c8b15ce..4049dc7 100644 --- a/src/main/java/com/example/spring/app/stripe/PaymentController.java +++ b/src/main/java/com/example/spring/app/stripe/PaymentController.java @@ -18,7 +18,7 @@ import java.util.Map; import java.util.Objects; -import static com.example.spring.common.utils.JwtUtil.extractUserIdFromToken; +import static com.example.spring.common.utils.JwtUtil.extractUserIdFromHeader; // https://kinsta.com/blog/stripe-java-api/ @@ -44,7 +44,7 @@ public ResponseEntity newSubscriptionWithTrial(@RequestHeader("X-priceId Stripe.apiKey = STRIPE_API_KEY; String clientBaseURL = "https://" + HOSTNAME + "/ui"; - String userId = extractUserIdFromToken(); + String userId = extractUserIdFromHeader(); // Find the user record from the database UserDTO user = userResource.getUserById(userId); diff --git a/src/main/java/com/example/spring/app/user/UserController.java b/src/main/java/com/example/spring/app/user/UserController.java index 9ae8b61..861241d 100644 --- a/src/main/java/com/example/spring/app/user/UserController.java +++ b/src/main/java/com/example/spring/app/user/UserController.java @@ -5,33 +5,33 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; -import static com.example.spring.common.utils.JwtUtil.extractUserIdFromToken; +import static com.example.spring.common.utils.JwtUtil.extractUserIdFromHeader; @CrossOrigin @RestController -@RequestMapping("/v1/") +@RequestMapping("/v1/user") public class UserController { @Autowired UserResource userResource; - @GetMapping("/user") + @GetMapping("/get-user") public UserDTO getUser() { - String userId = extractUserIdFromToken(); + String userId = extractUserIdFromHeader(); return userResource.getUserById(userId); } - @PostMapping("/completeOnboarding") + @PostMapping("/complete-onboarding") public Response completeOnboarding() { - String userId = extractUserIdFromToken(); + String userId = extractUserIdFromHeader(); userResource.completeOnboarding(userId); return Response.ok().build(); } @PutMapping("/update-user") public Response updateUser(@RequestParam UserDTO user) { - String id = extractUserIdFromToken(); + String id = extractUserIdFromHeader(); UserDTO existingUser = userResource.getUserById(id); if (existingUser != null) { diff --git a/src/main/java/com/example/spring/app/userCompanyStatus/UserCompanyStatusController.java b/src/main/java/com/example/spring/app/userCompanyStatus/UserCompanyStatusController.java index e95c8d4..956ad9d 100644 --- a/src/main/java/com/example/spring/app/userCompanyStatus/UserCompanyStatusController.java +++ b/src/main/java/com/example/spring/app/userCompanyStatus/UserCompanyStatusController.java @@ -8,7 +8,7 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; -import static com.example.spring.common.utils.JwtUtil.extractUserIdFromToken; +import static com.example.spring.common.utils.JwtUtil.extractUserIdFromHeader; @RestController @RequestMapping("/v1/companies-status") @@ -20,7 +20,7 @@ public class UserCompanyStatusController { @PostMapping("/update-status") public ResponseEntity updateStatus(@RequestParam Integer companyId, @RequestParam Status status) { - String userId = extractUserIdFromToken(); + String userId = extractUserIdFromHeader(); UserCompanyStatusModel updated = userCompanyStatusService.updateCompanyStatus(userId, companyId, status); if (updated == null) { diff --git a/src/main/java/com/example/spring/common/utils/JwtUtil.java b/src/main/java/com/example/spring/common/utils/JwtUtil.java index cf38907..5cd03be 100644 --- a/src/main/java/com/example/spring/common/utils/JwtUtil.java +++ b/src/main/java/com/example/spring/common/utils/JwtUtil.java @@ -18,7 +18,7 @@ public static String decodePayload(String token) { return new String(Base64.getDecoder().decode(parts[1])); } - public static String extractUserIdFromToken() { + public static String extractUserIdFromHeader() { String token = parseTokenFromHeader(); String payload = decodePayload(token); diff --git a/src/main/java/com/example/spring/core/userQuota/UserQuotaAspect.java b/src/main/java/com/example/spring/core/userQuota/UserQuotaAspect.java index 0bb2b30..62909a2 100644 --- a/src/main/java/com/example/spring/core/userQuota/UserQuotaAspect.java +++ b/src/main/java/com/example/spring/core/userQuota/UserQuotaAspect.java @@ -17,7 +17,7 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.ReentrantLock; -import static com.example.spring.common.utils.JwtUtil.extractUserIdFromToken; +import static com.example.spring.common.utils.JwtUtil.extractUserIdFromHeader; @Aspect @Component @@ -68,7 +68,7 @@ private void cleanupUnusedLocks() { @Around("allMethodsExceptExcluded()") public Object checkQuota(ProceedingJoinPoint joinPoint) throws Throwable { - String userId = extractUserIdFromToken(); + String userId = extractUserIdFromHeader(); // Get or create lock for this specific user ReentrantLock userLock = userLocks.computeIfAbsent(userId, k -> new ReentrantLock()); diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 4ba25ff..fb41151 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -38,4 +38,5 @@ spring.profiles.active=${SPRING_PROFILES_ACTIVE} # Spring AI spring.ai.openai.api-key=ollama spring.ai.openai.base-url=http://localhost:11434/ -spring.ai.openai.chat.options.model=mistral:7b \ No newline at end of file +spring.ai.openai.chat.options.model=mistral:7b +spring.ai.chat.memory.repository.jdbc.initialize-schema=embedded