From 3affcac7eb002494e204124651bb6c897769827f Mon Sep 17 00:00:00 2001 From: Herbert Vicente Cotta Julio Date: Thu, 3 Oct 2024 15:32:49 -0300 Subject: [PATCH] =?UTF-8?q?Revert=20"[UXE-4942]=20feat:=20add=20Azion=20Co?= =?UTF-8?q?pilot=20with=20deepchat=20to=20AI=20Chat=20component=E2=80=A6"?= =?UTF-8?q?=20(#1785)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 9c0c520c94f2d491284dde8ba5881ef513498d04. --- azion.config.cjs | 19 --- .../azion-ai-chat/azion-ai-chat-block.vue | 68 ++++------- .../azion-ai-chat/azion-ai-get-help.vue | 22 +--- .../contextual-prompts/domains/index.js | 43 +------ .../contextual-prompts/generic/index.js | 20 ---- .../azion-ai-chat/contextual-prompts/index.js | 3 +- src/modules/azion-ai-chat/index.vue | 50 +++----- .../services/interceptor-service.js | 57 --------- .../services/make-request-config.js | 3 +- .../services/request-interceptor-service.js | 23 ++++ .../tests/make-request-config.test.js | 2 +- .../tests/requestInterceptorService.test.js | 113 ++++++------------ src/templates/navbar-block/index.vue | 3 - vite.config.js | 5 - 14 files changed, 110 insertions(+), 321 deletions(-) delete mode 100644 src/modules/azion-ai-chat/contextual-prompts/generic/index.js delete mode 100644 src/modules/azion-ai-chat/services/interceptor-service.js create mode 100644 src/modules/azion-ai-chat/services/request-interceptor-service.js diff --git a/azion.config.cjs b/azion.config.cjs index 197b4922c..1016a155d 100644 --- a/azion.config.cjs +++ b/azion.config.cjs @@ -255,19 +255,6 @@ const backRules = [ }, rewrite: '/webhook/console_feedback' } - }, - { - name: 'Route Send Message to Copilot', - description: 'this router will send the user message to the chatbot', - match: '^/ai', - behavior: { - forwardCookies: true, - setOrigin: { - name: 'origin-console-ai', - type: 'single_origin' - }, - rewrite: '/chat-completions' - } } ] @@ -316,12 +303,6 @@ const AzionConfig = { hostHeader: `automate.azion.net`, addresses: [`automate.azion.net`] }, - { - name: 'origin-console-ai', - type: 'single_origin', - hostHeader: `ivg5gk272b.map.azionedge.net`, - addresses: [`ivg5gk272b.map.azionedge.net`] - } ], rules: { request: [...commonRules, ...frontRules, ...backRules], diff --git a/src/modules/azion-ai-chat/azion-ai-chat-block.vue b/src/modules/azion-ai-chat/azion-ai-chat-block.vue index 45e4b7ca6..93e2fd23f 100644 --- a/src/modules/azion-ai-chat/azion-ai-chat-block.vue +++ b/src/modules/azion-ai-chat/azion-ai-chat-block.vue @@ -9,8 +9,14 @@ :textInput="textInputStyles" :submitButtonStyles="submitButtonStyles" :request="makeRequestConfig()" - :requestInterceptor="interceptor" - :responseInterceptor="responseInterceptorService" + :requestInterceptor=" + (requestDetails) => + requestInterceptorService(requestDetails, { + sessionId: aiAskAzionSessionId, + url: currentRouteFullPath, + userName: account.name + }) + " >
@@ -40,10 +46,7 @@ import suggestionIconMetrics from './assets/suggestion-icon-metrics.svg?url' import suggestionIconSecurity from './assets/suggestion-icon-security.svg?url' import AzionAiChatSuggestion from './azion-ai-chat-suggestion.vue' - import { - requestInterceptorService, - responseInterceptorService - } from './services/interceptor-service' + import { requestInterceptorService } from './services/request-interceptor-service' import { loadPromptSuggestion } from './services/load-prompt-suggestions' import { makeRequestConfig } from './services/make-request-config' import { makeSessionId } from './services/make-session-id' @@ -52,6 +55,7 @@ import { useRouter } from 'vue-router' import { useAccountStore } from '@/stores/account' const { currentRoute } = useRouter() + const { account } = useAccountStore() defineOptions({ name: 'azion-ai-chat-block' @@ -59,12 +63,19 @@ onMounted(() => { generateChatSessionId() + getUserNameInfo() }) - const accountStore = ref(null) + const deepChatRef = ref(null) + defineExpose({ + deepChatRef + }) + + const currentRouteFullPath = currentRoute.value.path const aiAskAzionSessionId = ref('') - const promptOrigin = ref(null) + const accountName = ref('') + const errorMessages = ref({ overrides: { default: 'Connection failed. Try sending your message again.' @@ -287,55 +298,26 @@ const handleSubmitSuggestion = (promptText) => { deepChatRef.value.submitUserMessage({ text: promptText }) } - const generateChatSessionId = () => { aiAskAzionSessionId.value = makeSessionId() } - - const submitUserMessageGetHelp = ({ user, system }) => { - promptOrigin.value = { - user_prompt: user, - system_prompt: system - } - deepChatRef.value.submitUserMessage({ text: user }) + const getUserNameInfo = () => { + accountName.value = account.name } const calculateIconBySuggestionIndex = (index) => { return index === 0 ? suggestionIconMetrics : suggestionIconSecurity } - const interceptor = (requestDetails) => { - requestInterceptorService(requestDetails, { - sessionId: aiAskAzionSessionId.value, - url: currentRoute.value.path, - userName: accountStore.value.name, - clientId: accountStore.value.client_id, - prompt: promptOrigin.value, - allMessage: deepChatRef.value.getMessages() - }) - } - const loadPromptSuggestionWithRoleDecorator = (role) => { suggestionsOptions.value = loadPromptSuggestion(role) } watchEffect(() => { - const { account } = useAccountStore() - - accountStore.value = account - - loadPromptSuggestionWithRoleDecorator(accountStore.value.jobRole) - }) - - const clearMessages = () => { - deepChatRef.value.clearMessages() - promptOrigin.value = null - } + const { + account: { jobRole } + } = useAccountStore() - defineExpose({ - deepChatRef, - clearMessages, - submitUserMessageGetHelp + loadPromptSuggestionWithRoleDecorator(jobRole) }) -./services/interceptor-service diff --git a/src/modules/azion-ai-chat/azion-ai-get-help.vue b/src/modules/azion-ai-chat/azion-ai-get-help.vue index 51deb707d..7cc2b2e98 100644 --- a/src/modules/azion-ai-chat/azion-ai-get-help.vue +++ b/src/modules/azion-ai-chat/azion-ai-get-help.vue @@ -1,31 +1,11 @@ @@ -37,6 +17,6 @@ label="Get Help" icon="ai ai-ask-azion" icon-pos="right" - v-prompt="formatPrompt" + v-prompt="props.prompt" /> diff --git a/src/modules/azion-ai-chat/contextual-prompts/domains/index.js b/src/modules/azion-ai-chat/contextual-prompts/domains/index.js index 9f46fe328..b462a08dc 100644 --- a/src/modules/azion-ai-chat/contextual-prompts/domains/index.js +++ b/src/modules/azion-ai-chat/contextual-prompts/domains/index.js @@ -4,46 +4,13 @@ * @typedef {Object} DomainPrompts * @property {string} create.general - Prompt for providing detailed instructions for each field in the domain creation form. * @property {string} create.settings - Prompt for explaining the use of the CNAME field and the CNAME Access Only option, and describing the differences between the digital certificate options. - * @property {string} create.authentication - Prompt for explaining the use of the Mutual Authentication Settings. - * @property {string} edit.general - Prompt for providing detailed instructions for each field in the domain edition form. - * @property {string} edit.settings - Prompt for explaining the editing and managing of the CNAME field and the CNAME Access Only option, and describing the differences between the digital certificate options. - * @property {string} edit.authentication - Prompt for explaining the editing and managing of the Mutual Authentication Settings. - -*/ + */ export default { create: { - general: { - system: - 'You are a technical writer providing help to users on the Azion platform. Assist the user in creating and configuring a new domain to link to an edge application. Provide a detailed, step-by-step guide for setting up each field in the domain creation form, referencing Azion documentation and contextual, on-screen information. Organize the response into main steps or tasks, if possible. Include examples, best practices, and any relevant information to ensure proper configuration. Add related links and sources at the end of the response. Use technical terms where necessary, but prioritize plain, direct, and simple language. Write in American English, using a conversational tone with contractions, and address the user directly with "you".', - user: 'I want to create a new domain with Azion and link it to an edge application. I need a step-by-step guide on configuring my new domain, including detailed instructions for each field in the domain creation form.' - }, - settings: { - system: - 'You are a technical writer providing help to users on the Azion platform. Assist the user in understanding the purpose of CNAMEs when creating a new domain linked to an edge application, how to list them to associate with the Azion domain, and the differences between digital certificate options. Provide a detailed explanation to help the user choose the best option for their needs, referencing Azion documentation and contextual, on-screen information. Use technical terms where necessary, but prioritize plain, direct, and simple language. Write in American English, using a conversational tone with contractions, and address the user directly with "you".', - user: 'I am creating a new domain with Azion Console. While linking it to an edge application, I want to better understand CNAMEs, how to list them to associate with the Azion domain, and the differences between digital certificate options to choose the best one for my needs.' - }, - authentication: { - system: - 'You are a technical writer providing help to users on the Azion platform. Assist the user in understanding the Mutual Authentication Settings when creating a new domain. Explain how enabling it can benefit the user, providing a detailed guide that includes best practices and examples. Reference Azion documentation and contextual, on-screen information. Use technical terms where necessary, but prioritize plain, direct, and simple language. Write in American English, using a conversational tone with contractions, and address the user directly with "you".', - user: 'I am creating a new domain with Azion Console. While configuring the main settings, I want to learn more about Mutual Authentication Settings and how enabling it can benefit me.' - } - }, - edit: { - general: { - system: - 'You are a technical writer providing help to users on the Azion platform. Assist the user in editing and managing an existing domain. Provide a detailed, step-by-step guide for modifying each field in the domain editing form, referencing Azion documentation and contextual, on-screen information. Organize the response into main steps or tasks, if possible. Include examples, best practices, and any relevant information to ensure proper reconfiguration. Add related links and sources at the end of the response. Use technical terms where necessary, but prioritize plain, direct, and simple language. Write in American English, using a conversational tone with contractions, and address the user directly with "you".', - user: 'I want to edit one of my domains on Azion. Provide a step-by-step guide on how to edit the main settings in the domain editing form' - }, - settings: { - system: - 'You are a technical writer providing help to users on the Azion platform. Assist the user in editing and managing the CNAME field and the CNAME Access Only option for an existing domain. Provide an overview of the differences between digital certificate options and guide the user on how to edit these settings effectively to avoid issues later. Reference Azion documentation and contextual, on-screen information. Use technical terms where necessary, but prioritize plain, direct, and simple language. Write in American English, using a conversational tone with contractions, and address the user directly with "you".', - user: 'I am editing one of my domains on Azion and need an overview of CNAMEs, how to list them to associate with the Azion domain, and the differences between digital certificate options. Help me edit these settings in the best way to avoid issues later.' - }, - authentication: { - system: - 'You are a technical writer providing help to users on the Azion platform. Assist the user in managing the Mutual Authentication Settings for an existing domain. Provide guidance that includes best practices and examples, referencing Azion documentation and contextual, on-screen information. Use technical terms where necessary, but prioritize plain, direct, and simple language. Write in American English, using a conversational tone with contractions, and address the user directly with "you".', - user: 'I am editing the main settings for one of my domains on Azion and would like guidance on managing the Mutual Authentication Settings, including some best practices.' - } + general: + 'Please assist me in creating a domain. Provide detailed instructions for each field in the domain creation form.', + settings: + 'Please explain the use of the CNAME field and the CNAME Access Only option. Additionally, describe the differences between the digital certificate options.' } } diff --git a/src/modules/azion-ai-chat/contextual-prompts/generic/index.js b/src/modules/azion-ai-chat/contextual-prompts/generic/index.js deleted file mode 100644 index f63b146df..000000000 --- a/src/modules/azion-ai-chat/contextual-prompts/generic/index.js +++ /dev/null @@ -1,20 +0,0 @@ -/** - * Object containing contextual prompts for the domain creation form. - * - * @typedef {Object} GenericPrompts - * @property {string} generic.create - Generic prompt for providing detailed instructions on how to create and configure a new instance. - * @property {string} generic.edit - Generic prompt for providing detailed instructions on how to edit and manage a new instance. - */ - -export default { - create: { - system: - 'You are a technical writer providing help to users on Azion platform. Assist the user in creating and configuring a new ${instance} to run on the edge using Azion Console. Provide a detailed, step-by-step guide for setting up the main settings, referencing Azion documentation and contextual, on-screen information. Organize the response into main steps or tasks, if possible. Include examples, best practices, and any relevant information to ensure proper configuration. Add related links and sources at the end of the response. Use technical terms where necessary, but prioritize plain, direct, and simple language. Write in American English, using a conversational tone with contractions, and address the user directly with you.', - user: 'I want to configure a new ${instance} to run on the edge using Azion Console. Provide a detailed, step-by-step guide for setting up the main settings, including examples and best practices where applicable.' - }, - edit: { - system: - 'You are a technical writer providing help to users on Azion platform. Assist the user in editing settings and managing an existing ${instance} to run on the edge using Azion Console. Provide a detailed, step-by-step guide for modifying the main settings and other fields, referencing Azion documentation and contextual, on-screen information. Organize the response into main steps or tasks, if possible. Include examples, best practices, and any relevant information to ensure proper reconfiguration. Add related links and sources at the end of the response. Use technical terms where necessary, but prioritize plain, direct, and simple language. Write in American English, using a conversational tone with contractions, and address the user directly with you.', - user: 'I want to edit and manage an existing ${instance} to run on the edge using Azion Console. Provide a detailed, step-by-step guide for modifying the main settings and other fields, including examples and best practices where applicable.' - } -} diff --git a/src/modules/azion-ai-chat/contextual-prompts/index.js b/src/modules/azion-ai-chat/contextual-prompts/index.js index 1efe16bca..3e6ca27fd 100644 --- a/src/modules/azion-ai-chat/contextual-prompts/index.js +++ b/src/modules/azion-ai-chat/contextual-prompts/index.js @@ -1,4 +1,3 @@ import DomainsPrompts from './domains' -import Generic from './generic' -export { DomainsPrompts, Generic } +export { DomainsPrompts } diff --git a/src/modules/azion-ai-chat/index.vue b/src/modules/azion-ai-chat/index.vue index 7fc6ab5be..e562f14bb 100644 --- a/src/modules/azion-ai-chat/index.vue +++ b/src/modules/azion-ai-chat/index.vue @@ -33,15 +33,11 @@
- +

Copilot + { window.addEventListener('message', aiCustomPromptListenerHandler) addSupportToHljs() @@ -140,10 +132,6 @@ window.removeEventListener('message', aiCustomPromptListenerHandler) }) - const isChatMobile = computed(() => { - return currentWidth.value > SCREEN_BREAKPOINT_MD - }) - const azionAiChatStore = useAzionAiChatStore() const isChatAiOpen = computed(() => { return azionAiChatStore.isOpen @@ -160,34 +148,24 @@ } const handleClearChat = () => { - updateChatRenderInstance() - azionAiChatRef?.value?.clearMessages() - azionAiChatMobileRef?.value?.clearMessages() + azionAiChatRef?.value.deepChatRef.clearMessages() + azionAiChatMobileRef?.value.deepChatRef.clearMessages() updateSessionId() + updateChatRenderInstance() } const aiCustomPromptListenerHandler = (event) => { - handleClearChat() - setTimeout(() => { - if (event.data.type === AZION_MESSAGE_TYPE) { - azionAiChatStore.open() - azionAiChatRef?.value?.submitUserMessageGetHelp(event.data.prompt) - - azionAiChatMobileRef?.value?.submitUserMessageGetHelp(event.data.prompt) - } - }, 100) + if (event.data.type === AZION_MESSAGE_TYPE) { + azionAiChatStore.open() + azionAiChatRef?.value.deepChatRef.submitUserMessage({ text: event.data.prompt }) + setTimeout(() => { + azionAiChatMobileRef?.value.deepChatRef.submitUserMessage({ text: event.data.prompt }) + }, 100) + } } const openChatInNewTab = () => { const url = `${window.location.origin}${router.resolve({ name: 'copilot' }).path}` windowOpen(url, '_blank') } - - watch( - width, - () => { - currentWidth.value = width.value - }, - { immediate: true } - ) diff --git a/src/modules/azion-ai-chat/services/interceptor-service.js b/src/modules/azion-ai-chat/services/interceptor-service.js deleted file mode 100644 index f226e2caf..000000000 --- a/src/modules/azion-ai-chat/services/interceptor-service.js +++ /dev/null @@ -1,57 +0,0 @@ -/** - * Intercepts the request details and adds sessionId, url, userName, and app to the request body. - * - * @param {Object} requestDetails - The details of the request. - * @param {string} requestDetails.sessionId - The session ID. - * @param {string} requestDetails.url - The URL. - * @param {string} requestDetails.userName - The user name. - * @param {string} requestDetails.clientId - The client ID. - * @param {Array} requestDetails.allMessage - The array of messages. - * @param {string} requestDetails.prompt - The prompt that determines the origin of the chat call. - * @return {Object} The modified request details with added properties. - */ -export const requestInterceptorService = ( - requestDetails, - { sessionId, url, userName, clientId, allMessage, prompt } -) => { - const APP_PLATFORM_NAME = 'console' - - if (!requestDetails?.body) { - return - } - - const messages = allMessage.map(({ role, text }) => ({ - role: role === 'user' ? 'user' : 'system', - content: text - })) - - const azionAiChat = { - session_id: sessionId, - user_name: userName, - client_id: clientId, - url, - app: APP_PLATFORM_NAME, - ...(prompt && { - user_prompt: prompt.user_prompt, - system_prompt: prompt.system_prompt - }) - } - - requestDetails.body = { - ...requestDetails.body, - messages, - stream: true, - azion: azionAiChat - } - - return requestDetails -} - -export const responseInterceptorService = ({ choices }) => { - if (!choices?.length) { - return - } - return { - text: choices[0].delta?.content - } -} diff --git a/src/modules/azion-ai-chat/services/make-request-config.js b/src/modules/azion-ai-chat/services/make-request-config.js index 6c96beb7a..1d7266c0b 100644 --- a/src/modules/azion-ai-chat/services/make-request-config.js +++ b/src/modules/azion-ai-chat/services/make-request-config.js @@ -1,8 +1,9 @@ +import { makeAzionAiBaseUrl } from './make-azion-ai-base-url' /** * Generates a request configuration object for the Ask Azion API. */ export const makeRequestConfig = () => { return { - url: '/ai' + url: `${makeAzionAiBaseUrl()}/chat_stream` } } diff --git a/src/modules/azion-ai-chat/services/request-interceptor-service.js b/src/modules/azion-ai-chat/services/request-interceptor-service.js new file mode 100644 index 000000000..d2529e4e7 --- /dev/null +++ b/src/modules/azion-ai-chat/services/request-interceptor-service.js @@ -0,0 +1,23 @@ +/** + * Intercepts the request details and adds sessionId, url, userName, and app to the request body. + * + * @param {Object} requestDetails - The details of the request. + * @param {string} requestDetails.sessionId - The session ID. + * @param {string} requestDetails.url - The URL. + * @param {string} requestDetails.userName - The user name. + * @return {Object} The modified request details with added properties. + */ +export const requestInterceptorService = (requestDetails, { sessionId, url, userName }) => { + const APP_PLATFORM_NAME = 'console' + + if (!requestDetails || !requestDetails.body) { + return + } + + requestDetails.body['session_id'] = sessionId + requestDetails.body['url'] = url + requestDetails.body['username'] = userName + requestDetails.body['app'] = APP_PLATFORM_NAME + + return requestDetails +} diff --git a/src/modules/azion-ai-chat/tests/make-request-config.test.js b/src/modules/azion-ai-chat/tests/make-request-config.test.js index cb48af42c..0b38e916c 100644 --- a/src/modules/azion-ai-chat/tests/make-request-config.test.js +++ b/src/modules/azion-ai-chat/tests/make-request-config.test.js @@ -13,6 +13,6 @@ describe('makeRequestConfig', () => { const result = sut() - expect(result).toEqual({ url: '/ai' }) + expect(result).toEqual({ url: '/api/v4/ai/chat_stream' }) }) }) diff --git a/src/modules/azion-ai-chat/tests/requestInterceptorService.test.js b/src/modules/azion-ai-chat/tests/requestInterceptorService.test.js index 09441c4d2..b3991adc6 100644 --- a/src/modules/azion-ai-chat/tests/requestInterceptorService.test.js +++ b/src/modules/azion-ai-chat/tests/requestInterceptorService.test.js @@ -1,5 +1,5 @@ import { describe, expect, it } from 'vitest' -import { requestInterceptorService } from '../services/interceptor-service' +import { requestInterceptorService } from '../services/request-interceptor-service' const makeSut = () => { return { @@ -8,91 +8,54 @@ const makeSut = () => { } describe('requestInterceptorService', () => { - it('should return undefined if requestDetails.body is not provided', () => { + it('should add correct properties to the request', () => { const { sut } = makeSut() - const requestDetails = {} - - const result = sut(requestDetails, { - sessionId: '123', - url: '/test-url', - userName: 'testUser', - clientId: 'client123', - allMessage: [], - prompt: null + const requestDetailsMock = { + body: {} + } + const sessionIdMock = '123' + const urlMock = 'https://example.com' + const userNameMock = 'john' + + const result = sut(requestDetailsMock, { + sessionId: sessionIdMock, + url: urlMock, + userName: userNameMock }) - expect(result).toBeUndefined() + expect(result.body.session_id).toEqual(sessionIdMock) + expect(result.body.url).toEqual(urlMock) + expect(result.body.username).toEqual(userNameMock) + expect(result.body.app).toEqual('console') }) - it('should modify requestDetails.body with valid data', () => { + it('should not modify the request when its undefined', () => { const { sut } = makeSut() - const requestDetails = { body: {} } - - const result = sut(requestDetails, { - sessionId: '123', - url: '/test-url', - userName: 'testUser', - clientId: 'client123', - allMessage: [{ role: 'user', text: 'Hello' }], - prompt: { user_prompt: 'User prompt', system_prompt: 'System prompt' } - }) - - expect(result.body).toEqual({ - messages: [{ role: 'user', content: 'Hello' }], - stream: true, - azion: { - session_id: '123', - user_name: 'testUser', - client_id: 'client123', - url: '/test-url', - app: 'console', - user_prompt: 'User prompt', - system_prompt: 'System prompt' - } + const sessionIdMock = '123' + const urlMock = 'https://example.com' + const userNameMock = 'john' + + const result = sut(undefined, { + sessionId: sessionIdMock, + url: urlMock, + userName: userNameMock }) - }) - - it('should handle different roles in allMessage', () => { - const { sut } = makeSut() - const requestDetails = { body: {} } - const result = sut(requestDetails, { - sessionId: '123', - url: '/test-url', - userName: 'testUser', - clientId: 'client123', - allMessage: [ - { role: 'user', text: 'Hello' }, - { role: 'system', text: 'System message' } - ], - prompt: null - }) - - expect(result.body.messages).toEqual([ - { role: 'user', content: 'Hello' }, - { role: 'system', content: 'System message' } - ]) + expect(result).toBeUndefined() }) - it('should handle when prompt is not provided', () => { + it('should not modify the request when its empty', () => { const { sut } = makeSut() - const requestDetails = { body: {} } - - const result = sut(requestDetails, { - sessionId: '123', - url: '/test-url', - userName: 'testUser', - clientId: 'client123', - allMessage: [{ role: 'user', text: 'Hello' }], - prompt: null - }) - - expect(result.body.azion).toEqual({ - session_id: '123', - user_name: 'testUser', - client_id: 'client123', - url: '/test-url', - app: 'console' + const requestDetailsMock = {} + const sessionIdMock = '123' + const urlMock = 'https://example.com' + const userNameMock = 'john' + + const result = sut(requestDetailsMock, { + sessionId: sessionIdMock, + url: urlMock, + userName: userNameMock }) + expect(result).toBeUndefined() }) }) diff --git a/src/templates/navbar-block/index.vue b/src/templates/navbar-block/index.vue index 27b377d8c..a29f1dfa9 100644 --- a/src/templates/navbar-block/index.vue +++ b/src/templates/navbar-block/index.vue @@ -29,8 +29,6 @@
- - { changeOrigin: true, rewrite: (path) => path.replace(/^\/api/, '') }, - '/ai': { - target: 'https://ivg5gk272b.map.azionedge.net', - changeOrigin: true, - rewrite: (path) => path.replace(/^\/ai/, '/chat-completions') - }, '/webpagetest': { target: `https://www.azion.com/api/webpagetest`, changeOrigin: true,