ServiceNow – Testing Automatic Code Entry from ChatGPT – Initial Testing

This is purely a test at the moment and still needs some work. As mentioned in my previous post, I am looking to create a ChatGPT integration that will allow for ChatGPT to enter code directly into the environment.

To do this, I added several functions into the ChatGPT script include I created in the last post. The updated script include is included at the bottom of this post.

I’m thinking to use this I might create a table to store the ChatGPT requests.

The new functions are as follows:

FunctionNotes
extractAssistantMessageUsed to extract the response message from ChatGPT. Purely to make things a bit easier.
createScriptCreates a script on the system based on the response it receives from ChatGPT.
extractCodeBlocksNOT YET USED: This script extracts the code blocks that ChatGPT returns. Not used at present but might update if there are multiple code blocks.

To test the process, I created a fix script. This fix script asks ChatGPT to create a ServiceNow fix script to query for active users with a first name of Jon.

var chatGPT = new global.ChatGPT();
try {
    var premise = chatGPT.setPremise("You are writing a code block for use in ServiceNow. I understand you cannot write it into ServiceNow directly. You should respond as a JSON string with no additional text. The response should have the following keys: name (used as a simple name for the script), table (the script table name, E.G. fix script is sys_script_fix), code (the code you are providing), notes (any notes you have about the code).");
    var message1 = chatGPT.createMessage("user", "Can you write me a ServiceNow fix script to query for active users with a first name of Jon.");
    var result = chatGPT.submitChat([premise, message1]);
    chatGPT.logDebug("RESULT IS: " + result);

    var extract = chatGPT.extractAssistantMessage(result);
    chatGPT.logDebug("ASSISTANT MESSAGE IS: " + extract);

    var scriptId = chatGPT.createScript(extract);
    if (scriptId) {
        chatGPT.logDebug("Script was created successfully with id: " + scriptId);
    } else {
        chatGPT.logDebug("Script creation failed.");
    }
} catch (e) {
    gs.error("Error during execution: " + e.message, "ChatGPT");
}

When you run the fix script, you get the following responses. For the result:

RESULT IS: {
  "id": "chatcmpl-XXXXXXXXXXXXXXXXX",
  "object": "chat.completion",
  "created": 1690730521,
  "model": "gpt-3.5-turbo-0613",
  "choices": [
    {
      "index": 0,
      "message": {
        "role": "assistant",
        "content": "{\n  \"name\": \"ActiveUsersWithFirstNameJon\",\n  \"table\": \"sys_script_fix\",\n  \"code\": \"var grUsers = new GlideRecord('sys_user');\\n\\\ngrUsers.addQuery('active', true);\\n\\\ngrUsers.addQuery('first_name', 'Jon');\\n\\\ngrUsers.query();\\n\\\n\\n\\\nwhile (grUsers.next()) {\\n\\\n    gs.info('User: ' + grUsers.name);\\n\\\n}\",\n  \"notes\": \"This fix script queries the sys_user table for active users with a first name of 'Jon' and logs their names using the gs.info method.\"\n}"
      },
      "finish_reason": "stop"
    }
  ],
  "usage": {
    "prompt_tokens": 121,
    "completion_tokens": 133,
    "total_tokens": 254
  }
}

For the assistant message:

ASSISTANT MESSAGE IS: {
  "name": "Query Active Users with First Name Jon",
  "table": "sys_script_fix",
  "code": "var gr = new GlideRecord('sys_user');\n\ngr.addQuery('active', true);\ngr.addQuery('first_name', 'Jon');\ngr.query();",
  "notes": "This fix script queries the sys_user table for active users with a first name of Jon."
}

If all is well, you should get some messages saying the script has been created.

ChatGPT: Creating script with name: Query Active Users with First Name Jon
ChatGPT: Script created with sys_id: 02b1a8ae475831100dbe0bdbd36d43f0
ChatGPT: Script was created successfully with id: 02b1a8ae475831100dbe0bdbd36d43f0

I have seen a few issues with the response from ChatGPT having unescaped characters that the code doesn’t like. Trying to find a way around that.

Below is the updated ChatGPT script include with the new functions and some additional logging. Hope it helps.

