Name

global.AutoResolutionComposite

Description

Composite API integration for Auto-Resolution flow

Script

var AutoResolutionComposite = Class.create();
AutoResolutionComposite.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();
  },

  addSolutionDefinition: function(inputSolutionName) {
  	var compositeSolutionTemplate = this.getCompositeSolutionTemplate(inputSolutionName,
  		AutoResolutionConstants.ML_CAPBILITIES.AGENT_ZERO_WORKFLOW);
  	if (gs.nil(compositeSolutionTemplate)) {
  		return '';
  	}

  	var compositeSolutionName;
  	try {
  		compositeSolutionName = sn_ml.CompositeSolutionStore.add(compositeSolutionTemplate);
  	} catch (error) {
  		this.LOGGER.error("Failed to add a Composite solution definition. Error: {0}", error);
  		compositeSolutionName = '';
  	}

  	return compositeSolutionName;
  },

  createOrUpdateCompositeSolution: 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 Composite solution name
  	var compositeSolutionName;
  	try {
  		compositeSolutionName = 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 Composite
  		compositeSolutionName = '';
  	}

  	var isUpdateSuccessful = true;
  	if (gs.nil(compositeSolutionName)) {
  		// If the solution name does not exist, create a new solution
  		compositeSolutionName = this.addSolutionDefinition(inputSolutionName);
  		var capability = AutoResolutionConstants.ML_CAPBILITIES.COMPOSITE;
  		languageConfigGr = AutoResolutionLanguageHelper.createLanguageConfigRecord(this.getConfigSysId(), languageCode,
  			capability, compositeSolutionName);
  	} else {
  		// If the solution name does exist, update an existing solution
  		isUpdateSuccessful = this.updateSolutionDefinition(compositeSolutionName);
  		languageConfigGr = AutoResolutionLanguageHelper.getLanguageConfigRecordBySolutionName(this.getConfigSysId(), this.getCapability(), compositeSolutionName);
  	}

  	if (gs.nil(compositeSolutionName)) {
  		this.LOGGER.error("Failed to create the Composite solution because the Composite solution name does not exist.");
  		return;
  	}

  	if (!isUpdateSuccessful) {
  		this.LOGGER.error("Failed to update Composite solution.");
  		return;
  	}

  	if (gs.nil(languageConfigGr)) {
  		this.LOGGER.error("Failed to create or update the Composite solution because the language configuration record does not exist.");
  		return;
  	}

  	// Set the input solution to be inactive
  	inputLanguageConfigGr.active = false;
  	inputLanguageConfigGr.update();

  	// delete any languageX configuration record (if it exist, mainly for Utah --> Vancouver upgrade scenario)
  	AutoResolutionLanguageHelper.deleteLanguageConfiguration(this.getConfigSysId(), AutoResolutionConstants.ML_CAPBILITIES.LANGUAGEX);

  	// Update the training results on the language config record
  	var trainingResults = this._getDefaultTrainingResults(compositeSolutionName);
  	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);

  },

  /**
   * Returns solution name for the configSysId
   */
  getSolutionName: function(languageCode) {
  	return AutoResolutionLanguageHelper.getSolutionNameForLanguage(languageCode, this.getConfigSysId(), this.getCapability());
  },

  hasActiveVersion: function(solutionName) {
  	try {
  		if (!this.checkSolutionExists(solutionName))
  			return false;
  		var mlSolution = sn_ml.CompositeSolutionStore.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.CompositeSolutionStore.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;
  	}
  },

  predict: function(taskGr, predictionInputFields, options, languageCode) {
  	var solutionName = this.getSolutionName(languageCode);
  	return this.predictBase(sn_ml.CompositeSolutionStore, solutionName, taskGr, predictionInputFields, options);
  },

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

  // For Composite capability we don't need to show refresh or cancel training UI actions
  showRefreshOrCancelTrainingButton: function() {
  	return false;
  },

  setCapability: function() {
  	this.capability = AutoResolutionConstants.ML_CAPBILITIES.COMPOSITE;
  },

  getCapability: function() {
  	return this.capability;
  },

  /**
   * Gets the Composite 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 Composite solution.
   * @return {Object} The Composite Solution template.
   */
  getCompositeSolutionTemplate: function(inputSolutionName, capability) {
  	// Get list of all config sys_ids that use the same Composite 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);

  	var configGr = new GlideRecord(AutoResolutionConstants.CONFIG_TABLE_NAME);
  	configGr.get(this.getConfigSysId());
  	var taskType = AutoResolutionUtil.getTaskType(configGr.getValue(AutoResolutionConstants.CONFIGURATION_TABLENAME_FIELD_NAME));

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

  	// Add all the NLU model names to the list of custom intents in the configJSON object
  	configJSON.bindings = [];
  	completeNluModelNameList.forEach(function(name) {
  		var obj = {
  			"bindingId": "",
  			"actionReference": "FLOATING",
  			"solutionName": name,
  			"capability": "nlu_trainer",
  			"solutionVersion": "",
  			"parameters": {
  			},
  			"sharedModel": false
  		};
  		configJSON.bindings.push(obj);
  	});

  	var solutionTemplate;
  	try {
  		solutionTemplate = new sn_ml.CompositeSolution(configJSON);
  	} catch (error) {
  		this.LOGGER.error("Failed to create a new CompositeSolution template. Error: {0}", error);
  		solutionTemplate = null;
  	}

  	return solutionTemplate;
  },

  /**
   * Updates the Composite solution definition in the ml_capability_definition_base table. Should increase the version number by 1.
   * @param {string} CompositeSolutionName	The name of the Composite solution.
   * @return Whether the solution definition has been successfully updated.
   */
  updateSolutionDefinition: function(compositeSolutionName) {
  	try {
  		var compositeSolutionTemplate = this.getCompositeSolutionTemplate(compositeSolutionName,
  			AutoResolutionConstants.ML_CAPBILITIES.COMPOSITE);
  		sn_ml.CompositeSolutionStore.update(compositeSolutionName, compositeSolutionTemplate);
  		return true;

  	} catch (error) {
  		this.LOGGER.error("Failed to update the Composite solution definition. Error: {0}", error);
  	}

  	return false;
  },

  /**
   * Updates all solution definitions for all languages that use the Composite capability.
   */
  updateSolutionDefinitionsForAllLanguages: function() {
  	var compositeSolutionNameList = AutoResolutionLanguageHelper.getAllSolutionNamesByCapability(this.getConfigSysId(),
  		this.getCapability());

  	// For each solution name, update the solution definition
  	for (var i = 0; i < compositeSolutionNameList.length; i += 1) {
  		var compositeSolutionName = compositeSolutionNameList[i];
  		var isUpdateSuccessful = this.updateSolutionDefinition(compositeSolutionName);

  		if (!isUpdateSuccessful) {
  			continue;
  		}

  		// Update the training results on the language config record
  		var trainingResults = this._getDefaultTrainingResults(compositeSolutionName);
  		var languageConfigGr = AutoResolutionLanguageHelper.getLanguageConfigRecordBySolutionName(
  			this.getConfigSysId(), this.getCapability(), compositeSolutionName);
  		AutoResolutionLanguageHelper.updateLanguageConfigRecord(languageConfigGr, trainingResults);
  	}
  },

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

  	}
  	return predictionResults;
  },

  /**
   * Gets the configuration JSON object from the existing Composite solution definition given the solution name.
   * @param {string} compositeSolutionName	The name of the Composite solution.
   * @return {Object} JSON object for configuring the Composite solution definition.
   * @private
   */
  _getConfigJSONFromMLCapabilitySolutionBaseRecord: function(compositeSolutionName) {
  	var mlDefinitionGr = new GlideRecord("ml_capability_definition_base");
  	mlDefinitionGr.addQuery("solution_name", compositeSolutionName);
  	mlDefinitionGr.query();

  	if (!mlDefinitionGr.next()) {
  		this.LOGGER.error("Failed to update the Composite solution definitions for the configuration {0} because there does not exist a Solution Definition record for the Composite solution name {1}",
  			this.getConfigSysId(), compositeSolutionName);
  		return "";
  	}

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

  	var configJSON;
  	try {
  		configJSON = JSON.parse(solutionProperties);
  	} catch (error) {
  		this.LOGGER.error("Failed to update the Composite solution definitions for the configuration {0} because the solution properties is invalid in the Solution Definition record for the Composite solution name {1}",
  			this.getConfigSysId(), compositeSolutionName);
  		configJSON = "";
  	}

  	return configJSON;
  },

  /**
   * Creates the configuration JSON object for creating or updating the Composite solution definition with.
   * @param {string} inputSolutionName	The name of the input solution that the Composite solution will wrap around.
   * @return {Object} JSON object for configuring the Composite solution definition.
   * @private
   */
  _getDefaultConfigJSON: function(inputSolutionName, taskType) {
  	var configJSON = {
  		"languageVersion":"0.2",
  		"solutionName":"Issue Auto-Resolution",
  		"taskType":taskType,
  		"label":"Issue Auto-Resolution",
  		"outputs":{
  			"output":[
  				"agent_zero_workflow"
  			]
  		},
  		"solutionVersion":"1",
  		"inputs":{
  			"input":{
  				"fields":[
  					"short_description"
  				]
  			}
  		},
  		"activities":[
  			{
  				"activityId":"type_adapter_lds",
  				"action":{
  					"actionType":"type_adapter",
  					"boundSolutionConfig":{
  						"solutionName":"",
  						"solutionVersion":""
  					},
  					"parameters":{
  						"concatenateRecordIntoField":"short_description"
  					},
  					"capability":"",
  					"capabilityVersion":{
  						"version":"",
  						"parameters":{

  						}
  					},
  					"inputSource":"",
  					"jsonAdapter":{
  						"INPUT":{
  							"type":"sourceField",
  							"value":"",
  							"source":"input.short_description",
  							"jsonPath":"",
  							"isOutputList":false,
  							"outputType":"STRING"
  						}
  					}
  				},
  				"next_activities":[
  					{
  						"activityId":"agent_zero_workflow",
  						"on_condition":{

  						},
  						"additionalOutputFields":{
  							"serviceOutput":{
  								"type":"jsonPath",
  								"value":"",
  								"source":"",
  								"jsonPath":"$..predictedValue"
  							},
  							"serviceOutputScore":{
  								"type":"jsonPath",
  								"value":"",
  								"source":"",
  								"jsonPath":"$..confidence"
  							},
  							"serviceModelUsed":{
  								"type":"jsonPath",
  								"value":"",
  								"source":"",
  								"jsonPath":"$..serviceModelUsed"
  							},
  							"service":{
  								"type":"jsonPath",
  								"value":"",
  								"source":"",
  								"jsonPath":"$..service"
  							},
  							"serviceModelSolutionName":{
  								"type":"jsonPath",
  								"value":"",
  								"source":"",
  								"jsonPath":"$..serviceModelSolutionName"
  							},
  							"serviceModelSolutionVersion":{
  								"type":"jsonPath",
  								"value":"",
  								"source":"",
  								"jsonPath":"$..serviceModelSolutionVersion"
  							},
  							"serviceModelParentServiceName":{
  								"type":"sourceField",
  								"value":"",
  								"source":"parameters.serviceModelParentServiceName",
  								"jsonPath":""
  							},
  							"serviceModelParentServiceVersion":{
  								"type":"sourceField",
  								"value":"",
  								"source":"parameters.serviceModelParentServiceVersion",
  								"jsonPath":""
  							}
  						}
  					}
  				]
  			},
  			{
  				"activityId":"agent_zero_workflow",
  				"action":{
  					"actionType":"solution",
  					"boundSolutionConfig":{
  						"solutionName":inputSolutionName
  					},
  					"parameters":{
  						"predictorOutputKey":"ml_prediction_results"
  					},
  					"capability":"workflow_trainer",
  					"capabilityVersion":{
  						"version":"",
  						"parameters":{

  						}
  					},
  					"inputSource":"type_adapter_lds",
  					"jsonAdapter":{

  					}
  				},
  				"next_activities":[

  				]
  			}
  		]
  	};
  	return configJSON;
  },

  /**
   * Creates the object of training result values to set the language configuration record of a Composite 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} CompositeSolutionName	Name of the Composite 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(compositeSolutionName) {
  	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.CompositeSolutionStore, compositeSolutionName);
  	return trainingResults;
  },

  type: 'AutoResolutionComposite'
});

Sys ID

89006f22775a1110f14a24f1cd5a996b

Offical Documentation

Official Docs: