mirror of
				https://github.com/lingble/chatwoot.git
				synced 2025-10-31 11:08:04 +00:00 
			
		
		
		
	 42f6621afb
			
		
	
	42f6621afb
	
	
	
		
			
			Fixes https://github.com/chatwoot/chatwoot/issues/8436 Fixes https://github.com/chatwoot/chatwoot/issues/9767 Fixes https://github.com/chatwoot/chatwoot/issues/10156 Fixes https://github.com/chatwoot/chatwoot/issues/6031 Fixes https://github.com/chatwoot/chatwoot/issues/5696 Fixes https://github.com/chatwoot/chatwoot/issues/9250 Fixes https://github.com/chatwoot/chatwoot/issues/9762 --------- Co-authored-by: Pranav <pranavrajs@gmail.com> Co-authored-by: Sivin Varghese <64252451+iamsivin@users.noreply.github.com>
		
			
				
	
	
		
			204 lines
		
	
	
		
			5.6 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			204 lines
		
	
	
		
			5.6 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| import { computed, onMounted } from 'vue';
 | |
| import {
 | |
|   useStore,
 | |
|   useStoreGetters,
 | |
|   useMapGetter,
 | |
| } from 'dashboard/composables/store';
 | |
| import { useAlert, useTrack } from 'dashboard/composables';
 | |
| import { useI18n } from 'vue-i18n';
 | |
| import { OPEN_AI_EVENTS } from 'dashboard/helper/AnalyticsHelper/events';
 | |
| import OpenAPI from 'dashboard/api/integrations/openapi';
 | |
| 
 | |
| /**
 | |
|  * Cleans and normalizes a list of labels.
 | |
|  * @param {string} labels - A comma-separated string of labels.
 | |
|  * @returns {string[]} An array of cleaned and unique labels.
 | |
|  */
 | |