var ChatGPT = Class.create();
ChatGPT.prototype = {
    debug: true, // Set to true to enable logging

    initialize: function() {
        this.model = "gpt-3.5-turbo";
        this.logDebug("ChatGPT instance created with model: " + this.model);
    },

    setPremise: function(premise) {
        try {
            this.logDebug("Setting premise: " + premise);
            return this.createMessage("system", premise);
        } catch (ex) {
            var exception_message = ex.getMessage();
            gs.error(exception_message, "ChatGPT");
        }
    },

    createMessage: function(role, content) {
        try {
            this.logDebug("Creating message with role: " + role + " and content: " + content);
            return {
                "role": role,
                "content": content
            };
        } catch (ex) {
            var exception_message = ex.getMessage();
            gs.error(exception_message, "ChatGPT");
        }
    },

    submitChat: function(messages) {
        try {
            this.logDebug("Submitting chat messages: " + JSON.stringify(messages));
            var request = new sn_ws.RESTMessageV2("ChatGPT", "POST");
            request.setHttpMethod('POST');

            var payload = {
                "model": this.model,
                "messages": messages,
                "temperature": 0.7
            };

            this.logDebug("Payload: " + JSON.stringify(payload));
            request.setRequestBody(JSON.stringify(payload));

            var response = request.execute();
            var httpResponseStatus = response.getStatusCode();
            var httpResponseContentType = response.getHeader('Content-Type');

            if (httpResponseStatus === 200 && httpResponseContentType === 'application/json') {
                this.logDebug("ChatGPT API call was successful");
                this.logDebug("ChatGPT Response was: " + response.getBody());
                return response.getBody();
            } else {
                gs.error('Error calling the ChatGPT API. HTTP Status: ' + httpResponseStatus, "ChatGPT");
            }
        } catch (ex) {
            var exception_message = ex.getMessage();
            gs.error(exception_message, "ChatGPT");
        }
    },

    extractAssistantMessage: function(apiResponse) {
        try {
            var apiResponseObject = JSON.parse(apiResponse);

            if (apiResponseObject.choices && apiResponseObject.choices[0] && apiResponseObject.choices[0].message && apiResponseObject.choices[0].message.content) {
                this.logDebug("Extracted assistant message: " + apiResponseObject.choices[0].message.content);
                return apiResponseObject.choices[0].message.content;
            } else {
                gs.error("No message found in the API response.", "ChatGPT");
                return null;
            }
        } catch (ex) {
            var exception_message = ex.getMessage();
            gs.error(exception_message, "ChatGPT");
        }
    },

    extractCodeBlocks: function(assistantMessage) {
        try {
            if (!assistantMessage) {
                gs.error("Assistant message is null or undefined", "ChatGPT");
                return null;
            }

            if (typeof(assistantMessage) == "string")
                assistantMessage = JSON.parse(assistantMessage);

            var code = assistantMessage.code;

            if (!code) {
                gs.error("No code found in the assistant message.", "ChatGPT");
                return null;
            }

            return code;
        } catch (ex) {
            var exception_message = ex.getMessage();
            gs.error(exception_message, "ChatGPT");
        }
    },

    createScript: function(scriptJson) {
        try {
            if (typeof(scriptJson) == "string")
                scriptJson = JSON.parse(scriptJson);

            if (!scriptJson.name || !scriptJson.code || !scriptJson.notes || !scriptJson.table) {
                gs.error("JSON is missing required properties", "ChatGPT");
                return null;
            }

            this.logDebug("Creating script with name: " + scriptJson.name);

            var gr = new GlideRecord(scriptJson.table);
            gr.initialize();
            gr.setValue('name', scriptJson.name);
            gr.setValue('script', scriptJson.code);
            gr.setValue('description', scriptJson.notes);
            var sys_id = gr.insert();

            if (sys_id) {
                this.logDebug("Script created with sys_id: " + sys_id);
                return sys_id;
            } else {
                gs.error("Failed to create script", "ChatGPT");
                return null;
            }
        } catch (e) {
            gs.error("Failed to parse script JSON: " + e.message, "ChatGPT");
            return null;
        }
    },

    logDebug: function(log_message) {
        if (this.debug) {
            gs.log(log_message, "ChatGPT");
        }
    },

    type: 'ChatGPT'
};

ServiceNow: Automating Fix Script Generation with a UI Action

Sorry for the title; didn’t know what to call this! Ever so often I’ll run a fix script to perform some sort of action on a GlideRecord (updating an attribute on a number of records for example).

With this idea, I created a UI action that generates a fix script template based on the current record. This script includes the basic structure for loading the specific record and leaves space for you to define the operations you want to perform on it.

To implement this, you need to create a new UI action and use the provided script below. You can customize it according to your needs. I’m sharing this in case anyone else finds it as useful as I do. Let me know if you have any thoughts or suggestions for improvement! This will only show for admins due to the conditions.

NameCreate fix script to get this record
Tableglobal
Form linktrue
Show updatetrue
Conditionsgs.hasRole(“admin”)

In the script area, add the following:

// Retrieving the relevant data from the current context: table name, record ID, and user
var tableName = current.getTableName();
var recordSysId = current.sys_id.toString();
var username = gs.getUser().getName();

// Creating a unique name for the fix script, using the retrieved username and table name
var fixScriptName = "Auto Fix Script: " + username + " - " + tableName;

// Generating the fix script, which will load a record from the table and 
// then perform operations on it (operations to be defined by the user)
var fixScript =
    "// Generated Fix Script\n" +
    "var record = new GlideRecord('" + tableName + "');\n" +
    "if (record.get('" + recordSysId + "')) {\n" +
    "    // perform operations on the record here\n" +
    "}\n";

// Logging the generated script to the system log
gs.info("Generated Fix Script: \n" + fixScript);

// Creating a new record in the 'sys_script_fix' table, to save the generated fix script
var fix_script = new GlideRecord('sys_script_fix');
fix_script.name = fixScriptName;
fix_script.description = "Automatically created via UI action.";
fix_script.script = fixScript;

// Inserting the new fix script record into the system and storing the returned sys_id
var fixScriptSysId = fix_script.insert();

// Redirecting the user to the newly created fix script record in the system
action.setRedirectURL(fix_script);

Once done, submit the UI action. You should now see a new UI action appear in the form of a link on the various records. It should look something like this:

When you run the UI action on a record you should get something like this: