320 lines
11 KiB
Vue
320 lines
11 KiB
Vue
<template>
|
|
<div class="chat-wrapper p-p-3">
|
|
<div class="p-d-flex p-flex-column" style="height: 100%">
|
|
<div class="chat-messages" ref="messagesContainer">
|
|
<div v-for="(msg, index) in messages" :key="index" :class="['chat-message', msg.sender]">
|
|
<div class="message-bubble">
|
|
<div v-if="msg.sender === 'bot'">
|
|
<MdPreview class="editor" theme="light" previewTheme="github" v-model="msg.text" language="en-US" :key="index" />
|
|
</div>
|
|
<p v-else>{{ msg.text }}</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<p-inputGroup class="p-mt-2" style="width: 100%">
|
|
<p-inputText v-model="message" placeholder="Ask anything..." @keyup.enter="sendMessage" />
|
|
|
|
<p-button label="Ask" icon="pi pi-send" class="p-button-primary" @click="sendMessage" />
|
|
<p-button label="Clear" icon="pi pi-trash" @click="clearHistory" class="p-button-warn" />
|
|
<!-- <p-button icon="pi pi-cog" @click="showSettings = !showSettings" class="p-button-normal" /> -->
|
|
</p-inputGroup>
|
|
</div>
|
|
|
|
|
|
</div>
|
|
</template>
|
|
|
|
<script>
|
|
import { marked } from 'marked';
|
|
import { MdPreview } from 'md-editor-v3';
|
|
import 'md-editor-v3/lib/style.css';
|
|
import Button from 'primevue/button';
|
|
import Card from 'primevue/card';
|
|
import Checkbox from 'primevue/checkbox';
|
|
import InputGroup from 'primevue/inputgroup';
|
|
import InputGroupAddon from 'primevue/inputgroupaddon';
|
|
import InputText from 'primevue/inputtext';
|
|
import ScrollPanel from 'primevue/scrollpanel';
|
|
|
|
import { UserPrefStore } from '../stores/UserPrefStore.js';
|
|
const userPrefStore = UserPrefStore();
|
|
|
|
export default {
|
|
name: 'ChatGPTInterface',
|
|
components: {
|
|
'p-scrollPanel': ScrollPanel,
|
|
'p-inputText': InputText,
|
|
'p-button': Button,
|
|
'p-checkbox': Checkbox,
|
|
'p-card': Card,
|
|
'p-inputGroup': InputGroup,
|
|
'p-inputGroupAddon': InputGroupAddon,
|
|
MdPreview
|
|
},
|
|
props: {
|
|
scenarioExecutionId: {
|
|
type: String,
|
|
default: ''
|
|
}
|
|
},
|
|
data() {
|
|
return {
|
|
conversationId: `${userPrefStore.user.id}-${userPrefStore.user.selectedProject.internal_name}`,
|
|
message: '',
|
|
messages: [],
|
|
useDocumentation: true,
|
|
useSource: true,
|
|
project: userPrefStore.user.selectedProject.internal_name,
|
|
application: userPrefStore.getSelApp ? userPrefStore.getSelApp.internal_name : '',
|
|
scenarioExecutionId: this.scenarioExecutionId,
|
|
showSettings: false,
|
|
authorization: 'Bearer ' + this.$auth.token(),
|
|
waitingData: false,
|
|
previousMessagesLength: 0
|
|
};
|
|
},
|
|
//
|
|
|
|
|
|
mounted() {
|
|
console.log('userPrefStore', userPrefStore);
|
|
this.updateConversationId();
|
|
},
|
|
|
|
methods: {
|
|
async updateConversationId() {
|
|
this.conversationId = this.scenarioExecutionId
|
|
? `${userPrefStore.user.id}-${this.scenarioExecutionId}`
|
|
: `${userPrefStore.user.id}-${userPrefStore.user.selectedProject.internal_name}`;
|
|
await this.fetchChatHistory();
|
|
if (this.scenarioExecutionId && this.messages.length === 0) {
|
|
this.loadContext();
|
|
}
|
|
},
|
|
|
|
async fetchChatHistory() {
|
|
if (!this.conversationId.trim()) {
|
|
console.warn('No conversation ID set.');
|
|
return;
|
|
}
|
|
try {
|
|
const response = await fetch(`${import.meta.env.VITE_BACKEND_URL.remove("/hermone")}/chatservice/get-history?conversationId=${this.conversationId}&lastN=100`, {
|
|
method: 'GET',
|
|
headers: { 'Content-Type': 'application/json', authorization: 'Bearer ' + this.$auth.token() }
|
|
});
|
|
if (!response.ok) throw new Error('Failed to fetch chat history');
|
|
|
|
const history = await response.json();
|
|
|
|
// Convert API format to frontend format
|
|
this.messages = [];
|
|
|
|
history.forEach((msg) => {
|
|
if (msg.messageType != 'SYSTEM') {
|
|
this.messages.push({
|
|
sender: msg.messageType === 'USER' ? 'user' : 'bot',
|
|
text: msg.text
|
|
});
|
|
}
|
|
});
|
|
this.scrollToBottom();
|
|
} catch (error) {
|
|
console.error('Error loading chat history:', error);
|
|
}
|
|
},
|
|
|
|
async sendMessage() {
|
|
if (this.message.trim() === '' || !this.conversationId.trim()) return;
|
|
|
|
this.messages.push({ sender: 'user', text: this.message });
|
|
const botMessage = { sender: 'bot', text: '' };
|
|
this.messages.push(botMessage);
|
|
this.scrollToBottom();
|
|
const payload = {
|
|
message: this.message,
|
|
conversationId: this.conversationId,
|
|
useDocumentation: this.useDocumentation,
|
|
useSource: this.useSource,
|
|
project: this.project,
|
|
application: this.application
|
|
};
|
|
|
|
this.message = '';
|
|
|
|
try {
|
|
this.waitingData = true;
|
|
const response = await fetch(import.meta.env.VITE_BACKEND_URL.remove("/hermone")+'/chatservice/chat', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json', authorization: this.authorization },
|
|
body: JSON.stringify(payload)
|
|
});
|
|
this.waitingData = false;
|
|
if (!response.body) throw new Error('Streaming not supported');
|
|
|
|
const reader = response.body.getReader();
|
|
const decoder = new TextDecoder();
|
|
let buffer = '';
|
|
|
|
const processStream = async ({ done, value }) => {
|
|
if (done) return;
|
|
buffer += decoder.decode(value, { stream: true });
|
|
|
|
const parts = buffer.split('\n');
|
|
buffer = parts.pop();
|
|
|
|
parts.forEach((line) => {
|
|
if (line.startsWith('data:')) {
|
|
try {
|
|
const jsonData = JSON.parse(line.slice(5).trim());
|
|
if (jsonData.result?.output?.text) {
|
|
botMessage.text += jsonData.result.output.text;
|
|
}
|
|
this.$forceUpdate();
|
|
this.scrollToBottom();
|
|
} catch (error) {
|
|
console.error('Error parsing JSON:', error);
|
|
}
|
|
}
|
|
});
|
|
|
|
return reader.read().then(processStream);
|
|
};
|
|
|
|
await reader.read().then(processStream);
|
|
} catch (error) {
|
|
console.error('Error fetching response:', error);
|
|
botMessage.text += '\n[Error fetching response]';
|
|
}
|
|
},
|
|
|
|
formatMessage(text) {
|
|
return marked.parse(text); // Converts Markdown to HTML
|
|
},
|
|
|
|
scrollToBottom() {
|
|
this.$nextTick(() => {
|
|
const container = this.$refs.messagesContainer;
|
|
container.scrollTop = container.scrollHeight;
|
|
});
|
|
},
|
|
|
|
resetChat() {
|
|
this.messages = []; // Clear chat messages
|
|
this.fetchChatHistory(); // Reload history for new conversation ID
|
|
},
|
|
|
|
async clearHistory() {
|
|
try {
|
|
const response = await fetch( `${import.meta.env.VITE_BACKEND_URL.remove("/hermone")}/chatservice/delete-history?conversationId=${this.conversationId}`, {
|
|
method: 'DELETE',
|
|
headers: { 'Content-Type': 'application/json', authorization: this.authorization }
|
|
});
|
|
if (!response.ok) {
|
|
throw new Error('Failed to clear chat history');
|
|
}
|
|
this.messages = [];
|
|
console.log('Chat history deleted successfully!');
|
|
} catch (error) {
|
|
console.error('Error clearing chat history:', error);
|
|
}
|
|
},
|
|
|
|
async loadContext() {
|
|
try {
|
|
const response = await fetch(`${import.meta.env.VITE_BACKEND_URL.remove("/hermone")}/chatservice/load-context-to-conversation?conversationId=${this.conversationId}&scenarioExecutionId=${this.scenarioExecutionId}`, {
|
|
method: 'GET',
|
|
headers: { 'Content-Type': 'application/json', authorization: this.authorization }
|
|
});
|
|
if (!response.ok) {
|
|
throw new Error('Failed to clear chat history');
|
|
}
|
|
this.messages = [];
|
|
this.resetChat();
|
|
console.log('Chat history deleted successfully!');
|
|
} catch (error) {
|
|
console.error('Error clearing chat history:', error);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
</script>
|
|
|
|
<style scoped>
|
|
@import url('https://fonts.googleapis.com/css2?family=Roboto:wght@400;500&display=swap');
|
|
@import 'md-editor-v3/lib/style.css';
|
|
|
|
.md-editor {
|
|
background-color: inherit;
|
|
}
|
|
|
|
.chat-wrapper {
|
|
width: 100%;
|
|
box-sizing: border-box;
|
|
}
|
|
|
|
.chat-card {
|
|
width: 100%;
|
|
margin: 0 auto;
|
|
height: 82vh;
|
|
}
|
|
|
|
/* Card delle impostazioni */
|
|
.chat-settings-card {
|
|
width: 100%;
|
|
margin: 0 auto;
|
|
}
|
|
|
|
/* Area messaggi */
|
|
.chat-messages {
|
|
background: #f4f4f4;
|
|
border-radius: 4px;
|
|
padding: 1rem;
|
|
overflow-y: auto;
|
|
max-height: 70vh;
|
|
min-height: 70vh;
|
|
}
|
|
|
|
/* Singolo messaggio */
|
|
.chat-message {
|
|
margin-bottom: 1rem;
|
|
display: flex;
|
|
align-items: flex-start;
|
|
}
|
|
|
|
/* Messaggi dell'utente a destra */
|
|
.chat-message.user {
|
|
justify-content: flex-end;
|
|
}
|
|
|
|
/* Bolla del messaggio */
|
|
.message-bubble {
|
|
max-width: 70%;
|
|
padding: 0.75rem;
|
|
border-radius: 15px;
|
|
font-size: 1.1rem;
|
|
line-height: 1.4;
|
|
}
|
|
|
|
/* Stile messaggi del bot */
|
|
.chat-message.bot .message-bubble {
|
|
background: #e1e1e1;
|
|
color: #000;
|
|
}
|
|
|
|
/* Stile messaggi dell'utente */
|
|
.chat-message.user .message-bubble {
|
|
background: #6f3ff5; /* Sostituisci con il colore desiderato */
|
|
color: #fff;
|
|
}
|
|
|
|
/* Esempio di pulsanti "outlined" personalizzati */
|
|
.p-button-outlined {
|
|
border: 1px solid #6f3ff5; /* Adatta al tuo tema */
|
|
color: #6f3ff5;
|
|
}
|
|
.p-button-outlined.p-button-danger {
|
|
border: 1px solid #f44336; /* Rosso */
|
|
color: #f44336;
|
|
}
|
|
</style>
|