Add SQLite persistence to restore and continue previous conversations

This commit is contained in:
Pablo de la Torre Jamardo 2025-06-29 12:03:11 +02:00
parent a150ce37f8
commit 73f3ade0f0
13 changed files with 197 additions and 45 deletions

4
.gitignore vendored
View File

@ -37,4 +37,6 @@ build/
*.gguf *.gguf
*.log *.log
*.db

17
pom.xml
View File

@ -42,6 +42,23 @@
<version>4.2.0</version> <version>4.2.0</version>
</dependency> </dependency>
<!-- Database -->
<dependency>
<groupId>org.xerial</groupId>
<artifactId>sqlite-jdbc</artifactId>
<version>3.45.1.0</version>
</dependency>
<dependency>
<groupId>org.hibernate.orm</groupId>
<artifactId>hibernate-community-dialects</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!-- Test support --> <!-- Test support -->
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>

View File

@ -1,10 +1,9 @@
package com.pablotj.ia.chat.boot.adapter.controller; package com.pablotj.ia.chat.boot.adapter.controller;
import com.pablotj.ia.chat.boot.application.session.ChatSessionManager;
import com.pablotj.ia.chat.boot.application.usecase.ChatUseCase; import com.pablotj.ia.chat.boot.application.usecase.ChatUseCase;
import com.pablotj.ia.chat.boot.domain.model.ChatMessage; import com.pablotj.ia.chat.boot.domain.model.ChatMessage;
import com.pablotj.ia.chat.boot.web.session.ChatSessionManager;
import jakarta.servlet.http.HttpSession; import jakarta.servlet.http.HttpSession;
import java.util.Date;
import java.util.List; import java.util.List;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
@ -32,7 +31,7 @@ public class ChatPageController {
List<ChatMessage> messages = chatSessionManager.getMessages(session); List<ChatMessage> messages = chatSessionManager.getMessages(session);
if (messages != null && messages.isEmpty()) { if (messages != null && messages.isEmpty()) {
try { try {
chatUseCase.processUserPrompt("", session); messages.add(chatUseCase.processUserPrompt("", session));
} catch (Exception e) { } catch (Exception e) {
LOGGER.error(e.getMessage(), e); LOGGER.error(e.getMessage(), e);
} }

View File

@ -0,0 +1,38 @@
package com.pablotj.ia.chat.boot.application.session;
import com.pablotj.ia.chat.boot.domain.model.ChatMessage;
import com.pablotj.ia.chat.boot.domain.port.ChatMessageStore;
import jakarta.servlet.http.HttpSession;
import java.util.ArrayList;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;
import java.util.List;
@Component
public class ChatSessionManager {
private final ChatMessageStore chatMessageStore;
public ChatSessionManager(ChatMessageStore chatMessageStore) {
this.chatMessageStore = chatMessageStore;
}
public List<ChatMessage> getMessages(HttpSession session) {
String sessionId = session.getId();
List<ChatMessage> messages = chatMessageStore.getMessages(sessionId);
List<ChatMessage> filteredMessages = new ArrayList<>(messages);
if (ObjectUtils.isEmpty(filteredMessages)) {
return new ArrayList<>();
} else {
filteredMessages.removeIf(m -> ObjectUtils.isEmpty(m.text()));
}
return filteredMessages;
}
public void setMessages(HttpSession session, List<ChatMessage> messages) {
messages.removeIf(m -> ObjectUtils.isEmpty(m.text()));
chatMessageStore.saveMessages(session.getId(), messages);
}
}

View File

@ -2,10 +2,9 @@ package com.pablotj.ia.chat.boot.application.usecase;
import com.pablotj.ia.chat.boot.application.prompt.PromptBuilder; import com.pablotj.ia.chat.boot.application.prompt.PromptBuilder;
import com.pablotj.ia.chat.boot.application.prompt.PromptTemplates; import com.pablotj.ia.chat.boot.application.prompt.PromptTemplates;
import com.pablotj.ia.chat.boot.application.session.ChatSessionManager;
import com.pablotj.ia.chat.boot.domain.model.ChatMessage; import com.pablotj.ia.chat.boot.domain.model.ChatMessage;
import com.pablotj.ia.chat.boot.domain.service.ChatService;
import com.pablotj.ia.chat.boot.infraestructure.llm.LlmModelClient; import com.pablotj.ia.chat.boot.infraestructure.llm.LlmModelClient;
import com.pablotj.ia.chat.boot.web.session.ChatSessionManager;
import jakarta.servlet.http.HttpSession; import jakarta.servlet.http.HttpSession;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;

View File

@ -0,0 +1,9 @@
package com.pablotj.ia.chat.boot.domain.port;
import com.pablotj.ia.chat.boot.domain.model.ChatMessage;
import java.util.List;
public interface ChatMessageStore {
List<ChatMessage> getMessages(String sessionId);
void saveMessages(String sessionId, List<ChatMessage> messages);
}

View File

@ -0,0 +1,44 @@
package com.pablotj.ia.chat.boot.persistence;
import jakarta.persistence.*;
import java.util.Date;
@Entity
@Table(name = "chat_messages")
public class ChatMessageEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String sessionId;
@Column(length = 4000)
private String text;
private String role;
private Date date;
// Getters y Setters
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public String getSessionId() { return sessionId; }
public void setSessionId(String sessionId) { this.sessionId = sessionId; }
public String getText() { return text; }
public void setText(String text) { this.text = text; }
public String getRole() { return role; }
public void setRole(String role) { this.role = role; }
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
}

View File

@ -0,0 +1,10 @@
package com.pablotj.ia.chat.boot.persistence;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;
public interface ChatMessageJpaRepository extends JpaRepository<ChatMessageEntity, Long> {
List<ChatMessageEntity> findBySessionIdOrderByIdAsc(String sessionId);
void deleteBySessionId(String sessionId);
}

View File

@ -0,0 +1,19 @@
package com.pablotj.ia.chat.boot.persistence;
import com.pablotj.ia.chat.boot.domain.model.ChatMessage;
public class ChatMessageMapper {
public static ChatMessageEntity toEntity(String sessionId, ChatMessage message) {
ChatMessageEntity entity = new ChatMessageEntity();
entity.setSessionId(sessionId);
entity.setRole(message.role());
entity.setText(message.text());
entity.setDate(message.date());
return entity;
}
public static ChatMessage toDomain(ChatMessageEntity entity) {
return new ChatMessage(entity.getRole(), entity.getText(), entity.getDate());
}
}

View File

@ -0,0 +1,35 @@
package com.pablotj.ia.chat.boot.persistence;
import com.pablotj.ia.chat.boot.domain.model.ChatMessage;
import com.pablotj.ia.chat.boot.domain.port.ChatMessageStore;
import org.springframework.stereotype.Repository;
import java.util.List;
import org.springframework.transaction.annotation.Transactional;
@Repository
public class SqliteChatMessageStore implements ChatMessageStore {
private final ChatMessageJpaRepository repository;
public SqliteChatMessageStore(ChatMessageJpaRepository repository) {
this.repository = repository;
}
@Override
public List<ChatMessage> getMessages(String sessionId) {
return repository.findBySessionIdOrderByIdAsc(sessionId)
.stream()
.map(ChatMessageMapper::toDomain)
.toList();
}
@Override
@Transactional
public void saveMessages(String sessionId, List<ChatMessage> messages) {
repository.deleteBySessionId(sessionId);
List<ChatMessageEntity> entities = messages.stream()
.map(m -> ChatMessageMapper.toEntity(sessionId, m))
.toList();
repository.saveAll(entities);
}
}

View File

@ -1,30 +0,0 @@
package com.pablotj.ia.chat.boot.web.session;
import com.pablotj.ia.chat.boot.domain.model.ChatMessage;
import jakarta.servlet.http.HttpSession;
import java.util.ArrayList;
import java.util.List;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;
@Component
public class ChatSessionManager {
private static final String ATTR_MESSAGES = "messages";
@SuppressWarnings("unchecked")
public List<ChatMessage> getMessages(HttpSession session) {
List<ChatMessage> messages = (List<ChatMessage>) session.getAttribute(ATTR_MESSAGES);
if (messages == null) {
messages = new ArrayList<>();
session.setAttribute(ATTR_MESSAGES, messages);
}
messages.removeIf(m -> ObjectUtils.isEmpty(m.text()));
return messages;
}
public void setMessages(HttpSession session, List<ChatMessage> messages) {
session.setAttribute(ATTR_MESSAGES, messages);
}
}

View File

@ -1,9 +0,0 @@
spring.application.name = ia-chat-boot
server.port = 8080
! Model
llama.model.name = openchat-3.5-0106.Q4_K_M
llama.model.gpu.enabled = true
llama.model.gpu.layers = 35
llama.model.tokens = 1024

View File

@ -0,0 +1,19 @@
server:
port: 8080
spring:
application:
name: ia-chat-boot
datasource:
url: jdbc:sqlite:file:chat.db
jpa:
database-platform: org.hibernate.community.dialect.SQLiteDialect
hibernate:
ddl-auto: update
llama:
model:
name: openchat-3.5-0106.Q4_K_M
gpu:
enabled: true
layers: 35
tokens: 1024