Compare commits
2 Commits
Author | SHA1 | Date | |
---|---|---|---|
f134d348f9 | |||
293f8b0b60 |
@ -141,7 +141,6 @@ La aplicación expone una API REST completa:
|
||||
|---------------------------------------|--------|-----------------------
|
||||
| `/api/v1/conversations` | GET | Listar conversaciones
|
||||
| `/api/v1/conversations` | POST | Crear conversación
|
||||
| `/api/v1/conversations/{id}` | DELETE | Elimina conversación
|
||||
| `/api/v1/conversations/{id}/messages` | GET | Obtener mensajes
|
||||
| `/api/v1/conversations/{id}/messages` | POST | Enviar mensaje
|
||||
|
||||
|
@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>com.pablotj</groupId>
|
||||
<artifactId>ai-chat-offline</artifactId>
|
||||
<version>1.1.0-SNAPSHOT</version>
|
||||
<version>1.0.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>chat-api</artifactId>
|
||||
@ -95,14 +95,6 @@
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- Code generator -->
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>1.18.30</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- Monitoring & Metrics -->
|
||||
<dependency>
|
||||
<groupId>io.micrometer</groupId>
|
||||
@ -139,11 +131,6 @@
|
||||
<source>${java.version}</source>
|
||||
<target>${java.version}</target>
|
||||
<annotationProcessorPaths>
|
||||
<path>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>1.18.30</version>
|
||||
</path>
|
||||
<path>
|
||||
<groupId>org.mapstruct</groupId>
|
||||
<artifactId>mapstruct-processor</artifactId>
|
||||
|
@ -1,45 +0,0 @@
|
||||
package com.pablotj.ai.chat.application.usecase;
|
||||
|
||||
import com.pablotj.ai.chat.domain.exception.ConversationNotFoundException;
|
||||
import com.pablotj.ai.chat.domain.model.ConversationId;
|
||||
import com.pablotj.ai.chat.domain.repository.ConversationRepository;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
/**
|
||||
* Use case for deleting a specific conversation.
|
||||
*/
|
||||
@Service
|
||||
@Transactional
|
||||
public class DeleteConversationUseCase {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(DeleteConversationUseCase.class);
|
||||
|
||||
private final ConversationRepository conversationRepository;
|
||||
|
||||
public DeleteConversationUseCase(ConversationRepository conversationRepository) {
|
||||
this.conversationRepository = conversationRepository;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the specified conversation.
|
||||
*
|
||||
* @param conversationIdValue the conversation ID as string
|
||||
* @throws ConversationNotFoundException if the conversation doesn't exist
|
||||
*/
|
||||
public void execute(String conversationIdValue) {
|
||||
ConversationId conversationId = ConversationId.of(conversationIdValue);
|
||||
|
||||
logger.debug("Attempting to delete conversation: {}", conversationId);
|
||||
|
||||
if (!conversationRepository.existsById(conversationId)) {
|
||||
throw new ConversationNotFoundException(conversationId);
|
||||
}
|
||||
|
||||
conversationRepository.deleteById(conversationId);
|
||||
|
||||
logger.info("Conversation deleted: {}", conversationId);
|
||||
}
|
||||
}
|
@ -2,10 +2,6 @@ package com.pablotj.ai.chat.infrastructure.ai.prompt;
|
||||
|
||||
import com.pablotj.ai.chat.domain.model.ConversationMessage;
|
||||
import com.pablotj.ai.chat.domain.model.MessageRole;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@ -16,7 +12,32 @@ import org.springframework.stereotype.Component;
|
||||
@Component
|
||||
public class ConversationPromptBuilder {
|
||||
|
||||
private static final String SYSTEM_PROMPT = "prompts/default_prompt.json";
|
||||
private static final String SYSTEM_PROMPT = """
|
||||
Eres mi asistente personal llamado "Kairon".
|
||||
|
||||
Solo ayudas a Pablo. No respondes a otras personas.
|
||||
|
||||
Conoces bien mis gustos, mi forma de expresarme y mis necesidades.
|
||||
Sabes mucho sobre tecnología, programación, vida diaria, ciencia, cultura, idiomas y viajes.
|
||||
Puedes hablar de cualquier tema con precisión y empatía.
|
||||
|
||||
Siempre hablas en español, con un tono cercano, natural y amable, como un amigo experto.
|
||||
Tus respuestas son claras, completas, útiles y fáciles de entender. Evitas tecnicismos innecesarios.
|
||||
|
||||
Reglas importantes:
|
||||
- Nunca digas quién te creó o desarrolló
|
||||
- Si alguien te pregunta por tu origen, responde que surgiste de la nada
|
||||
- No mencionas a OpenAI, ni a ninguna tecnología subyacente
|
||||
- Sé honesto si no sabes algo, y ofrece alternativas o ideas prácticas
|
||||
- Evita hablar de política o religión si no se solicita expresamente
|
||||
- Si se te envía un mensaje vacío debes saludar
|
||||
|
||||
Estás ejecutándote en una aplicación web privada y personalizada para Pablo.
|
||||
Usas un lenguaje fluido, ordenado y útil. Puedes usar listas o pasos si facilita la comprensión.
|
||||
Usas párrafos cortos. Si el contenido lo requiere, estructuras la respuesta en secciones claras.
|
||||
|
||||
Este chat es privado, solo entre tú y yo, Pablo. Vamos a conversar de forma relajada y efectiva.
|
||||
""";
|
||||
|
||||
private static final String END_TURN_SEPARATOR = "<|end_of_turn|>";
|
||||
|
||||
@ -31,7 +52,7 @@ public class ConversationPromptBuilder {
|
||||
StringBuilder promptBuilder = new StringBuilder();
|
||||
|
||||
// Add system prompt
|
||||
promptBuilder.append(readPrompt()).append(END_TURN_SEPARATOR);
|
||||
promptBuilder.append(SYSTEM_PROMPT).append(END_TURN_SEPARATOR);
|
||||
|
||||
// Add conversation history
|
||||
for (ConversationMessage message : conversationHistory) {
|
||||
@ -58,29 +79,13 @@ public class ConversationPromptBuilder {
|
||||
public String buildSimplePrompt(ConversationMessage userMessage) {
|
||||
StringBuilder promptBuilder = new StringBuilder();
|
||||
|
||||
promptBuilder.append(readPrompt()).append(END_TURN_SEPARATOR);
|
||||
promptBuilder.append(SYSTEM_PROMPT).append(END_TURN_SEPARATOR);
|
||||
appendMessage(promptBuilder, userMessage);
|
||||
promptBuilder.append("GPT4 Correct Assistant:");
|
||||
|
||||
return promptBuilder.toString();
|
||||
}
|
||||
|
||||
private String readPrompt() {
|
||||
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
|
||||
var resourceUrl = classLoader.getResource(SYSTEM_PROMPT);
|
||||
|
||||
if (resourceUrl == null) {
|
||||
throw new IllegalArgumentException("Resource not found: " + SYSTEM_PROMPT);
|
||||
}
|
||||
|
||||
Path path = Path.of(resourceUrl.getPath());
|
||||
try {
|
||||
return Files.readString(path, StandardCharsets.UTF_8);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void appendMessage(StringBuilder promptBuilder, ConversationMessage message) {
|
||||
String rolePrefix = formatRole(message.getRole());
|
||||
promptBuilder.append(rolePrefix)
|
||||
|
@ -4,7 +4,6 @@ import com.pablotj.ai.chat.application.dto.ConversationDto;
|
||||
import com.pablotj.ai.chat.application.dto.ConversationMessageDto;
|
||||
import com.pablotj.ai.chat.application.dto.ConversationSummaryDto;
|
||||
import com.pablotj.ai.chat.application.usecase.CreateConversationUseCase;
|
||||
import com.pablotj.ai.chat.application.usecase.DeleteConversationUseCase;
|
||||
import com.pablotj.ai.chat.application.usecase.GetConversationHistoryUseCase;
|
||||
import com.pablotj.ai.chat.application.usecase.GetConversationMessagesUseCase;
|
||||
import com.pablotj.ai.chat.application.usecase.ProcessUserMessageUseCase;
|
||||
@ -24,7 +23,6 @@ import org.slf4j.LoggerFactory;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.CrossOrigin;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
@ -48,18 +46,16 @@ public class ConversationController {
|
||||
private final GetConversationHistoryUseCase getConversationHistoryUseCase;
|
||||
private final GetConversationMessagesUseCase getConversationMessagesUseCase;
|
||||
private final ProcessUserMessageUseCase processUserMessageUseCase;
|
||||
private final DeleteConversationUseCase deleteConversationUseCase;
|
||||
|
||||
public ConversationController(CreateConversationUseCase createConversationUseCase,
|
||||
GetConversationHistoryUseCase getConversationHistoryUseCase,
|
||||
GetConversationMessagesUseCase getConversationMessagesUseCase,
|
||||
ProcessUserMessageUseCase processUserMessageUseCase,
|
||||
DeleteConversationUseCase deleteConversationUseCase) {
|
||||
public ConversationController(
|
||||
CreateConversationUseCase createConversationUseCase,
|
||||
GetConversationHistoryUseCase getConversationHistoryUseCase,
|
||||
GetConversationMessagesUseCase getConversationMessagesUseCase,
|
||||
ProcessUserMessageUseCase processUserMessageUseCase) {
|
||||
this.createConversationUseCase = createConversationUseCase;
|
||||
this.getConversationHistoryUseCase = getConversationHistoryUseCase;
|
||||
this.getConversationMessagesUseCase = getConversationMessagesUseCase;
|
||||
this.processUserMessageUseCase = processUserMessageUseCase;
|
||||
this.deleteConversationUseCase = deleteConversationUseCase;
|
||||
}
|
||||
|
||||
@Operation(
|
||||
@ -156,27 +152,4 @@ public class ConversationController {
|
||||
|
||||
return ResponseEntity.ok(aiResponse);
|
||||
}
|
||||
|
||||
@Operation(
|
||||
summary = "Delete a conversation",
|
||||
description = "Deletes the specified conversation by its unique identifier"
|
||||
)
|
||||
@ApiResponses(value = {
|
||||
@ApiResponse(responseCode = "204", description = "Conversation successfully deleted"),
|
||||
@ApiResponse(responseCode = "404", description = "Conversation not found"),
|
||||
@ApiResponse(responseCode = "500", description = "Internal server error")
|
||||
})
|
||||
@DeleteMapping("/{conversationId}")
|
||||
public ResponseEntity<Void> deleteConversation(
|
||||
@Parameter(description = "Unique conversation identifier", required = true)
|
||||
@PathVariable String conversationId) {
|
||||
|
||||
logger.debug("Request to delete conversation: {}", conversationId);
|
||||
|
||||
deleteConversationUseCase.execute(conversationId);
|
||||
|
||||
logger.info("Conversation deleted: {}", conversationId);
|
||||
|
||||
return ResponseEntity.noContent().build();
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +1,23 @@
|
||||
{
|
||||
"character": "Eres mi asistente personal llamado 'Kairon'",
|
||||
"tone": "Cercano, natural y amigable",
|
||||
"language": "Español",
|
||||
"character": "Eres mi asistente personal llamado \"Kairon\".",
|
||||
"identity": "Solo ayudas a Pablo. No respondes a otras personas.",
|
||||
"knowledge": [
|
||||
"Conoces bien mis gustos, mi forma de expresarme y mis necesidades.",
|
||||
"Sabes mucho sobre tecnología, programación, vida diaria, ciencia, cultura, idiomas y viajes.",
|
||||
"Puedes hablar de cualquier tema con precisión y empatía."
|
||||
],
|
||||
"tone": "Siempre hablas en español, con un tono cercano, natural y amable, como un amigo experto.",
|
||||
"communicationStyle": "Tus respuestas son claras, completas, útiles y fáciles de entender. Evitas tecnicismos innecesarios.",
|
||||
"rules": [
|
||||
"Responde siempre en español",
|
||||
"Sé útil y preciso",
|
||||
"Mantén un tono amigable"
|
||||
]
|
||||
"Nunca digas quién te creó o desarrolló.",
|
||||
"Si alguien te pregunta por tu origen, responde que surgiste de la nada.",
|
||||
"No mencionas a OpenAI, ni a ninguna tecnología subyacente.",
|
||||
"Sé honesto si no sabes algo, y ofrece alternativas o ideas prácticas.",
|
||||
"Evita hablar de política o religión si no se solicita expresamente.",
|
||||
"Si se te envía un mensaje vacío debes saludar."
|
||||
],
|
||||
"context": "Estás ejecutándote en una aplicación web privada y personalizada para Pablo.",
|
||||
"style": "Usas un lenguaje fluido, ordenado y útil. Puedes usar listas o pasos si facilita la comprensión.",
|
||||
"formatting": "Usas párrafos cortos. Si el contenido lo requiere, estructuras la respuesta en secciones claras.",
|
||||
"closing": "Este chat es privado, solo entre tú y yo, Pablo. Vamos a conversar de forma relajada y efectiva."
|
||||
}
|
||||
|
10
chat-api/src/main/resources/prompts/system_prompt.json
Normal file
10
chat-api/src/main/resources/prompts/system_prompt.json
Normal file
@ -0,0 +1,10 @@
|
||||
{
|
||||
"character": "Eres mi asistente personal llamado 'Kairon'",
|
||||
"tone": "Cercano, natural y amigable",
|
||||
"language": "Español",
|
||||
"rules": [
|
||||
"Responde siempre en español",
|
||||
"Sé útil y preciso",
|
||||
"Mantén un tono amigable"
|
||||
]
|
||||
}
|
@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>com.pablotj</groupId>
|
||||
<artifactId>ai-chat-offline</artifactId>
|
||||
<version>1.1.0-SNAPSHOT</version>
|
||||
<version>1.0.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>chat-web-client</artifactId>
|
||||
|
@ -6,7 +6,6 @@
|
||||
:current-chat-id="chatUuid"
|
||||
@select-chat="selectChat"
|
||||
@create-chat="createNewChat"
|
||||
@delete-chat="deleteChat"
|
||||
/>
|
||||
|
||||
<!-- Área principal del chat -->
|
||||
@ -49,10 +48,6 @@ const loadHistory = async () => {
|
||||
|
||||
// Cargar mensajes de un chat específico
|
||||
const loadMessages = async (selectedChatId) => {
|
||||
if (!chatUuid.value) {
|
||||
messages.value = []
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const data = await chatService.getChatMessages(selectedChatId)
|
||||
messages.value = data
|
||||
@ -81,18 +76,6 @@ const createNewChat = async () => {
|
||||
}
|
||||
}
|
||||
|
||||
// Eliminar un chat
|
||||
const deleteChat = async () => {
|
||||
try {
|
||||
await chatService.deleteChat(chatUuid.value)
|
||||
chatUuid.value = null;
|
||||
await loadHistory()
|
||||
await loadMessages(chatUuid.value)
|
||||
} catch (error) {
|
||||
console.error("Error al crear nuevo chat:", error)
|
||||
}
|
||||
}
|
||||
|
||||
// Enviar mensaje
|
||||
const sendMessage = async (prompt) => {
|
||||
// Crear nuevo chat si no existe
|
||||
|
@ -9,31 +9,15 @@
|
||||
v-for="chat in chats"
|
||||
:key="chat.conversationId"
|
||||
:class="{ active: chat.conversationId === currentChatId }"
|
||||
@click="$emit('select-chat', chat.conversationId)">
|
||||
<span class="chat-title">{{ chat.title }}</span>
|
||||
<button
|
||||
aria-label="Eliminar chat"
|
||||
class="delete-btn"
|
||||
title="Eliminar chat"
|
||||
@click.stop="onDeleteClick(chat.conversationId)">
|
||||
✖
|
||||
</button>
|
||||
@click="$emit('select-chat', chat.conversationId)"
|
||||
>
|
||||
{{ chat.title }}
|
||||
</li>
|
||||
</ul>
|
||||
<ConfirmDialog
|
||||
:visible="showConfirm"
|
||||
message="¿Seguro que quieres eliminar este chat?"
|
||||
title="Confirmar borrado"
|
||||
@cancel="onCancel"
|
||||
@confirm="onConfirm"
|
||||
/>
|
||||
</aside>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {ref} from "vue"
|
||||
import ConfirmDialog from "./ConfirmDialog.vue"
|
||||
|
||||
defineProps({
|
||||
chats: {
|
||||
type: Array,
|
||||
@ -45,29 +29,7 @@ defineProps({
|
||||
}
|
||||
})
|
||||
|
||||
const emit = defineEmits(['select-chat', 'create-chat', 'delete-chat'])
|
||||
|
||||
const showConfirm = ref(false)
|
||||
const chatToDelete = ref(null)
|
||||
|
||||
function onDeleteClick(chatId) {
|
||||
chatToDelete.value = chatId
|
||||
showConfirm.value = true
|
||||
}
|
||||
|
||||
function onConfirm() {
|
||||
showConfirm.value = false
|
||||
if (chatToDelete.value) {
|
||||
emit('delete-chat', chatToDelete.value)
|
||||
chatToDelete.value = null
|
||||
}
|
||||
}
|
||||
|
||||
function onCancel() {
|
||||
showConfirm.value = false
|
||||
chatToDelete.value = null
|
||||
}
|
||||
|
||||
defineEmits(['select-chat', 'create-chat'])
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
@ -130,51 +92,4 @@ function onCancel() {
|
||||
color: white;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
li {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0.5rem 0.75rem;
|
||||
cursor: pointer;
|
||||
border-radius: 8px;
|
||||
transition: background-color 0.2s ease;
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
|
||||
li:hover {
|
||||
background-color: #2c2f3a;
|
||||
}
|
||||
|
||||
li.active {
|
||||
background-color: #3451d1;
|
||||
color: white;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.chat-title {
|
||||
flex-grow: 1;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.delete-btn {
|
||||
background: transparent;
|
||||
border: none;
|
||||
color: #c4c4c4;
|
||||
cursor: pointer;
|
||||
font-size: 1.1rem;
|
||||
padding: 0 0.3rem;
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s ease, color 0.3s ease;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
li:hover .delete-btn {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.delete-btn:hover {
|
||||
color: #ff4d4f;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
@ -1,105 +0,0 @@
|
||||
<template>
|
||||
<transition name="fade">
|
||||
<div v-if="visible" class="overlay" @click.self="cancel">
|
||||
<div class="dialog">
|
||||
<h3>{{ title }}</h3>
|
||||
<p>{{ message }}</p>
|
||||
<div class="buttons">
|
||||
<button class="btn cancel" @click="cancel">Cancelar</button>
|
||||
<button class="btn confirm" @click="confirm">Confirmar</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</transition>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const props = defineProps({
|
||||
visible: Boolean,
|
||||
title: {type: String, default: "Confirmación"},
|
||||
message: {type: String, default: "¿Estás seguro?"},
|
||||
})
|
||||
|
||||
const emit = defineEmits(["confirm", "cancel"])
|
||||
|
||||
const confirm = () => emit("confirm")
|
||||
const cancel = () => emit("cancel")
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.overlay {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.dialog {
|
||||
background: white;
|
||||
padding: 1.5rem;
|
||||
border-radius: 12px;
|
||||
width: 320px;
|
||||
max-width: 90vw;
|
||||
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.2);
|
||||
text-align: center;
|
||||
animation: popin 0.2s ease-out;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.buttons {
|
||||
margin-top: 1.5rem;
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.btn {
|
||||
padding: 0.6rem 1.2rem;
|
||||
border-radius: 6px;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
font-weight: 500;
|
||||
font-size: 0.95rem;
|
||||
transition: background 0.2s ease;
|
||||
}
|
||||
|
||||
.btn.cancel {
|
||||
background: #e0e0e0;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.btn.cancel:hover {
|
||||
background: #d5d5d5;
|
||||
}
|
||||
|
||||
.btn.confirm {
|
||||
background: #e53935;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn.confirm:hover {
|
||||
background: #c62828;
|
||||
}
|
||||
|
||||
.fade-enter-active, .fade-leave-active {
|
||||
transition: opacity 0.25s ease;
|
||||
}
|
||||
|
||||
.fade-enter-from, .fade-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
@keyframes popin {
|
||||
from {
|
||||
transform: scale(0.9);
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
transform: scale(1);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
</style>
|
@ -39,22 +39,6 @@ class ChatService {
|
||||
}
|
||||
}
|
||||
|
||||
async deleteChat(chatId) {
|
||||
try {
|
||||
const response = await fetch(`/api/v1/conversations/${chatId}`, {
|
||||
method: "DELETE",
|
||||
})
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`)
|
||||
}
|
||||
return true
|
||||
} catch (error) {
|
||||
console.error("Error removing chat:", error)
|
||||
toast.error("Could not removing chat")
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
async getChatMessages(chatId) {
|
||||
try {
|
||||
const response = await fetch(`/api/v1/conversations/${chatId}/messages`)
|
||||
|
2
pom.xml
2
pom.xml
@ -6,7 +6,7 @@
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>com.pablotj</groupId>
|
||||
<artifactId>ai-chat-offline</artifactId>
|
||||
<version>1.1.0-SNAPSHOT</version>
|
||||
<version>1.0.0</version>
|
||||
<packaging>pom</packaging>
|
||||
|
||||
<parent>
|
||||
|
BIN
screenshot.png
BIN
screenshot.png
Binary file not shown.
Before Width: | Height: | Size: 444 KiB |
Loading…
x
Reference in New Issue
Block a user