Name

global.AutoResolutionLanguageX

Description

LanguageX API integration for Auto-Resolution flow

Script

var AutoResolutionLanguageX = Class.create();
AutoResolutionLanguageX.ERROR_CODE = 500;

AutoResolutionLanguageX.prototype = Object.extendsObject(AutoResolutionMLBase, {
  contextSysId : '',
  capability : '',

  initialize: function(configSysId, contextSysId) {
  	this.setConfigSysId(configSysId);
  	this.contextSysId = contextSysId;
  	this.setCapability();
  	this.LOGGER = new AutoResolutionLoggingUtils()
  		.withName(this.type)
  		.withConfiguration(this.getConfigSysId())
  		.createLogger();
  },

  /**
   * Adds a solution definition for languageX and updates the ml_capability_definition_base table.
   * @param {string} inputSolutionName	The name of the input solution that the LanguageX solution will wrap around.
   @ @return {string} The LanguageX Solution name, or an empty string.
   */
  addSolutionDefinition: function(inputSolutionName) {
  	var languageXSolutionTemplate = this.getLanguageXSolutionTemplate(inputSolutionName, 
  		AutoResolutionConstants.ML_CAPBILITIES.AGENT_ZERO_WORKFLOW);
  	if (gs.nil(languageXSolutionTemplate)) {
  		return '';
  	}
  	
  	var languageXSolutionName;
  	try {
  		languageXSolutionName = sn_ml.LanguageXSolutionStore.add(languageXSolutionTemplate);
  	} catch (error) {
  		this.LOGGER.error("Failed to add a LanguageX solution definition. Error: {0}", error);
  		languageXSolutionName = '';
  	}
  	
  	return languageXSolutionName;
  },
  
  /**
   * Creates a new LanguageX solution or updates an existing one.
   * @param {string} inputLanguageConfigGr	GlideRecord of the input language configuration used to trigger the creation or update of its 
   * 											corresponding LanguageX solution language configuration.
   */
  createOrUpdateLanguageXSolution: function(inputLanguageConfigGr) {
  	var inputSolutionName = inputLanguageConfigGr.getValue("ml_solution_name");
  	var languageCode = inputLanguageConfigGr.getValue("training_language");
  	var isConfigActive = AutoResolutionUtil.isConfigurationActive(this.getConfigSysId());
  	var languageConfigGr;
  	
  	// Check if there exists a LanguageX solution name
  	var languageXSolutionName;
  	try {
  		languageXSolutionName = AutoResolutionLanguageHelper.getSolutionNameForLanguage(languageCode, 
  			this.getConfigSysId(), this.getCapability());
  	} catch (error) {
  		// Creating a new language config record because there does not exist a language config record with the LanguageX
  		languageXSolutionName = '';
  	}
  	
  	var isUpdateSuccessful = true;
  	if (gs.nil(languageXSolutionName)) {
  		// If the solution name does not exist, create a new solution
  		languageXSolutionName = this.addSolutionDefinition(inputSolutionName);
  		var capability = AutoResolutionConstants.ML_CAPBILITIES.LANGUAGEX;
  		languageConfigGr = AutoResolutionLanguageHelper.createLanguageConfigRecord(this.getConfigSysId(), languageCode, 
  			capability, languageXSolutionName);
  	} else {
  		// If the solution name does exist, update an existing solution
  		isUpdateSuccessful = this.updateSolutionDefinition(languageXSolutionName);
  		languageConfigGr = AutoResolutionLanguageHelper.getLanguageConfigRecordBySolutionName(this.getConfigSysId(), this.getCapability(), languageXSolutionName);
  	}
  	
  	if (gs.nil(languageXSolutionName)) {
  		this.LOGGER.error("Failed to create the LanguageX solution because the LanguageX solution name does not exist.");
  		return;
  	}
  	
  	if (!isUpdateSuccessful) {
  		this.LOGGER.error("Failed to update LanguageX solution.");
  		return;
  	}
  	
  	if (gs.nil(languageConfigGr)) {
  		this.LOGGER.error("Failed to create or update the LanguageX solution because the language configuration record does not exist.");
  		return;
  	}

  	// Set the input solution to be inactive
  	inputLanguageConfigGr.active = false;
  	inputLanguageConfigGr.update();
  	
  	// Update the training results on the language config record
  	var trainingResults = this._getDefaultTrainingResults(languageXSolutionName);
  	AutoResolutionLanguageHelper.updateLanguageConfigRecord(languageConfigGr, trainingResults);

  	// Restore the active value of IAR configuration record to which may have been altered due to training
  	if (isConfigActive)
  		AutoResolutionUtil.updateConfigRecord(this.getConfigSysId(), {"active": isConfigActive});

  	// Set BR for language configuration active check to active if not so already
  	AutoResolutionUtil.setBRActive(AutoResolutionConstants.LANGUAGE_CONFIG_ACTIVE_CHECK_BR);

  },
  
  /**
   * Calls the LangX predict API using the current solution name by default
   * @param taskGr - the GlideRecord of the task
   * @param predictionInputFields - fields from taskGr with its value that needs to be sent to LangX for prediction
   * @returns {{message: string, status: string, payload: object}}
   */
  predict: function(taskGr, predictionInputFields, options) {
  	var solutionName = this.getSolutionName();
  	return this.predictBase(sn_ml.LanguageXSolutionStore, solutionName, taskGr, predictionInputFields, options);
  },
  
  /**
   * Returns solution name for the configSysId
   */
  getSolutionName: function() {
  	return AutoResolutionLanguageHelper.getSolutionNameForLanguage("en", this.getConfigSysId(), this.getCapability());
  },

  hasActiveVersion: function(solutionName) {
  	try {
  		if (!this.checkSolutionExists(solutionName))
  			return false;
  		var mlSolution = sn_ml.LanguageXSolutionStore.get(solutionName);
  		var solutionVersion = mlSolution.getActiveVersion();
  		return !gs.nil(solutionVersion);
  	} catch(ex) {
  		this.LOGGER.warn('Error checking for ML solution active version: {0}', ex.getMessage());
  		return false;
  	}
  },
  
  checkSolutionExists: function(solutionName) {
  	try {
  		if (!gs.nil(solutionName)) {
  			var autoResolutionPISolution = sn_ml.LanguageXSolutionStore.get(solutionName);
  			return !gs.nil(autoResolutionPISolution);
  		}
  		return false;
  	} catch(ex) {
  		this.LOGGER.warn('Error checking ML solution exists: name={0}: {1}', solutionName, ex);
  		return false;
  	}
  },

  // For languageX capability we don't need to show train UI Action
  showTrainButton: function() {
  	return false;
  },

  // For languageX capability we don't need to show refresh or cancel training UI actions
  showRefreshOrCancelTrainingButton: function() {
  	return false;
  },
  
  setCapability: function() {
  	this.capability = AutoResolutionConstants.ML_CAPBILITIES.LANGUAGEX;
  },
  
  getCapability: function() {
  	return this.capability;
  },
  
  /**
   * Gets the LanguageX solution template, by an existing configJSON from the ML solution if it exists, or
   * by creating a new configJSON if not.
   * @param {string} inputSolutionName	The solution name of a Workflow or LanguageX solution.
   * @return {Object} The LanguageX Solution template.
   */
  getLanguageXSolutionTemplate: function(inputSolutionName, capability) {
  	// Get list of all config sys_ids that use the same LanguageX solution name
  	var configIdList = AutoResolutionUtil.getConfigIdsWithSameSolutionName(inputSolutionName);
  	var completeNluModelNameList = [];

  	// Use the list of config sys_ids to create a set of NLU model names
  	var arrayUtil = new ArrayUtil();
  	configIdList.forEach(function(configId) {
  		var partialNluModelNameList = AutoResolutionUtil.getNLUModelNameListFromConfig(configId);
  		completeNluModelNameList = arrayUtil.concat(completeNluModelNameList, partialNluModelNameList);
  	});
  	completeNluModelNameList = arrayUtil.unique(completeNluModelNameList);

  	// Get existing LanguageX config, if the inputSolutionName refers to a LanguageX solution
  	// Otherwise, treat the inputSolutionName as a Workflow solution name and create a new config object using this
  	var configJSON = (capability === AutoResolutionConstants.ML_CAPBILITIES.LANGUAGEX) 
  		? this._getConfigJSONFromMLCapabilitySolutionBaseRecord(inputSolutionName)
  		: this._getDefaultConfigJSON(inputSolutionName);

  	// Add all the NLU model names to the list of custom intents in the configJSON object
  	configJSON.options.customIntents = [];
  	completeNluModelNameList.forEach(function(name) {
  		var obj = {
  			"solutionName": name,
  		};
  		configJSON.options.customIntents.push(obj);
  	});

  	var solutionTemplate;
  	try {
  		solutionTemplate = new sn_ml.LanguageXSolution(configJSON);
  	} catch (error) {
  		this.LOGGER.error("Failed to create a new LanguageXSolution template. Error: {0}", error);
  		solutionTemplate = null;
  	}
  	
  	return solutionTemplate;
  },
  
  /**
   * Updates the LanguageX solution definition in the ml_capability_definition_base table. Should increase the version number by 1.
   * @param {string} languageXSolutionName	The name of the LanguageX solution.
   * @return Whether the solution definition has been successfully updated.
   */
  updateSolutionDefinition: function(languageXSolutionName) {
  	try {
  		var languageXSolutionTemplate = this.getLanguageXSolutionTemplate(languageXSolutionName, 
  			AutoResolutionConstants.ML_CAPBILITIES.LANGUAGEX);
  		sn_ml.LanguageXSolutionStore.update(languageXSolutionName, languageXSolutionTemplate);
  		return true;
  		
  	} catch (error) {
  		this.LOGGER.error("Failed to update the LanguageX solution definition. Error: {0}", error);
  	}
  	
  	return false;
  },
  
  /**
   * Updates all solution definitions for all languages that use the LanguageX capability.
   */
  updateSolutionDefinitionsForAllLanguages: function() {
  	var languageXSolutionNameList = AutoResolutionLanguageHelper.getAllSolutionNamesByCapability(this.getConfigSysId(), 
  		this.getCapability());

  	// For each solution name, update the solution definition
  	for (var i = 0; i < languageXSolutionNameList.length; i += 1) {
  		var languageXSolutionName = languageXSolutionNameList[i];
  		var isUpdateSuccessful = this.updateSolutionDefinition(languageXSolutionName);
  		
  		if (!isUpdateSuccessful) {
  			continue;
  		}
  		
  		// Update the training results on the language config record
  		var trainingResults = this._getDefaultTrainingResults(languageXSolutionName);
  		var languageConfigGr = AutoResolutionLanguageHelper.getLanguageConfigRecordBySolutionName(
  			this.getConfigSysId(), this.getCapability(), languageXSolutionName);
  		AutoResolutionLanguageHelper.updateLanguageConfigRecord(languageConfigGr, trainingResults);
  	}
  },

  /**
   * Perform bulk LanguageX prediction
   * @param {string} autoResConfigSysID - the sys_id of IAR Configuration GlideRecord
   * @param {Object} requestObjectList - JSON with input parameters for LanguageX prediction
   * @return {Object} The LanguageX bulk prediction results
   */
  getBulkPrediction: function(requestObjectList) {
  	var predictionResults;
  	try {
  		var languageXSolutionName = this.getSolutionName();
  		var languageXSolution = sn_ml.LanguageXSolutionStore.get(languageXSolutionName);
  		predictionResults = JSON.parse(languageXSolution.getActiveVersion().predict(requestObjectList));
  	}
  	catch (error) {
  		this.LOGGER.error("Failed to perform bulk prediction for the configuration {0} with the LanguageX solution name {1}", 
  			this.getConfigSysId(), languageXSolutionName);
  		predictionResults = null;

  	}
  	return predictionResults;
  },
  
  /**
   * Gets the configuration JSON object from the existing LanguageX solution definition given the solution name.
   * @param {string} languageXSolutionName	The name of the LanguageX solution.
   * @return {Object} JSON object for configuring the LanguageX solution definition.
   * @private
   */
  _getConfigJSONFromMLCapabilitySolutionBaseRecord: function(languageXSolutionName) {
  	var mlDefinitionGr = new GlideRecord("ml_capability_definition_base");
  	mlDefinitionGr.addQuery("solution_name", languageXSolutionName);
  	mlDefinitionGr.query();
  	
  	if (!mlDefinitionGr.next()) {
  		this.LOGGER.error("Failed to update the LanguageX solution definitions for the configuration {0} because there does not exist a Solution Definition record for the LanguageX solution name {1}", 
  			this.getConfigSysId(), languageXSolutionName);
  		return "";
  	}

  	// Solution properties will contain the configuration object used to create the LanguageX solution with
  	var solutionProperties = mlDefinitionGr.getValue("solution_properties");
  	if (gs.nil(solutionProperties)) {
  		this.LOGGER.error("Failed to update the LanguageX solution definitions for the configuration {0} because the solution properties is empty in the Solution Definition record for the LanguageX solution name {1}", 
  			this.getConfigSysId(), languageXSolutionName);
  		return "";
  	}

  	var configJSON;
  	try {
  		configJSON = JSON.parse(solutionProperties);
  	} catch (error) {
  		this.LOGGER.error("Failed to update the LanguageX solution definitions for the configuration {0} because the solution properties is invalid in the Solution Definition record for the LanguageX solution name {1}", 
  			this.getConfigSysId(), languageXSolutionName);
  		configJSON = "";
  	}
  	
  	return configJSON;
  },
  
  /**
   * Creates the configuration JSON object for creating or updating the LanguageX solution definition with.
   * @param {string} inputSolutionName	The name of the input solution that the LanguageX solution will wrap around.
   * @return {Object} JSON object for configuring the LanguageX solution definition.
   * @private
   */
  _getDefaultConfigJSON: function(inputSolutionName) {
  	var configJSON = {
  		"schemaVersion": "1.0",
  		"label": "Issue Auto Resolution",
  		"templates": [{
  			"templateName": "AGENT_ZERO_TEMPLATE",
  			"templateVersion": "1.0",
  			"templateConfig": {
  				"services": [{
  					"serviceName": "agent_zero_trainer",
  					"serviceConfig": {
  						"solutionName": inputSolutionName,
  					},
  				}]
  			},
  			"templateOptions": {},
  		}],
  		"options": {
  			"customIntents": [],
  		},
  	};
  	return configJSON;
  },
  
  /**
   * Creates the object of training result values to set the language configuration record of a LanguageX solution to. Although it
   * doesn't take any time or have a status output because the solution doesn't need to be trained, we still need to make these values
   * non-empty to be consistent with the other solutions that do need to be trained.
   * @param {string} languageXSolutionName	Name of the LanguageX solution.
   * @return {Object} JSON object containing a map of the column names on a language configuration record to their corresponding training
   *                  status values that need to be updated on the record.
   * @private
   */
  _getDefaultTrainingResults: function(languageXSolutionName) {
  	var trainingResults = {};
  	trainingResults.active = true;
  	trainingResults.ml_training_state = "solution_complete";
  	trainingResults.ml_training_progress = 100;
  	trainingResults.ml_status_last_updated = new GlideDateTime();
  	trainingResults.latest_trained_version = 
  		this.getActiveSolutionVersionNumberForSolutionName(sn_ml.LanguageXSolutionStore, languageXSolutionName);
  	return trainingResults;
  },
  
  type: 'AutoResolutionLanguageX'
});

Sys ID

a631bfcd77110110f14a24f1cd5a995b

Offical Documentation

Official Docs: