diff --git a/DocService/sources/ai/aiProxyHandler.js b/DocService/sources/ai/aiProxyHandler.js index 539750ea..9bda5dd8 100644 --- a/DocService/sources/ai/aiProxyHandler.js +++ b/DocService/sources/ai/aiProxyHandler.js @@ -62,14 +62,16 @@ const nodeCache = new utils.NodeCache(cfgAiApiCache); */ function handleCorsHeaders(req, res, ctx, handleOptions = true) { const requestOrigin = req.headers.origin; + + const tenAiApiAllowedOrigins = ctx.getCfg('aiSettings.allowedCorsOrigins', cfgAiApiAllowedOrigins); // If no origin in request or allowed origins list is empty, do nothing - if (!requestOrigin || cfgAiApiAllowedOrigins.length === 0) { + if (!requestOrigin || tenAiApiAllowedOrigins.length === 0) { return false; } // If the origin is in our allowed list - if (cfgAiApiAllowedOrigins.includes(requestOrigin)) { + if (tenAiApiAllowedOrigins.includes(requestOrigin)) { res.setHeader('Access-Control-Allow-Origin', requestOrigin); res.setHeader('Access-Control-Allow-Credentials', 'true'); res.setHeader('Vary', 'Origin'); // Important when using dynamic origin @@ -113,7 +115,9 @@ async function proxyRequest(req, res) { try { ctx.logger.info('Start proxyRequest'); + await ctx.initTenantCache(); const tenTokenEnableBrowser = ctx.getCfg('services.CoAuthoring.token.enable.browser', cfgTokenEnableBrowser); + const tenAiApiTimeout = ctx.getCfg('aiSettings.timeout', cfgAiApiTimeout); const tenAiApi = ctx.getCfg('aiSettings', cfgAiSettings); // 1. Handle CORS preflight (OPTIONS) requests if necessary @@ -134,8 +138,8 @@ async function proxyRequest(req, res) { // Configure timeout options for the request const timeoutOptions = { - connectionAndInactivity: cfgAiApiTimeout, - wholeCycle: cfgAiApiTimeout + connectionAndInactivity: tenAiApiTimeout, + wholeCycle: tenAiApiTimeout }; // Get request size limit if configured diff --git a/branding/info/js/ai-integration.js b/branding/info/js/ai-integration.js index 74f78c69..1fc36071 100644 --- a/branding/info/js/ai-integration.js +++ b/branding/info/js/ai-integration.js @@ -134,6 +134,7 @@ const AIIntegration = { /** * Load the current view in the iframe + * @returns {void} No return value */ loadCurrentView() { if (this.isCollapsed) return; @@ -144,27 +145,34 @@ const AIIntegration = { if (!iframeSettings || !iframeEdit || !iframeList) return; - // Switch iframe visibility based on current view + // Hide all iframes first + [iframeSettings, iframeEdit, iframeList].forEach(iframe => { + iframe.classList.add('hidden'); + }); + + // Show loading overlay + const overlay = document.getElementById('ai-overlay'); + if (overlay) { + overlay.classList.add('loading'); + } + + // Always reassign src to force reload, then show the appropriate iframe switch (this.currentView) { case 'settings': + iframeSettings.src = 'ai/settings.html'; iframeSettings.classList.remove('hidden'); - iframeEdit.classList.add('hidden'); - iframeList.classList.add('hidden'); break; case 'aiModelEdit': - iframeSettings.classList.add('hidden'); + iframeEdit.src = 'ai/aiModelEdit.html'; iframeEdit.classList.remove('hidden'); - iframeList.classList.add('hidden'); break; case 'aiModelsList': - iframeSettings.classList.add('hidden'); - iframeEdit.classList.add('hidden'); + iframeList.src = 'ai/aiModelsList.html'; iframeList.classList.remove('hidden'); break; default: + iframeSettings.src = 'ai/settings.html'; iframeSettings.classList.remove('hidden'); - iframeEdit.classList.add('hidden'); - iframeList.classList.add('hidden'); } this.updateControls(); @@ -205,11 +213,11 @@ const AIIntegration = { /** * Handle iframe load event + * @returns {void} No return value */ onIframeLoad() { - this.loadedIframes++; const overlay = document.getElementById('ai-overlay'); - if (overlay && this.loadedIframes === this.totalIframes) { + if (overlay) { // Hide loading overlay after a short delay setTimeout(() => { overlay.classList.remove('loading'); diff --git a/branding/info/js/ai-interface.js b/branding/info/js/ai-interface.js index 77eaff59..4ae2a696 100644 --- a/branding/info/js/ai-interface.js +++ b/branding/info/js/ai-interface.js @@ -40,6 +40,8 @@ var urlSettings = 'plugin/settings'; var urlModels = 'plugin/models'; var urlConfig = 'config'; + + var tmpModel = null; // Initialize AI functionality when DOM is loaded document.addEventListener('DOMContentLoaded', function() { @@ -152,6 +154,20 @@ name: 'onThemeChanged', data: {type:'light', name: 'theme-light'} }, source); + const providers = Object.keys(settings.providers).map(function(key) { return settings.providers[key]; }); + var model = {id: "", name: "", provider: "", capabilities: 0}; + if (tmpModel) { + model = settings.models.find(function(model) { return model.name === tmpModel.name; }); + tmpModel = null; + } + var data = { + model : model, + providers : providers + } + sendMessageToSettings({ + name: 'onModelInfo', + data: data + }, source); } /** @@ -241,35 +257,8 @@ } break; case 'onOpenEditModal': + tmpModel = message.data.model; AIIntegration.navigateToView('aiModelEdit'); - var aiModelEditWindow = findIframeBySrcPart('aiModelEdit'); - if(aiModelEditWindow) { - const providers = Object.keys(settings.providers).map(function(key) { return settings.providers[key]; }); - var model = {id: "", name: "", provider: "", capabilities: 0}; - if (message.data.model) { - model = settings.models.find(function(model) { return model.name === message.data.model.name; }); - } - var data = { - model : model, - providers : providers - } - sendMessageToSettings({ - name: 'onProvidersUpdate', - data: data - }, aiModelEditWindow.contentWindow); - - - - sendMessageToSettings({ - name: 'onModelInfo', - data: data - }, aiModelEditWindow.contentWindow); - - // sendMessageToSettings({ - // name: 'onGetModels', - // data: {error: 1, models: []} - // }, aiModelEditWindow.contentWindow); - } break; case 'onDeleteAiModel': for (var i = 0; i < settings.models.length; i++) {