Develop scenario/scenario executions chat

This commit is contained in:
2025-03-18 17:11:13 +01:00
parent 38b21032e7
commit 49b0863a60
6 changed files with 737 additions and 745 deletions

View File

@@ -1,107 +1,59 @@
<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 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>
</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="Send"
icon="pi pi-send"
class="p-button-primary"
@click="sendMessage"
/>
<p-button
label="Reset"
icon="pi pi-trash"
@click="clearHistory"
class=" p-button-danger"
/>
<p-button
icon="pi pi-cog"
@click="showSettings = !showSettings"
class=" p-button-normal"
/>
</p-inputGroup>
</div>
<p-inputGroup class="p-mt-2" style="width: 100%">
<p-inputText v-model="message" placeholder="Ask anything..." @keyup.enter="sendMessage" />
<!-- CARD DELLE IMPOSTAZIONI -->
<p-card v-if="showSettings" class="chat-settings-card p-p-2 p-mt-3">
<template #title>
<div class="p-d-flex p-ai-center">
<i class="pi pi-cog p-mr-2"></i>
<span>Chat Settings</span>
<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-danger" />
<!-- <p-button icon="pi pi-cog" @click="showSettings = !showSettings" class="p-button-normal" /> -->
</p-inputGroup>
</div>
</template>
<!-- Checkboxes e campi di testo -->
<template #content>
<p-inputGroup class="p-mt-2" style="width: 100%;">
<div class="p-field-checkbox p-mr-3">
<p-checkbox v-model="useDocumentation" inputId="documentation" binary="true" />
<label for="documentation">Documentation</label>
</div>
<div class="p-field-checkbox p-mr-3">
<p-checkbox v-model="useSource" inputId="source" binary="true" />
<label for="source">Source</label>
</div>
<p-inputText
v-model="scenarioExecutionId"
placeholder="Enter executionId..."
class="p-mr-2"
/>
<p-button
label="Load Context"
icon="pi pi-upload"
@click="loadContext"
class="p-button-outlined p-button-sm"
/>
<p-inputText disabled
v-model="conversationId"
placeholder="Enter conversation ID..."
class="p-mr-2"
/>
<p-inputText disabled
v-model="project"
placeholder="Project"
class="p-mr-2"
/>
<p-inputText disabled
v-model="application"
placeholder="Application"
class="p-mr-2"
/>
</p-inputGroup>
<!-- CARD DELLE IMPOSTAZIONI -->
<!-- <p-card v-if="showSettings" class="chat-settings-card p-p-2 p-mt-3">
<template #title>
<div class="p-d-flex p-ai-center">
<i class="pi pi-cog p-mr-2"></i>
<span>Chat Settings</span>
</div>
</template>
</template>
</p-card>
</div>
Checkboxes e campi di testo
<template #content>
<p-inputGroup class="p-mt-2" style="width: 100%">
<div class="p-field-checkbox p-mr-3">
<p-checkbox v-model="useDocumentation" inputId="documentation" binary="true" />
<label for="documentation">Documentation</label>
</div>
<div class="p-field-checkbox p-mr-3">
<p-checkbox v-model="useSource" inputId="source" binary="true" />
<label for="source">Source</label>
</div>
<p-inputText v-model="scenarioExecutionId" placeholder="Enter executionId..." class="p-mr-2" />
<p-button label="Load Context" icon="pi pi-upload" @click="loadContext" class="p-button-outlined p-button-sm" />
<p-inputText disabled v-model="conversationId" placeholder="Enter conversation ID..." class="p-mr-2" />
<p-inputText disabled v-model="project" placeholder="Project" class="p-mr-2" />
<p-inputText disabled v-model="application" placeholder="Application" class="p-mr-2" />
</p-inputGroup>
</template>
</p-card> -->
</div>
</template>
<script>
import { marked } from "marked";
import { marked } from 'marked';
import { MdPreview } from 'md-editor-v3';
import 'md-editor-v3/lib/style.css';
import Button from 'primevue/button';
@@ -115,277 +67,281 @@ import ScrollPanel from 'primevue/scrollpanel';
import { UserPrefStore } from '../stores/UserPrefStore.js';
const userPrefStore = UserPrefStore();
export default {
name: "ChatGPTInterface",
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
'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, // Default conversation ID
message: "",
messages: [],
useDocumentation: true,
useSource: true,
project: userPrefStore.user.selectedProject.internal_name,
application: userPrefStore.getSelApp ? userPrefStore.getSelApp.internal_name : "",
scenarioExecutionId:"",
showSettings: false,
authorization:"Bearer " + this.$auth.token(),
waitingData: false
};
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("mounted");
console.log("userPrefStore", userPrefStore);
this.fetchChatHistory();
console.log('userPrefStore', userPrefStore);
this.updateConversationId();
},
methods: {
async fetchChatHistory() {
if (!this.conversationId.trim()) {
console.warn("No conversation ID set.");
return;
}
try {
const response = await fetch(`http://olympus-api-gateway-aks.olympusai.live/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 => {
console.log("msg", msg);
if (msg.messageType != "SYSTEM") {
this.messages.push ({
sender : msg.messageType === "USER" ? "user" : 'bot',
text : msg.text
})
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();
}
});
console.log("messages", this.messages);
},
this.scrollToBottom();
} catch (error) {
console.error("Error loading chat history:", error);
}
},
async fetchChatHistory() {
if (!this.conversationId.trim()) {
console.warn('No conversation ID set.');
return;
}
try {
const response = await fetch(`http://olympus-api-gateway-aks.olympusai.live/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');
async sendMessage() {
if (this.message.trim() === "" || !this.conversationId.trim()) return;
const history = await response.json();
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
};
// Convert API format to frontend format
this.messages = [];
this.message = "";
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);
}
},
try {
this.waitingData = true;
const response = await fetch("http://olympus-api-gateway-aks.olympusai.live/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");
async sendMessage() {
if (this.message.trim() === '' || !this.conversationId.trim()) return;
const reader = response.body.getReader();
const decoder = new TextDecoder();
let buffer = "";
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
};
const processStream = async ({ done, value }) => {
if (done) return;
buffer += decoder.decode(value, { stream: true });
this.message = '';
const parts = buffer.split("\n");
buffer = parts.pop();
try {
this.waitingData = true;
const response = await fetch('http://olympus-api-gateway-aks.olympusai.live/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');
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);
}
}
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;
});
},
return reader.read().then(processStream);
};
resetChat() {
this.messages = []; // Clear chat messages
this.fetchChatHistory(); // Reload history for new conversation ID
},
await reader.read().then(processStream);
} catch (error) {
console.error("Error fetching response:", error);
botMessage.text += "\n[Error fetching response]";
async clearHistory() {
try {
const response = await fetch(`http://olympus-api-gateway-aks.olympusai.live/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(`http://olympus-api-gateway-aks.olympusai.live/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);
}
}
},
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(`http://olympus-api-gateway-aks.olympusai.live/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(`http://olympus-api-gateway-aks.olympusai.live/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';
};
</script>
.md-editor {
<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;
.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;
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;
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;
margin-bottom: 1rem;
display: flex;
align-items: flex-start;
}
/* Messaggi dell'utente a destra */
.chat-message.user {
justify-content: flex-end;
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;
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;
background: #e1e1e1;
color: #000;
}
/* Stile messaggi dell'utente */
.chat-message.user .message-bubble {
background: #6f3ff5; /* Sostituisci con il colore desiderato */
color: #fff;
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;
border: 1px solid #6f3ff5; /* Adatta al tuo tema */
color: #6f3ff5;
}
.p-button-outlined.p-button-danger {
border: 1px solid #f44336; /* Rosso */
color: #f44336;
border: 1px solid #f44336; /* Rosso */
color: #f44336;
}
</style>
</style>