[bug] Fix startup failure when scripts/engine/... is missing; Fix bug 75169

This commit is contained in:
Sergey Konovalov
2025-06-12 19:08:49 +03:00
parent 4ff2b1cb8f
commit 3efc6d1c36
3 changed files with 61 additions and 65 deletions

View File

@ -42,10 +42,10 @@ const vm = require('vm');
// Configuration constants
const cfgAiApiTimeout = config.get('aiSettings.timeout');
const cfgAiApiModels = config.get('aiSettings.models');
const cfgAiApiActions = config.get('aiSettings.actions');
const cfgAiPluginDir = config.get('aiSettings.pluginDir');
const engineScriptsDir = path.join(cfgAiPluginDir, 'scripts/engine');
function setCtx(ctx) {
sandbox.ctx = ctx;
console.log = ctx.logger.debug.bind(ctx.logger);//todo make default in logger
@ -55,14 +55,29 @@ function setCtx(ctx) {
// Set up the environment for the client-side engine.js
const sandbox = {
ctx: null,
window: {AI: {}},
window: {
AI: {
TmpProviderForModels: null,
Providers: {},
InternalProviders: [],
_getHeaders: function() {return {};},
serializeProviders: function() {return [];},
ActionsGetSorted: function() {return [];},
getModels: function() {return [];},
onLoadInternalProviders: function() {},
Storage: {
serializeModels: function() {return [];}
},
CapabilitiesUI: {}
}
},
Asc: {
plugin: {
tr: function(text) {
// Just return the original text in the stub
return text;
tr: function(text) {
// Just return the original text in the stub
return text;
}
}
}
},
/**
@ -89,7 +104,7 @@ const sandbox = {
options.headers || {},
options.body || null,
timeoutOptions,
10 * 1024 * 1024,
null,
false
)
.then(async (result) => {
@ -110,7 +125,7 @@ const sandbox = {
};
// Initialize minimal AI object with required functionality
const AI = sandbox.AI = sandbox.window.AI;
sandbox.AI = sandbox.window.AI;
setCtx(operationContext.global);
/**
@ -118,8 +133,15 @@ setCtx(operationContext.global);
*/
function loadInternalProviders() {
// Add simple provider loading logic
const enginePath = path.join(cfgAiPluginDir, 'scripts/engine/providers/internal');
const enginePath = path.join(engineScriptsDir, 'providers/internal');
// Check if the providers directory exists before trying to read it
if (!fs.existsSync(enginePath)) {
sandbox.ctx.logger.warn('Internal providers directory not found:', enginePath);
sandbox.AI.onLoadInternalProviders();
return;
}
try {
// Read providers directory
const files = fs.readdirSync(enginePath);
@ -150,27 +172,28 @@ function loadInternalProviders() {
sandbox.ctx.logger.error('Error loading internal providers:', error);
}
}
/**
* Simple loadInternalProviders implementation
*/
function fillConfigObjects() {
AI.Models = cfgAiApiModels;
for(let id in cfgAiApiActions) {
let action = cfgAiApiActions[id];
if (action.model && AI.Actions[id]) {
AI.Actions[id].model = action.model;
}
}
}
// Load engine.js
let engineCode = '';
engineCode += fs.readFileSync(path.join(cfgAiPluginDir, 'scripts/engine/storage.js'), 'utf8');
engineCode += fs.readFileSync(path.join(cfgAiPluginDir, 'scripts/engine/local_storage.js'), 'utf8');
engineCode += fs.readFileSync(path.join(cfgAiPluginDir, 'scripts/engine/providers/base.js'), 'utf8');
engineCode += fs.readFileSync(path.join(cfgAiPluginDir, 'scripts/engine/providers/provider.js'), 'utf8');
engineCode += fs.readFileSync(path.join(cfgAiPluginDir, 'scripts/engine/engine.js'), 'utf8');
vm.runInNewContext(engineCode, sandbox);
try {
engineCode += fs.readFileSync(path.join(engineScriptsDir, 'storage.js'), 'utf8');
engineCode += fs.readFileSync(path.join(engineScriptsDir, 'local_storage.js'), 'utf8');
engineCode += fs.readFileSync(path.join(engineScriptsDir, 'providers/base.js'), 'utf8');
engineCode += fs.readFileSync(path.join(engineScriptsDir, 'providers/provider.js'), 'utf8');
engineCode += fs.readFileSync(path.join(engineScriptsDir, 'engine.js'), 'utf8');
} catch (error) {
sandbox.ctx.logger.warn('Error reading engine script files:', error);
}
// Run engine code if available
if (engineCode) {
try {
vm.runInNewContext(engineCode, sandbox);
} catch (error) {
sandbox.ctx.logger.error('Error executing engine scripts:', error);
}
}
//start from engine/register.js
(function() {
@ -240,7 +263,6 @@ vm.runInNewContext(engineCode, sandbox);
sandbox.AI.loadInternalProviders = loadInternalProviders;
loadInternalProviders();
fillConfigObjects();
exports.setCtx = setCtx;
exports.AI = sandbox.AI;

View File

@ -33,7 +33,6 @@
'use strict';
const { pipeline } = require('stream/promises');
const { buffer } = require('node:stream/consumers');
const config = require('config');
const utils = require('./../../../Common/sources/utils');
const operationContext = require('./../../../Common/sources/operationContext');
@ -45,12 +44,10 @@ const aiEngine = require('./aiEngineWrapper');
const cfgAiApiAllowedOrigins = config.get('aiSettings.allowedCorsOrigins');
const cfgAiApiTimeout = config.get('aiSettings.timeout');
const cfgAiApiCache = config.get('aiSettings.cache');
const cfgTokenEnableBrowser = config.get('services.CoAuthoring.token.enable.browser');
const cfgAiSettings = config.get('aiSettings');
const AI = aiEngine.AI;
const nodeCache = new utils.NodeCache(cfgAiApiCache);
/**
* Helper function to set CORS headers if the request origin is allowed
*
@ -103,7 +100,7 @@ function handleCorsHeaders(req, res, ctx, handleOptions = true) {
/**
* Makes an HTTP request to an AI API endpoint using the provided request and response objects
*
*
* @param {object} req - Express request object
* @param {object} res - Express response object
* @returns {Promise<void>} - Promise resolving when the request is complete
@ -142,10 +139,6 @@ async function proxyRequest(req, res) {
wholeCycle: tenAiApiTimeout
};
// Get request size limit if configured
const sizeLimit = 10 * 1024 * 1024; // Default to 10MB
let providerHeaders;
// Determine which API key to use based on the target URL
if (body.target) {
@ -171,21 +164,13 @@ async function proxyRequest(req, res) {
headers,
body: body.data,
timeout: timeoutOptions,
limit: sizeLimit,
limit: null,
filterPrivate: false
};
// Create a safe copy for logging without sensitive info
const safeLogParams = { ...requestParams };
// if (safeLogParams.headers) {
// safeLogParams.headers = { ...safeLogParams.headers };
// if (safeLogParams.headers.Authorization) {
// safeLogParams.headers.Authorization = '[REDACTED]';
// }
// }
// Log the sanitized request parameters
ctx.logger.debug(`Proxying request: %j`, safeLogParams);
ctx.logger.debug(`Proxying request: %j`, requestParams);
// Use utils.httpRequest to make the request
const result = await utils.httpRequest(
@ -221,9 +206,9 @@ async function proxyRequest(req, res) {
}
});
}
} finally {
ctx.logger.info('End proxyRequest');
}
} finally {
ctx.logger.info('End proxyRequest');
}
}
/**
@ -243,7 +228,7 @@ async function processProvider(ctx, provider) {
let engineModelsUI = [];
try {
// Call getModels from engine.js
if (provider.key) {
if (provider.key && AI.Providers[provider.name]) {
AI.Providers[provider.name].key = provider.key;
// aiEngine.setCtx(ctx);
// await AI.getModels(provider);
@ -275,11 +260,6 @@ async function processProvider(ctx, provider) {
async function getPluginSettings(ctx) {
const logger = ctx.logger;
logger.info('Starting getPluginSettings');
let res = nodeCache.get(ctx.tenant);
if (res) {
ctx.logger.debug('getPluginSettings from cache');
return res;
}
const result = {
version: 3,
actions: {},
@ -337,7 +317,6 @@ async function getPluginSettings(ctx) {
}
const tenVersion = ctx.getCfg('aiSettings.version', cfgAiSettings.version);
result.version = tenVersion;
// nodeCache.set(ctx.tenant, result);
} catch (error) {
logger.error('Error retrieving AI models from config:', error);
}
@ -392,9 +371,9 @@ async function requestModels(req, res) {
if (AI.Providers[body.name]) {
AI.Providers[body.name].key = body.key;
AI.Providers[body.name].url = body.url;
}
}
let getRes = await AI.getModels(body);
getRes.modelsApi = AI.TmpProviderForModels?.models;
getRes.modelsApi = AI.TmpProviderForModels?.models;
res.json(getRes);
} catch (error) {
ctx.logger.error('getModels error: %s', error.stack);