From b90459305e44b7c1a8fbd8d34fe2ef446e85ad8f Mon Sep 17 00:00:00 2001
From: PedroMiolaSilva <pedro.silva@azion.com>
Date: Wed, 19 Feb 2025 09:22:47 -0300
Subject: [PATCH 1/4] feat: adding basic auth + clerk

---
 .../vue/vue3-ai-chatbot-widget/src/App.vue    |   6 +-
 .../src/assets/auth-modal.css                 |  64 ++++++++++
 .../src/components/BasicAuthWindow.vue        |  65 ++++++++++
 .../src/components/layout-chat/index.vue      |  50 +++++++-
 .../src/composables/useAzionCopilot.js        |   8 +-
 .../src/core/azion-copilot.js                 |  31 ++++-
 .../src/core/constants.js                     |   3 +-
 .../vue/vue3-ai-chatbot-widget/src/main.js    |  46 +++++--
 .../src/services/auth.js                      | 116 ++++++++++++++++++
 9 files changed, 371 insertions(+), 18 deletions(-)
 create mode 100644 templates/vue/vue3-ai-chatbot-widget/src/assets/auth-modal.css
 create mode 100644 templates/vue/vue3-ai-chatbot-widget/src/components/BasicAuthWindow.vue
 create mode 100644 templates/vue/vue3-ai-chatbot-widget/src/services/auth.js

diff --git a/templates/vue/vue3-ai-chatbot-widget/src/App.vue b/templates/vue/vue3-ai-chatbot-widget/src/App.vue
index 7d96d2ee..2028c4a5 100644
--- a/templates/vue/vue3-ai-chatbot-widget/src/App.vue
+++ b/templates/vue/vue3-ai-chatbot-widget/src/App.vue
@@ -15,7 +15,8 @@
     isOpenByDefault: Boolean,
     isMaximizedByDefault: Boolean,
     previewText: String,
-    footerDisclaimer: String
+    footerDisclaimer: String,
+    authMode: String
   })
 
   const chatWidget = reactive({
@@ -27,7 +28,8 @@
     isOpenChat: props.isOpenByDefault,
     isMaximizedChat: props.isMaximizedByDefault,
     previewText: props.previewText,
-    footerDisclaimer: props.footerDisclaimer
+    footerDisclaimer: props.footerDisclaimer,
+    authMode: props.authMode
   })
 
   provide('chatWidget', chatWidget)