| const cleanLabels = labels => {
 | |
|   return labels
 | |
|     .toLowerCase() // Set it to lowercase
 | |
|     .split(',') // split the string into an array
 | |
|     .filter(label => label.trim()) // remove any empty strings
 | |
|     .map(label => label.trim()) // trim the words
 | |
|     .filter((label, index, self) => self.indexOf(label) === index);
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * A composable function for AI-related operations in the dashboard.
 | |
|  * @returns {Object} An object containing AI-related methods and computed properties.
 | |
|  */
 | |
| export function useAI() {
 | |
|   const store = useStore();
 | |
|   const getters = useStoreGetters();
 | |
|   const { t } = useI18n();
 | |
| 
 | |
|   /**
 | |
|    * Computed property for UI flags.
 | |
|    * @type {import('vue').ComputedRef<Object>}
 | |
|    */
 | |
|   const uiFlags = computed(() => getters['integrations/getUIFlags'].value);
 | |
| 
 | |
|   const appIntegrations = useMapGetter('integrations/getAppIntegrations');
 | |
|   const currentChat = useMapGetter('getSelectedChat');
 | |
|   const replyMode = useMapGetter('draftMessages/getReplyEditorMode');
 | |
| 
 | |
|   /**
 | |
|    * Computed property for the AI integration.
 | |
|    * @type {import('vue').ComputedRef<Object|undefined>}
 | |
|    */
 | |
|   const aiIntegration = computed(
 | |
|     () =>
 | |
|       appIntegrations.value.find(
 | |
|         integration => integration.id === 'openai' && !!integration.hooks.length
 | |
|       )?.hooks[0]
 | |
|   );
 | |
| 
 | |
|   /**
 | |
|    * Computed property to check if AI integration is enabled.
 | |
|    * @type {import('vue').ComputedRef<boolean>}
 | |
|    */
 | |
|   const isAIIntegrationEnabled = computed(() => !!aiIntegration.value);
 | |
| 
 | |
|   /**
 | |
|    * Computed property to check if label suggestion feature is enabled.
 | |
|    * @type {import('vue').ComputedRef<boolean>}
 | |
|    */
 | |
|   const isLabelSuggestionFeatureEnabled = computed(() => {
 | |
|     if (aiIntegration.value) {
 | |
|       const { settings = {} } = aiIntegration.value || {};
 | |
|       return settings.label_suggestion;
 | |
|     }
 | |
|     return false;
 | |
|   });
 | |
| 
 | |
|   /**
 | |
|    * Computed property to check if app integrations are being fetched.
 | |
|    * @type {import('vue').ComputedRef<boolean>}
 | |
|    */
 | |
|   const isFetchingAppIntegrations = computed(() => uiFlags.value.isFetching);
 | |
| 
 | |
|   /**
 | |
|    * Computed property for the hook ID.
 | |
|    * @type {import('vue').ComputedRef<string|undefined>}
 | |
|    */
 | |
|   const hookId = computed(() => aiIntegration.value?.id);
 | |
| 
 | |
|   /**
 | |
|    * Computed property for the conversation ID.
 | |
|    * @type {import('vue').ComputedRef<string|undefined>}
 | |
|    */
 | |
|   const conversationId = computed(() => currentChat.value?.id);
 | |
| 
 | |
|   /**
 | |
|    * Computed property for the draft key.
 | |
|    * @type {import('vue').ComputedRef<string>}
 | |
|    */
 | |
|   const draftKey = computed(
 | |
|     () => `draft-${conversationId.value}-${replyMode.value}`
 | |
|   );
 | |
| 
 | |
|   /**
 | |
|    * Computed property for the draft message.
 | |
|    * @type {import('vue').ComputedRef<string>}
 | |
|    */
 | |
|   const draftMessage = computed(() =>
 | |
|     getters['draftMessages/get'].value(draftKey.value)
 | |
|   );
 | |
| 
 | |
|   /**
 | |
|    * Fetches integrations if they haven't been loaded yet.
 | |
|    * @returns {Promise<void>}
 | |
|    */
 | |
|   const fetchIntegrationsIfRequired = async () => {
 | |
|     if (!appIntegrations.value.length) {
 | |
|       await store.dispatch('integrations/get');
 | |
|     }
 | |
|   };
 | |
| 
 | |
|   /**
 | |
|    * Records analytics for AI-related events.
 | |
|    * @param {string} type - The type of event.
 | |
|    * @param {Object} payload - Additional data for the event.
 | |
|    * @returns {Promise<void>}
 | |
|    */
 | |
|   const recordAnalytics = async (type, payload) => {
 | |
|     const event = OPEN_AI_EVENTS[type.toUpperCase()];
 | |
|     if (event) {
 | |
|       useTrack(event, {
 | |
|         type,
 | |
|         ...payload,
 | |
|       });
 | |
|     }
 | |
|   };
 | |
| 
 | |
|   /**
 | |
|    * Fetches label suggestions for the current conversation.
 | |
|    * @returns {Promise<string[]>} An array of suggested labels.
 | |
|    */
 | |
|   const fetchLabelSuggestions = async () => {
 | |
|     if (!conversationId.value) return [];
 | |
| 
 | |
|     try {
 | |
|       const result = await OpenAPI.processEvent({
 | |
|         type: 'label_suggestion',
 | |
|         hookId: hookId.value,
 | |
|         conversationId: conversationId.value,
 | |
|       });
 | |
| 
 | |
|       const {
 | |
|         data: { message: labels },
 | |
|       } = result;
 | |
| 
 | |
|       return cleanLabels(labels);
 | |
|     } catch {
 | |
|       return [];
 | |
|     }
 | |
|   };
 | |
| 
 | |
|   /**
 | |
|    * Processes an AI event, such as rephrasing content.
 | |
|    * @param {string} [type='rephrase'] - The type of AI event to process.
 | |
|    * @returns {Promise<string>} The generated message or an empty string if an error occurs.
 | |
|    */
 | |
|   const processEvent = async (type = 'rephrase') => {
 | |
|     try {
 | |
|       const result = await OpenAPI.processEvent({
 | |
|         hookId: hookId.value,
 | |
|         type,
 | |
|         content: draftMessage.value,
 | |
|         conversationId: conversationId.value,
 | |
|       });
 | |
|       const {
 | |
|         data: { message: generatedMessage },
 | |
|       } = result;
 | |
|       return generatedMessage;
 | |
|     } catch (error) {
 | |
|       const errorData = error.response.data.error;
 | |
|       const errorMessage =
 | |
|         errorData?.error?.message ||
 | |
|         t('INTEGRATION_SETTINGS.OPEN_AI.GENERATE_ERROR');
 | |
|       useAlert(errorMessage);
 | |
|       return '';
 | |
|     }
 | |
|   };
 | |
| 
 | |
|   onMounted(() => {
 | |
|     fetchIntegrationsIfRequired();
 | |
|   });
 | |
| 
 | |
|   return {
 | |
|     draftMessage,
 | |
|     uiFlags,
 | |
|     appIntegrations,
 | |
|     currentChat,
 | |
|     replyMode,
 | |
|     isAIIntegrationEnabled,
 | |
|     isLabelSuggestionFeatureEnabled,
 | |
|     isFetchingAppIntegrations,
 | |
|     fetchIntegrationsIfRequired,
 | |
|     recordAnalytics,
 | |
|     fetchLabelSuggestions,
 | |
|     processEvent,
 | |
|   };
 | |
| }
 |