diff --git a/templates/vue/vue3-ai-chatbot-widget/src/assets/auth-modal.css b/templates/vue/vue3-ai-chatbot-widget/src/assets/auth-modal.css
new file mode 100644
index 00000000..8706473d
--- /dev/null
+++ b/templates/vue/vue3-ai-chatbot-widget/src/assets/auth-modal.css
@@ -0,0 +1,64 @@
+.auth-modal-overlay {
+  position: fixed;
+  top: 0;
+  left: 0;
+  width: 100%;
+  height: 100%;
+  background: rgba(0, 0, 0, 0.5);
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  z-index: 1000;
+}
+
+.auth-modal {
+  background: white;
+  padding: 2rem;
+  border-radius: 8px;
+  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
+}
+
+.auth-form {
+  display: flex;
+  flex-direction: column;
+  gap: 1rem;
+}
+
+input {
+  padding: 0.5rem;
+  border: 1px solid #ddd;
+  border-radius: 4px;
+}
+
+button {
+  padding: 0.5rem 1rem;
+  background: #f76707; /* Orange color */
+  color: white;
+  border: none;
+  border-radius: 4px;
+  cursor: pointer;
+  transition: background-color 0.2s;
+}
+
+button:hover {
+  background: #e85d04; /* Darker orange on hover */
+}
+
+button:disabled {
+  opacity: 0.7;
+  cursor: not-allowed;
+}
+
+.error-message {
+  color: #dc3545;
+  font-size: 0.875rem;
+  padding: 0.5rem;
+  background: #fde8e8;
+  border-radius: 4px;
+}
+
+.loading-message {
+  color: #666;
+  font-size: 0.875rem;
+  text-align: center;
+}
\ No newline at end of file
diff --git a/templates/vue/vue3-ai-chatbot-widget/src/components/BasicAuthWindow.vue b/templates/vue/vue3-ai-chatbot-widget/src/components/BasicAuthWindow.vue
new file mode 100644
index 00000000..9334ae78
--- /dev/null
+++ b/templates/vue/vue3-ai-chatbot-widget/src/components/BasicAuthWindow.vue
@@ -0,0 +1,65 @@
+<template>
+  <div class="auth-modal-overlay">
+    <div class="auth-modal">
+      <h2>Authentication Required</h2>
+      <form class="auth-form" @submit.prevent="handleSubmit">
+        <div v-if="error" class="error-message">
+          {{ error }}
+        </div>
+        <div v-if="loading" class="loading-message">
+          Loading...
+        </div>
+        <input 
+          type="password" 
+          v-model="password" 
+          placeholder="Enter password"
+          :disabled="loading"
+          autofocus
+        />
+        <button 
+          type="submit"
+          :disabled="loading"
+        >
+          {{ loading ? 'Signing in...' : 'Sign In' }}
+        </button>
+      </form>
+    </div>
+  </div>
+</template>
+
+<script setup>
+import { ref } from 'vue'
+
+const props = defineProps({
+  onSubmit: {
+    type: Function,
+    required: true
+  }
+})
+
+const password = ref('')
+const loading = ref(false)
+const error = ref('')
+
+const handleSubmit = async () => {
+  if (!password.value) {
+    error.value = 'Password is required'
+    return
+  }
+  
+  loading.value = true
+  error.value = ''
+  
+  try {
+    await props.onSubmit(password.value)
+  } catch (err) {
+    error.value = err.message || 'Invalid password'
+  } finally {
+    loading.value = false
+  }
+}
+</script>
+
+<style scoped>
+@import '../assets/auth-modal.css';
+</style> 
\ No newline at end of file
diff --git a/templates/vue/vue3-ai-chatbot-widget/src/components/layout-chat/index.vue b/templates/vue/vue3-ai-chatbot-widget/src/components/layout-chat/index.vue
index 4a883db8..eac5ff9f 100644
--- a/templates/vue/vue3-ai-chatbot-widget/src/components/layout-chat/index.vue
+++ b/templates/vue/vue3-ai-chatbot-widget/src/components/layout-chat/index.vue
@@ -9,8 +9,11 @@
   >
     <div
       v-if="chatWidget.isOpenChat || chatWidget.isClosing"
-      class="fixed right-0 z-[55] border surface-ground surface-border transition-transform ease-in-out max-md:w-full max-md:h-full max-md:top-0 max-md:right-0"
-      :class="chatClass"
+      :class="[
+        ...chatClass,
+        { 'pointer-events-none': showAuthOverlay },
+        'fixed right-0 z-[55] border surface-ground surface-border transition-transform ease-in-out max-md:w-full max-md:h-full max-md:top-0 max-md:right-0'
+      ]"
       @transitionend="onTransitionEnd"
     >
       <div class="h-full flex flex-col">
@@ -32,22 +35,61 @@
       </div>
     </div>
   </Transition>
+
+  <!-- Auth overlay - only show for basic auth -->
+  <Transition
+    enter-active-class="transition-all duration-300 ease-out"
+    enter-from-class="opacity-0"
+    enter-to-class="opacity-100"
+    leave-active-class="transition-all duration-300 ease-in"
+    leave-from-class="opacity-100"
+    leave-to-class="opacity-0"
+  >
+    <div v-if="showAuthOverlay && chatWidget.authMode === 'basic'" 
+         class="fixed inset-0 z-[60] bg-black/50 flex items-center justify-center">
+      <BasicAuthWindow :onSubmit="handleAuthSubmit" />
+    </div>
+  </Transition>
 </template>
 
 <script setup>
-  import { computed, inject } from 'vue'
+  import { computed, inject, ref } from 'vue'
   import ChatHeader from './chat-header.vue'
   import ChatBody from './chat-body.vue'
   import ChatFooter from './chat-footer.vue'
+  import BasicAuthWindow from '../BasicAuthWindow.vue'
   import { useAzionCopilot } from '../../composables/useAzionCopilot'
+  import { AuthService } from '../../services/auth'
+  import { CONSTANTS } from '../../core'
 
   const chatWidget = inject('chatWidget')
+  const showAuthOverlay = ref(false)
 
   defineOptions({ name: 'layout-chat' })
 
-  const { messages, sendMessage, cancelMessage, resetChat, isProcessingRequest, sendFeedback } =
+  const { messages, sendMessage, cancelMessage, resetChat, isProcessingRequest, sendFeedback, copilot } =
     useAzionCopilot({ server: chatWidget.serverUrl })
 
+  copilot.on(CONSTANTS.EVENTS.AUTH_REQUIRED, () => {
+    console.log(chatWidget)
+    if (chatWidget.authMode === 'basic') {
+      showAuthOverlay.value = true
+    }
+  })
+
+  const handleAuthSubmit = async (password) => {
+    const authService = new AuthService({
+      authMode: 'basic',
+      copilotBackend: chatWidget.serverUrl.url
+    })
+    
+    const result = await authService.fetchBasicAuth(password)
+    if (result.token) {
+      copilot.setAuthToken(result.token)
+      showAuthOverlay.value = false
+    }
+  }
+
   const closeChat = () => {
     chatWidget.isClosing = true
   }
diff --git a/templates/vue/vue3-ai-chatbot-widget/src/composables/useAzionCopilot.js b/templates/vue/vue3-ai-chatbot-widget/src/composables/useAzionCopilot.js
index fdb0fa41..f147e7ff 100644
--- a/templates/vue/vue3-ai-chatbot-widget/src/composables/useAzionCopilot.js
+++ b/templates/vue/vue3-ai-chatbot-widget/src/composables/useAzionCopilot.js
@@ -3,6 +3,11 @@ import { AzionCopilot, CONSTANTS } from '../core'
 
 export function useAzionCopilot(config) {
   const copilot = new AzionCopilot(config)
+  
+  const token = sessionStorage.getItem('copilot_auth_token')
+  if (token) {
+    copilot.setAuthToken(token)
+  }
 
   const messages = ref([])
   const isProcessingRequest = ref(false)
@@ -40,6 +45,7 @@ export function useAzionCopilot(config) {
     sendFeedback,
     resetChat,
     isProcessingRequest,
-    cancelMessage
+    cancelMessage,
+    copilot
   }
 }
diff --git a/templates/vue/vue3-ai-chatbot-widget/src/core/azion-copilot.js b/templates/vue/vue3-ai-chatbot-widget/src/core/azion-copilot.js
index 5b74b7e1..565e5aa9 100644
--- a/templates/vue/vue3-ai-chatbot-widget/src/core/azion-copilot.js
+++ b/templates/vue/vue3-ai-chatbot-widget/src/core/azion-copilot.js
@@ -6,6 +6,7 @@ export class AzionCopilot {
     this.messages = []
     this.sessionId = crypto.randomUUID()
     this.events = new EventEmitter()
+    this.authToken = null
 
     this.serverConfig = {
       ...CONSTANTS.SERVER.DEFAULT,
@@ -163,9 +164,18 @@ export class AzionCopilot {
 
     this.currentRequest = new AbortController()
     try {
+      const headers = { 
+        'Content-Type': 'application/json',
+      }
+
+      if (this.authToken) {
+        headers['Authorization'] = `Bearer ${this.authToken}`
+      }
+
       const response = await fetch(`${this.serverConfig.url}${this.serverConfig.conversation}`, {
         method: 'POST',
-        headers: { 'Content-Type': 'application/json' },
+        credentials: 'include',
+        headers,
         body: JSON.stringify({
           messages: messageQueue,
           stream: this.config.stream,
@@ -175,6 +185,10 @@ export class AzionCopilot {
       })
 
       if (!response.ok) {
+        if (response.status === 401) {
+          this.events.emit(CONSTANTS.EVENTS.AUTH_REQUIRED)
+          throw new Error('Authentication required')
+        }
         systemMessage.status = CONSTANTS.STATUS.MESSAGES.ERROR
         systemMessage.content = CONSTANTS.MESSAGES.SYSTEM.ERROR
 
@@ -258,9 +272,18 @@ export class AzionCopilot {
         comments
       }
 
+      const headers = { 
+        'Content-Type': 'application/json',
+      }
+
+      if (this.authToken) {
+        headers['Authorization'] = `Bearer ${this.authToken}`
+      }
+
       const response = await fetch(`${this.serverConfig.url}${this.serverConfig.feedback}`, {
         method: 'POST',
-        headers: { 'Content-Type': 'application/json' },
+        credentials: 'include',
+        headers,
         body: JSON.stringify(feedbackData)
       })
 
@@ -338,4 +361,8 @@ export class AzionCopilot {
   on(event, callback) {
     return this.events.on(event, callback)
   }
+
+  setAuthToken(token) {
+    this.authToken = token
+  }
 }
diff --git a/templates/vue/vue3-ai-chatbot-widget/src/core/constants.js b/templates/vue/vue3-ai-chatbot-widget/src/core/constants.js
index 51c08fc2..aa2fad25 100644
--- a/templates/vue/vue3-ai-chatbot-widget/src/core/constants.js
+++ b/templates/vue/vue3-ai-chatbot-widget/src/core/constants.js
@@ -33,6 +33,7 @@ export const CONSTANTS = {
     ERROR: 'error',
     CLEAR: 'clear',
     CANCEL: 'cancel',
-    FEEDBACK: 'feedback'
+    FEEDBACK: 'feedback',
+    AUTH_REQUIRED: 'auth_required'
   }
 }
diff --git a/templates/vue/vue3-ai-chatbot-widget/src/main.js b/templates/vue/vue3-ai-chatbot-widget/src/main.js
index 7346355d..f559772a 100644
--- a/templates/vue/vue3-ai-chatbot-widget/src/main.js
+++ b/templates/vue/vue3-ai-chatbot-widget/src/main.js
@@ -15,6 +15,7 @@ import PrimeVue from 'primevue/config'
 import Tooltip from 'primevue/tooltip'
 import ToastService from 'primevue/toastservice'
 import App from './App.vue'
+import { AuthService } from './services/auth'
 
 const getConfigDefaults = () => ({
   theme: import.meta.env.VITE_THEME || 'light',
@@ -33,19 +34,48 @@ const getConfigDefaults = () => ({
   isOpenByDefault: true,
   isMaximizedByDefault: true,
   previewText: import.meta.env.VITE_PREVIEW_TEXT || '',
-  footerDisclaimer: import.meta.env.VITE_FOOTER_DISCLAIMER || ''
+  footerDisclaimer: import.meta.env.VITE_FOOTER_DISCLAIMER || '',
+  clerkPublicKey: import.meta.env.VITE_CLERK_PUBLISHABLE_KEY,
+  authMode: import.meta.env.VITE_AUTH_MODE
 })
 
 const CONFIG_DEFAULT = getConfigDefaults()
 
-const app = createApp(App, CONFIG_DEFAULT)
-
 document.documentElement.className = `azion azion-${CONFIG_DEFAULT.theme}`
-
 document.title = CONFIG_DEFAULT.title || 'Copilot'
 
-app.use(PrimeVue)
-app.directive('tooltip', Tooltip)
-app.use(ToastService)
+async function init() {
+  const authService = new AuthService({
+    authMode: CONFIG_DEFAULT.authMode,
+    copilotBackend: CONFIG_DEFAULT.serverUrl.url,
+    clerkPublicKey: CONFIG_DEFAULT.clerkPublicKey
+  })
+
+  try {
+    const user = await authService.signIn()
+    if (user) {
+      mountMainApp()
+    }
+  } catch (error) {
+    console.error('Authentication error:', error)
+  }
+}
+
+function mountMainApp() {
+  const app = createApp(App, CONFIG_DEFAULT)
+  app.use(PrimeVue)
+  app.directive('tooltip', Tooltip)
+  app.use(ToastService)
+  app.mount('#app')
+}
+
+// Add this to handle the redirect
+if (window.location.hash.includes('__clerk_status=active')) {
+  console.log('Detected Clerk redirect, reloading...')
+  window.location.hash = ''
+  window.location.reload()
+} else {
+  init()
+}
+
 
-app.mount('#app')
diff --git a/templates/vue/vue3-ai-chatbot-widget/src/services/auth.js b/templates/vue/vue3-ai-chatbot-widget/src/services/auth.js
new file mode 100644
index 00000000..4a0e234d
--- /dev/null
+++ b/templates/vue/vue3-ai-chatbot-widget/src/services/auth.js
@@ -0,0 +1,116 @@
+import { Clerk } from '@clerk/clerk-js'
+import { createApp, defineAsyncComponent } from 'vue'
+
+export class AuthService {
+    constructor(args) {
+        this.authMode = ['clerk', 'basic', 'none'].includes(args.authMode) ? args.authMode : 'none'
+        this.copilotBackend = args.copilotBackend
+        this.clerkPublicKey = args.clerkPublicKey
+    }
+
+    async signIn(password = null) {
+        if (this.authMode === 'clerk') {
+            return this.clerkSignIn()
+        }
+
+        if (this.authMode === 'basic') {
+            if (sessionStorage.getItem('copilot_auth_token')) {
+                return { authenticated: true, token: sessionStorage.getItem('copilot_auth_token') }
+            }
+
+            if (password) {
+                return this.basicSignIn(password)
+            }
+            return this.basicSignIn()
+        }
+
+        return {}
+    }
+
+    async clerkSignIn() {
+        
+        const clerk = new Clerk(this.clerkPublicKey)
+        await clerk.load()
+        
+        if (!clerk.user) {
+          clerk.openSignIn({
+            appearance: {
+              socialButtonsVariant: "iconButton",
+              elements: {
+                card: {
+                  boxShadow: 'none',
+                },
+                modalCloseButton: {
+                  display: 'none'
+                },
+                footerAction: {
+                  display: 'none'
+                },
+              }
+            },
+          })
+        
+          await new Promise(resolve => {
+            clerk.addListener(({ user }) => {
+              if (user) resolve()
+            })
+          })
+        }
+        return clerk.user
+    }
+
+    async fetchBasicAuth(password) {
+        if (!password) {
+            throw new Error('Password required')
+        }
+
+        const response = await fetch(`${this.copilotBackend}/auth`, {
+            method: 'POST',
+            credentials: 'include',
+            headers: {
+                'Content-Type': 'application/json',
+                'Authorization': `Bearer ${password}`
+            },
+            body: JSON.stringify({
+                messages: [{
+                    "role": "user",
+                    "content": "Authenticate"
+                }]
+            })
+        })
+        const token = await response.text()
+
+        if (response.status !== 200 || !token) {
+            throw new Error('Authentication failed')
+        }
+        sessionStorage.setItem('copilot_auth_token', token)
+        return { authenticated: true, token: token}
+    }
+
+    async basicSignIn() {
+        sessionStorage.removeItem('copilot_auth_token')
+        return new Promise((resolve, reject) => {
+            const modalContainer = document.createElement('div')
+            document.body.appendChild(modalContainer)
+
+            const BasicAuthWindow = defineAsyncComponent(() =>
+                import('../components/BasicAuthWindow.vue')
+            )
+
+            const authApp = createApp(BasicAuthWindow, {
+                'onSubmit': async (password) => {
+                    try {
+                        const user = await this.fetchBasicAuth(password)
+                        authApp.unmount()
+                        document.body.removeChild(modalContainer)
+                        resolve(user)
+                    } catch (error) {
+                        throw error
+                    }
+                }
+            })
+
+            authApp.mount(modalContainer)
+        })
+    }
+}
\ No newline at end of file

From f4cff1b1de035d9d739ecdb36a18bfa7d88603cf Mon Sep 17 00:00:00 2001
From: PedroMiolaSilva <pedro.silva@azion.com>
Date: Wed, 19 Feb 2025 09:29:30 -0300
Subject: [PATCH 2/4] update env example

---
 templates/vue/vue3-ai-chatbot-widget/.env.example | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/templates/vue/vue3-ai-chatbot-widget/.env.example b/templates/vue/vue3-ai-chatbot-widget/.env.example
index 8bf453ca..593e0257 100644
--- a/templates/vue/vue3-ai-chatbot-widget/.env.example
+++ b/templates/vue/vue3-ai-chatbot-widget/.env.example
@@ -18,3 +18,10 @@ VITE_PREVIEW_TEXT=
 
 # VITE_FOOTER_DISCLAIMER: Footer disclaimer text
 VITE_FOOTER_DISCLAIMER=
+
+# VITE_AUTH_MODE: Authentication mode
+#   Options: basic or clerk. 
+VITE_AUTH_MODE=
+
+# VITE_CLERK_PUBLIC_KEY: Clerk public key
+VITE_CLERK_PUBLIC_KEY=

From 26693bd7278a87cd17386ce841cbe57acb8c403d Mon Sep 17 00:00:00 2001
From: PedroMiolaSilva <pedro.silva@azion.com>
Date: Wed, 19 Feb 2025 17:34:56 -0300
Subject: [PATCH 3/4] refactor: changing from system to assistant

---
 .../src/components/message-item.vue           |  2 +-
 .../src/core/azion-copilot.js                 | 38 +++++++++----------
 2 files changed, 20 insertions(+), 20 deletions(-)

diff --git a/templates/vue/vue3-ai-chatbot-widget/src/components/message-item.vue b/templates/vue/vue3-ai-chatbot-widget/src/components/message-item.vue
index 8a02b140..3b551bf2 100644
--- a/templates/vue/vue3-ai-chatbot-widget/src/components/message-item.vue
+++ b/templates/vue/vue3-ai-chatbot-widget/src/components/message-item.vue
@@ -149,7 +149,7 @@
     }
   })
 
-  const isSystem = computed(() => props.message.role === 'system')
+  const isSystem = computed(() => props.message.role === 'assistant')
   const messageReadingStatus = computed(
     () => props.message.status === CONSTANTS.STATUS.MESSAGES.RESPONDING
   )
diff --git a/templates/vue/vue3-ai-chatbot-widget/src/core/azion-copilot.js b/templates/vue/vue3-ai-chatbot-widget/src/core/azion-copilot.js
index 565e5aa9..934ebdf0 100644
--- a/templates/vue/vue3-ai-chatbot-widget/src/core/azion-copilot.js
+++ b/templates/vue/vue3-ai-chatbot-widget/src/core/azion-copilot.js
@@ -29,7 +29,7 @@ export class AzionCopilot {
       id: msg.id || crypto.randomUUID(),
       status: CONSTANTS.STATUS.MESSAGES.COMPLETED,
       feedback:
-        msg.role === 'system'
+        msg.role === 'assistant'
           ? (msg.feedback ?? { completed: false, rating: CONSTANTS.STATUS.FEEDBACK.NEUTRAL })
           : msg.feedback
     }))
@@ -54,9 +54,9 @@ export class AzionCopilot {
     }
   }
 
-  createSystemMessage() {
+  createAssistantMessage() {
     return {
-      role: 'system',
+      role: 'assistant',
       content: '',
       status: CONSTANTS.STATUS.MESSAGES.RESPONDING,
       feedback: {
@@ -156,10 +156,10 @@ export class AzionCopilot {
 
   async sendMessage(content) {
     const userMessage = this.createInitialMessage(content)
-    const systemMessage = this.createSystemMessage()
+    const assistantMessage = this.createAssistantMessage()
 
     const messageQueue = [...this.messages, userMessage]
-    this.messages = [...this.messages, userMessage, systemMessage]
+    this.messages = [...this.messages, userMessage, assistantMessage]
     this.emitMessagesUpdate()
 
     this.currentRequest = new AbortController()
@@ -189,21 +189,21 @@ export class AzionCopilot {
           this.events.emit(CONSTANTS.EVENTS.AUTH_REQUIRED)
           throw new Error('Authentication required')
         }
-        systemMessage.status = CONSTANTS.STATUS.MESSAGES.ERROR
-        systemMessage.content = CONSTANTS.MESSAGES.SYSTEM.ERROR
+        assistantMessage.status = CONSTANTS.STATUS.MESSAGES.ERROR
+        assistantMessage.content = CONSTANTS.MESSAGES.SYSTEM.ERROR
 
-        this.messages[this.messages.length - 1] = { ...systemMessage }
+        this.messages[this.messages.length - 1] = { ...assistantMessage }
 
         this.emitMessagesUpdate()
         throw new Error(`HTTP error! status: ${response.status}`)
       }
 
       if (this.config.stream) {
-        await this.handleStreamResponse(response, systemMessage)
+        await this.handleStreamResponse(response, assistantMessage)
       } else {
         const data = await response.json()
         const completedMessage = {
-          ...systemMessage,
+          ...assistantMessage,
           content: data.content,
           status: CONSTANTS.STATUS.MESSAGES.COMPLETED
         }
@@ -213,11 +213,11 @@ export class AzionCopilot {
         this.emitMessagesUpdate()
       }
 
-      return systemMessage
+      return assistantMessage
     } catch (error) {
       if (error.name !== 'AbortError') {
         const errorMessage = {
-          ...systemMessage,
+          ...assistantMessage,
           status: CONSTANTS.STATUS.MESSAGES.ERROR
         }
 
@@ -234,17 +234,17 @@ export class AzionCopilot {
     this.currentRequest?.abort()
     this.currentRequest = null
 
-    const lastSystemMessage = [...this.messages]
+    const lastAssistantMessage = [...this.messages]
       .reverse()
-      .find((m) => m.role === 'system' && m.status === CONSTANTS.STATUS.MESSAGES.RESPONDING)
+      .find((m) => m.role === 'assistant' && m.status === CONSTANTS.STATUS.MESSAGES.RESPONDING)
 
-    if (lastSystemMessage) {
-      lastSystemMessage.status = CONSTANTS.STATUS.MESSAGES.CANCELED
-      lastSystemMessage.content += '\n'
+    if (lastAssistantMessage) {
+      lastAssistantMessage.status = CONSTANTS.STATUS.MESSAGES.CANCELED
+      lastAssistantMessage.content += '\n'
 
-      const index = this.messages.findIndex((m) => m.id === lastSystemMessage.id)
+      const index = this.messages.findIndex((m) => m.id === lastAssistantMessage.id)
       if (index !== -1) {
-        this.messages[index] = { ...lastSystemMessage }
+        this.messages[index] = { ...lastAssistantMessage }
       }
 
       this.emitMessagesUpdate()

From fe8b3abca8a23c1ed885c6cb80c5a4509a342ca0 Mon Sep 17 00:00:00 2001
From: PedroMiolaSilva <pedro.silva@azion.com>
Date: Wed, 19 Feb 2025 17:47:32 -0300
Subject: [PATCH 4/4] refactor: changing from system to assistant

---
 .../src/components/message-item.vue                       | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/templates/vue/vue3-ai-chatbot-widget/src/components/message-item.vue b/templates/vue/vue3-ai-chatbot-widget/src/components/message-item.vue
index 3b551bf2..f5c08168 100644
--- a/templates/vue/vue3-ai-chatbot-widget/src/components/message-item.vue
+++ b/templates/vue/vue3-ai-chatbot-widget/src/components/message-item.vue
@@ -4,13 +4,13 @@
     :class="classRoleApply"
   >
     <div
-      v-if="isSystem"
+      v-if="isAssistant"
       class="flex gap-3 mt-1"
     >
       <Avatar />
     </div>
     <div class="message-content">
-      <div v-if="!isSystem">
+      <div v-if="!isAssistant">
         <div
           v-html="formattedMessage"
           class="formatted-content"
@@ -149,14 +149,14 @@
     }
   })
 
-  const isSystem = computed(() => props.message.role === 'assistant')
+  const isAssistant = computed(() => props.message.role === 'assistant')
   const messageReadingStatus = computed(
     () => props.message.status === CONSTANTS.STATUS.MESSAGES.RESPONDING
   )
 
   const classRole = {
     user: 'surface-300 ml-auto break-words w-fit rounded-lg h-fit px-4 py-3',
-    system: 'mr-auto w-full mt-3'
+    assistant: 'mr-auto w-full mt-3'
   }
 
   const classRoleApply = classRole[props.message.role]