Backend Integration

You can integrate skills with backend services, such as your company's APIs and the Oracle Cloud Infrastructure APIs, using either the built-in Call REST Service component or through your own custom components. Custom components are also useful for complex processing.

Note that, in addition to the Call REST Service component and custom components, you can use entity event handlers (EEH) to access backend services and do complex processing. When deciding which one to use, ask yourself these questions:

  • Is the call needed in the context of a composite bag entity? In this case, you can use an EEH.

  • Should the outcome of the call determine the next transition? This would be a case for the Call REST Service component or a custom component.

Access Backends Using the REST Service Component

If your skill needs to retrieve or update some data through a backend service, you can quickly implement this by adding a REST service configuration for the API's endpoint and calling that service from your skill using the Call REST Service component.

Add a REST Service for an Endpoint

Oracle Digital Assistant provides a built-in Call REST Service component that you can use in Visual Dialog skills to send a request to a REST service's endpoint. Before you can make the request from a Call REST Service component, you must first configure the endpoint in the REST Services tab.

To add a REST Service:

  1. In Oracle Digital Assistant, click Icon to open the side menu to open the side menu, select Settings, select API Services, and then click the REST Services tab.

  2. Provide this general information:

    Field Description
    Name A unique name that lets you easily identify the endpoint that you are configuring. For example, for an appointments API, you might name the REST services "appointmentsUser" and "appointmentsUserEntry".
    Endpoint The endpoint to access the REST operation. Use curly braces to delineate path parameters. For example: https://example.com/appointments/{userId}.

    For Oracle Cloud Infrastructure endpoints, ensure that you use the endpoint for your tenancy's region.

    Description An optional description that explains the purpose of the endpoint. For example: "Adds and retrieves a user's appointments."
    Authentication Type

    Select how to authenticate the REST call.

    • No Authentication Required: Select this when the service doesn't require authentication.

    • Basic Authentication: Select this when the service uses a Basic authentication. You’ll then need to provide a user name and a password..

    • API Key: Select this when the service uses an API Key for authentication. You’ll then need to indicate whether the key must be passed as a path parameter or a query parameter, and you'll need to specify the key name and value.

    • Bearer Token: Select this when the service uses a Bearer token for authentication. You’ll then need to specify the token.

    • OCI Resource Principal: Select this when you are accessing an Oracle Cloud Infrastructure API.

    Private Endpoint If you have a private endpoint set up for the service that you need to access, turn this switch to the On position and select the private endpoint you are using.

    Note: You can set up private endpoints for services that are not exposed publicly on the internet. See Private Endpoint.

  3. For each method that you want to configure for the endpoint, click + Add Method, select the method, and then provide this information:

    Field Description
    Content Type The type of the content that's included in the request body. application/json and text/plain are supported.
    Body For POST, PUT, and PATCH requests, the request body to send with the request. You can override this value in the Call REST Service component.

    For large request bodies, click Use Edit Dialog to open an editor.

    Parameters You can add path and query parameters for testing the request. You also can add path and query parameters to configure default values. Note that the skill developers can override these parameter values from the Call REST Service component. That is, they can add parameters in the component to override the values configured in the REST service, and not add the parameters where they want to use the values set in the REST service configuration.

    For the path parameters that are in the endpoint, add a parameter of type Path, set the key to match the path parameter, and set the desired value.

    For query parameters that you want to pass in the REST request, add a parameter of type Query, set the key to match the query parameter, and set the desired value.

    If the authentication type is API Key, and the key is passed in as a query parameter, don't add a query parameter for that key value here as it's already configured.

    After you edit the parameter, click Save button to add the parameter to the list.

    Headers Add any headers that you want to pass in the request.

    If the authentication type is API Key, and the key is passed in a header, don't add a header for that key value here as it's already configured.

    Static Response You can use the static response configuration for cases where you need a fallback response whenever there's an error. You can also use it to set up mock data for development or testing purposes.

    To configure a static response, select a return status code and then define the response body. Alternatively, set the desired parameters and headers, click Test Request, and then click Save as Static Response to save values for the static response.

  4. If you want to test the method, set the path and query parameters to use for the path, set any necessary headers, provide a request body if necessary, and click Test Request. A Response Status dialog appears with the response status and body.

    Tip:

    You can click Save as Static Response to save the status code and body in the static response fields.

Use the Call REST Service Component

When a skill needs to retrieve or update some data through a backend service, add a state that uses the Call REST Service component. Here's how to use that component.

Note

The Call Service Component is supported in visual dialogs only, not in YAML dialogs.
  1. Configure the endpoint on the Settings > API Services > REST Services page as described in Add a REST Service for an Endpoint. This is where you define the endpoint, authentication type, supported methods, request body, path and query parameters, headers, and an optional static response.

  2. In the desired flow in the skill's flow designer, add the state, choose Service Integration > Call Rest Service, provide a name, and then click Insert.

  3. Select the REST service that you configured on the REST Services page.

  4. Select the method.

  5. For POST, PUT, and PATCH requests, you'll typically need to provide a request body.

    Tip:

    If the body contains FreeMarker expressions, then you can switch Expression to On to see FreeMarker syntax coloring. However, if you do so, JSON syntax validation is turned off.
  6. In the Parameters section, configure the path and query parameters that you want to send with the request.

    Parameters that you set in the component override the parameters that are set in the REST service configuration. Conversely, if you don't set a parameter in the component, then the request uses the parameter value from the REST service configuration.

    For query parameters that are defined in the REST service configuration, if you don't want to pass that query parameter, set its value to ${r""}.

  7. Set any headers that you want to send with the request.

    Headers that you set in the component override the headers that are set in the REST service configuration. Conversely, if you don't set a header in the component, then the request uses the header value from the REST service configuration.

  8. Specify which response you want returned after the call completes:

    • Use Actual REST API Response: This returns the actual response from the REST service.

    • Always Use Static REST Response: This returns the static response that is configured on the REST Services tab. This response is helpful during development and test phases, among other uses.

    • Fallback Using Static Response: If the REST request is successful, then the REST response is returned. Otherwise, the static response that's configured on the REST Services tab is returned.

    Note that if the REST service configuration doesn't have a static response, then the only choice is Use Actual Response.

  9. For the result variable, select the map variable for storing the response data. If it doesn't exist yet, click Create to make one.

    After the request completes, the map will contain a responsePayload property for the response body and a statusCode property for the status code. How the response body is stored in the variable depends on the whether the response is a JSON object, JSON Array, or plain text (string):

    • JSON Object: The object is stored in the responsePayload property.

    • JSON Array: The array is stored in the responsePayload.responseItems property.

    • Plain Text: The text is stored in the responsePayload.message property.

  10. On the Transitions tab, specify the next transition and the transitions for the success and failure actions.
    • The success action occurs when the statusCode is between 100 and 299.

    • The failure action occurs when the statusCode is 300 and above.

For more details about this component, see Call REST Service.

Access Backends Using Custom Components

Oracle Digital Assistant has many built-in components to support basic actions like setting variables and prompting for user input. In cases where your bot design calls for actions outside of the provided components, such as calling REST APIs, implementing complex business logic, and customizing messages, you can write custom components.

Tip:

If the logic or processing is needed in the context of a composite bag entity, consider using entity event handlers, which you can create directly from the composite bag's configuration page. See Entity Event Handlers.

To use a custom component, complete these tasks:

  1. Implement: Using JavaScript and the Oracle Digital Assistant Node.js SDK, implement a custom component that transfers data to and from the skill using the SDK's metadata and conversation objects. See Task 1: Implement Custom Components.

  2. Deploy: If you are hosting the components on Oracle Mobile Hub backend, Oracle Cloud Infrastructure Functions, or a Node.js server, deploy the component package. See Task 2: Deploy the Component Package to a Service.

  3. Add to Skill: Make the components available to a skill by adding a component service for it. See Task 3: Add Component Package to a Skill.

Task 1: Implement Custom Components

To implement custom components, you use the Oracle Digital Assistant Node.js SDK to interface with Digital Assistant's custom component service.

Here's how to implement custom components that you can deploy to the Digital Assistant embedded container, Oracle Cloud Infrastructure Functions, a Mobile Hub backend, or a Node.js server:

  1. Install the software for building custom components.

  2. Create the custom component package.

  3. Create and build a custom component.

Note

If you plan to deploy the custom component package to an embedded custom component service, each skill that you add the package to is counted as a separate service. There's a limit to how many embedded custom component services an instance can have. If you don't know the limit, ask your service administrator to get the embedded-custom-component-service-count for you as described in View Service Limits in the Infrastructure Console. Consider packaging several components per package to minimize the number of embedded component services that you use. If you try to add a component service after you meet that limit, the service creation fails.
Step 1: Install the Software for Building Custom Components

To build a custom component package, you need Node.js, Node Package Manager, and the Oracle Digital Assistant Bots Node.js SDK.

Note

Oracle Cloud Infrastructure Functions currently supports Node 11 and Node 14. If you plan to deploy to the embedded container, your package should be compatible with Node 14.17.0.
  1. If you haven’t already, download Node.js from https://nodejs.org and install it for global access. Node Package Manager (npm) is distributed with Node.js.

    To test if Node.js and npm are installed, open a terminal window and type these commands:

    node –v 
    npm –v
  2. To install the Oracle Digital Assistant Bots Node.js SDK for global access, enter this command in a terminal window:
    npm install -g @oracle/bots-node-sdk

    On a Mac, you use the sudo command:

    sudo npm install -g @oracle/bots-node-sdk

    When you use the -g (global) option, you have direct access to the bots-node-sdk command line interface. Otherwise, use npx @oracle/bots-node-sdk.

  3. To verify your Oracle Digital Assistant Bots Node.js SDK installation, type the following command:
    bots-node-sdk -v
    The command should print the Oracle Digital Assistant Bots Node.js SDK version.
Step 2: Create the Custom Component Package

To start a project, you use the bots-node-sdk init command from the SDK’s command line interface (CLI) to create the necessary files and directory structure for your component structure.

The init command has a few options, such as whether to use JavaScript (the default) or TypeScript, and what to name the initial component's JavaScript file. These options are described in CLI Developer Tools. Here's the basic command for starting a JavaScript project:

bots-node-sdk init <top-level folder path> --name <component service name>

This command completes the following actions for a JavaScript package:

  • Creates the top-level folder.

  • Creates a components folder and adds a sample component JavaScript file named hello.world.js. This is where you'll put your component JavaScript files.

  • Adds a package.json file, which specifies main.js as the main entry point and lists @oracle/bots-node-sdk as a devDependency. The package file also points to some bots-node-sdk scripts.

    {
      "name": "myCustomComponentService",
      "version": "1.0.0",
      "description": "Oracle Bots Custom Component Package",
      "main": "main.js",
      "scripts": {
        "bots-node-sdk": "bots-node-sdk",
        "help": "npm run bots-node-sdk -- --help",
        "prepack": "npm run bots-node-sdk -- pack --dry-run",
        "start": "npm run bots-node-sdk -- service ."
      },
      "repository": {},
      "dependencies": {},
      "devDependencies": {
        "@oracle/bots-node-sdk": "^2.2.2",
        "express": "^4.16.3"
      }
    }
  • Adds a main.js file, which exports the package settings and points to the components folder for the location of the components, to the top-level folder.

  • Adds an .npmignore file to the top-level folder. This file is used when you export the component package. It must exclude .tgz files from the package. For example: *.tgz.

  • For some versions of npm, creates a package-lock.json file.

  • Installs all package dependencies into the node_modules subfolder.
Note

If you don't use the bots-node-sdk init command to create the package folder, then ensure that the top-level folder contains an .npmignore file that contains a *.tgz entry. For example:
*.tgz
spec
service-*

Otherwise, every time you pack the files into a TGZ file, you include the TGZ file that already exists in the top-level folder, and your TGZ file will continue to double in size.

If you plan to deploy to the embedded container, your package should be compatible with Node 14.17.0.

Step 3: Create and Build a Custom Component
Create the Component File

Use the SDK's CLI init component command to create a JavaScript or TypeScript file with the framework for working with the Oracle Digital Assistant Node.js SDK to write a custom component. The language that you specified when you ran the init command to create the component package determines whether a JavaScript or a TypeScript file is created.

For example, to create a file for the custom component, from a terminal window, CD to the package’s top-level folder and type the following command, replacing <component name> with your component's name:

bots-node-sdk init component <component name> c components

For JavaScript, this command adds the <component name>.js to the components folder. For TypeScript, the file is added to the src/components folder. The c argument indicates that the file is for a custom component.

Note that the component name can't exceed 100 characters. You can only use alphanumeric characters and underscores in the name. You can't use hyphens. Nor can the name have a System. prefix. Oracle Digital Assistant won't allow you to add a custom component service that has invalid component names.

For further details, see https://github.com/oracle/bots-node-sdk/blob/master/bin/CLI.md.

Add Code to the metadata and invoke Functions

Your custom component must export two objects:

  • metadata: This provides the following component information to the skill.

    • Component name

    • Supported properties

    • Supported transition actions

    For YAML-based dialog flows, the custom component supports the following properties by default. These properties aren't available for skills designed in Visual dialog mode.

    • autoNumberPostbackActions: Boolean. Not required. When true, buttons and list options are numbered automatically. The default is false. See Auto-Numbering for Text-Only Channels.

    • insightsEndConversation: Boolean. Not required. When true, the session stops recording the conversation for insights reporting. The default is false. See Model the Dialog Flow.

    • insightsInclude: Boolean. Not required. When true, the state is included in insights reporting. The default is true. See Model the Dialog Flow.

    • translate: Boolean. Not required. When true, autotranslation is enabled for this component. The default is the value of the autotranslation context variable. See Translation Services in Skills.

  • invoke: This contains the logic to execute. In this method, you can read and write skill context variables, create conversation messages, set state transitions, make REST calls, and more. Typically, you would use the async keyword with this function to handle promises. The invoke function takes the following argument:

    • context, which names the reference to the CustomComponentContext object in the Digital Assistant Node.js SDK. This class is described in the SDK documentation at https://oracle.github.io/bots-node-sdk/. In earlier versions of the SDK, the name was conversation. You can use either name.
    Note

    If you are using a JavaScript library that doesn't support promises (and thus aren't using async keyword), it is also possible to add a done argument as a callback that the component invokes when it has finished processing.

Here’s an example:

'use strict';

module.exports = {

  metadata: {
    name: 'helloWorld',
    properties: {
      human: { required: true, type: 'string' }
    },
    supportedActions: ['weekday', 'weekend']
  },

  invoke: async(context) => {
    // Retrieve the value of the 'human' component property.
    const { human } = context.properties();
    // determine date
    const now = new Date();
    const dayOfWeek = now.toLocaleDateString('en-US', { weekday: 'long' });
    const isWeekend = [0, 6].indexOf(now.getDay()) > -1;
    // Send two messages, and transition based on the day of the week
    context.reply(`Greetings ${human}`)
      .reply(`Today is ${now.toLocaleDateString()}, a ${dayOfWeek}`)
      .transition(isWeekend ? 'weekend' : 'weekday');   
  }
}

To learn more and explore some code examples, see Writing Custom Components in the Bots Node SDK documentation.

Control the Flow with keepTurn and transition

You use different combinations of the Bots Node SDK keepTurn and transition functions to define how the custom component interacts with a user and how the conversation continues after the component returns flow control to the skill.

  • keepTurn(boolean) specifies whether the conversation should transition to another state without first prompting for user input.

    Note that if you want to set keepTurn to true, you should call keepTurn after you call reply because reply implicitly sets keepTurn to false.

  • transition(action) causes the dialog to transition to the next state after all replies, if any, are sent. The optional action argument names that action (outcome) that the component returns.

    If you don't call transition(), the response is sent but the dialog stays in the state and subsequent user input comes back to this component. That is, invoke() is called again.

invoke: async (context) ==> {
   ...
   context.reply(payload);
   context.keepTurn(true);
   context.transition ("success"); 
}

Here are some common use cases where you would use keepTurn and transition to control the dialog flow:

Use Case Values Set for keepTurn and transition

A custom component that transitions to another state without first prompting the user for input.

  1. If applicable, use context.reply(<reply>) to send a reply.

  2. Set context.keepTurn(true).

  3. Set context.transition with either a supportedActions string (e.g., context.transition("success")) or with no argument (e.g., context.transition()).

For example, this custom component updates a variable with a list of values to be immediately displayed by the next state in the dialog flow.
invoke: async (context) => {
    const listVariableName = context.properties().variableName;
    ...
    // Write list of options to a context variable
    context.variable(listVariableName, list);
   // Navigate to next state without 
   // first prompting for user interaction.
   context.keepTurn(true);
   context.transition();
 }

A custom component that enables the skill to wait for input after control returns to the skill and before the skill transitions to another state.

  1. If applicable, use context.reply(<reply>) to send a reply.

  2. Set context.keepTurn(false) .

  3. Set context.transition with either a supportedActions string(context.transition("success")) or with no arguments (context.transition()).

For example:
context.keepTurn(false);
context.transition("success");
A custom component that gets user input without returning flow control back to the skill. For example:
  • A component passes the user input to query a backend search engine. If the skill can only accommodate a single result, but the query instead returns multiple hits, the component prompts the user for more input to filter the results. In this case, the custom component continues to handle the user input; it holds the conversation until the search engine returns a single hit. When it gets a single result, the component calls context.transition() to move on to another state as defined in the dialog flow definition.

  • A component processes a questionnaire and only transitions to another next state when all questions are answered.

  1. Do not call transition.

  2. Set keepTurn(false).

For example, this custom component outputs a quote and then displays Yes and No buttons to request another quote. It transitions back to the skill when the user clicks No.
  invoke: async (context) => {
    // Perform conversation tasks.
    const tracking_token = "a2VlcHR1cm4gZXhhbXBsZQ==";    
    const quotes = require("./json/Quotes.json");
    const quote = quotes[Math.floor(Math.random() * quotes.length)];
    
    // Check if postback action is issued. If postback action is issued, 
    // check if postback is from this component rendering. This ensures
    // that the component only responds to its own postback actions.     
    if (context.postback() && context.postback().token == tracking_token && context.postback().isNo) {
      context.keepTurn(true);
      context.transition();
    } else {
      // Show the quote of the day.
      context.reply("'" + quote.quote + "'");
      context.reply(" Quote by: " + quote.origin);
      // Create a single message with two buttons to 
      // request another quote or not.
      const mf = context.getMessageFactory();
      const message = mf.createTextMessage('Do you want another quote?')
        .addAction(mf.createPostbackAction('Yes', { isNo: false, token: tracking_token }))
        .addAction(mf.createPostbackAction('No', { isNo: true, token: tracking_token })); 
      context.reply(message);
      // Although reply() automatically sets keepTurn to false, 
      // it's good practice to explicitly set it so that it's
      // easier to see how you intend the component to behave.
      context.keepTurn(false);
    };
  }

If a component doesn’t transition to another state, then it needs to keep track of its own state, as shown in the above example.

For more complex state handling, such as giving the user the option to cancel if a data retrieval is taking too long, you can create and use a context variable. For example: context.variable("InternalComponentWaitTime", time). If you use a context variable, don't forget to reset it or set it to null before calling context.transition.

Note that as long as you don't transition, all values that are passed in as component properties are available.

The component invocation repeats without user input. For example:

  • A component pings a remote service for the status of an order until the status is returned as accepted or the component times out. If the accepted status is not returned after the fifth ping, then the component transitions with the failedOrder status.

  • The custom component hands the user over to a live agent. In this case, the user input and responses get dispatched to the agent. The component transitions to another state when either the user or the agent terminates their session.

  • Do not call transition.

  • Set context.keepTurn(true).

Here's a somewhat contrived example that shows how to repeat the invocation without waiting for user input, and then how to transition when done:
invoke: async (context) => {

  const quotes = require("./json/Quotes.json");
  const quote = quotes[Math.floor(Math.random() * quotes.length)];
  
  // Check if postback action is issued and postback is from this component rendering. 
  // This ensures that the component only responds to its own postback actions.     
  const um = context.getUserMessage()
  if (um instanceof PostbackMessage && um.getPostback() && um.getPostback()['system.state'] === context.getRequest().state && um.getPostback().isNo) {
    context.keepTurn(true);
    context.transition();
  } else {
    // Show the quote of the day.
    context.reply(`'${quote.quote}'`);
    context.reply(`Quote by: ${quote.origin}`);
    // Create a single message with two buttons to request another quote or not.
    let actions = [];

    const mf = context.getMessageFactory();
    const message = mf.createTextMessage('Do you want another quote?')
      .addAction(mf.createPostbackAction('Yes', { isNo: false }))
      .addAction(mf.createPostbackAction('No', { isNo: true }));
    context.reply(message);
    // Although reply() automatically sets keepTurn to false, it's good practice to explicitly set it so that it's
    // easier to see how you intend the component to behave.
    context.keepTurn(false);
  }
}
Access the Backend

You'll find that there are several Node.js libraries that have been built to make HTTP requests easy, and the list changes frequently. You should review the pros and cons of the currently available libraries and decide which one works best for you. We recommend that you use a library that supports promises so that you can leverage the async version of the invoke method, which was introduced in version 2.5.1, and use the await keyword to write your REST calls in a synchronous way.

One option is the node fetch API that's pre-installed with the Bots Node SDK. Access the Backend Using HTTP REST Calls in the Bots Node SDK documentation contains some code examples.

Use the SDK to Access Request and Response Payloads

You use CustomComponentContext instance methods to get the context for the invocation, access and change variables, and send results back to the dialog engine.

You can find several code examples for using these methods in Writing Custom Components and Conversation Messaging in the Bots Node SDK documentation

The SDK reference documentation is at https://github.com/oracle/bots-node-sdk.

Custom Components for Multi-Language Skills

When you design a custom component, you should consider whether the component will be used by a skill that supports more than one language.

If the custom component must support multi-language skills, then you need to know if the skills are configured for native language support or translation service.

When you use a translation service, you can translate the text from the skill. You have these options:

For native language skills, you have these options:

  • Pass the data back to the skill in context variables and then output the text from a system component by passing the variables' values to a resource bundle key, as described in Use a System Component to Reference a Resource Bundle. With this option, the custom component must have metadata properties for the skill to pass the names of the context variables to store the data in.

  • Use the resource bundle from the custom component to compose the custom component's reply, as described in Reference Resource Bundles from the Custom Component. You use the conversation.translate() method to get the resource bundle string to use for your call to context.reply(). This option is only valid for resource bundle definitions that use positional (numbered) parameters. It doesn't work for named parameters. With this option, the custom component must have a metadata property for name of the resource bundle key, and the named resource bundle key's parameters must match those used in the call to context.reply().

Here's an example of using the resource bundle from the custom component. In this example, fmTemplate would be set to something like ${rb('date.dayOfWeekMessage', 'lundi', '19 juillet 2021')}.

'use strict';

var IntlPolyfill    = require('intl');
Intl.DateTimeFormat = IntlPolyfill.DateTimeFormat;

module.exports = {
  metadata: () => ({
    name: 'Date.DayOfWeek',
    properties: {
      rbKey:   { required: true,  type: 'string'    }
    },
    supportedActions: []
  }),
  invoke: (context, done) => {
    const { rbKey } = context.properties();
    if (!rbKey || rbKey.startsWith('${')){
      context.transition();
            done(new Error('The state is missing the rbKey property or it uses an invalid expression to pass the value.'));
    }
    //detect user locale. If not set, define a default
    const locale  = context.getVariable('profile.locale') ? 
      context.getVariable('profile.locale') : 'en-AU';  
    const jsLocale     = locale.replace('_','-');
    //when profile languageTag is set, use it. If not, use profile.locale
    const languageTag = context.getVariable('profile.languageTag')?
                      context.getVariable('profile.languageTag') : jslocale;
   /* =============================================================
      Determine the current date in local format and 
      the day name for the locale
      ============================================================= */
    var now          = new Date();
    var dayTemplate  = new Intl.DateTimeFormat(languageTag,
      { weekday: 'long' });
    var dayOfWeek    = dayTemplate.format(now);
    var dateTemplate = new Intl.DateTimeFormat(languageTag, 
      { year: 'numeric', month: 'long', day: 'numeric'});
    var dateToday    = dateTemplate.format(now);

   /* =============================================================
      Use the context.translate() method to create the ${Freemarker} 
      template that's evaluated when the reply() is flushed to the 
      client.
      ============================================================= */
    const fmTemplate = context.translate(rbKey, dateToday, dayOfWeek );

    context.reply(fmTemplate)
                .transition()
                .logger().info('INFO : Generated FreeMarker => ' 
                + fmTemplate);
    done();  
  }
};
Ensure the Component Works in Digital Assistants

In a digital assistant conversation, a user can break a conversation flow by changing the subject. For example, if a user starts a flow to make a purchase, they might interrupt that flow to ask how much credit they have on a gift card. We call this a non sequitur. To enable the digital assistant to identify and handle non sequiturs, call the context.invalidInput(payload) method when a user utterance response is not understood in the context of the component.

In a digital conversation, the runtime determines if an invalid input is a non sequitur by searching for response matches in all skills. If it finds matches, it reroutes the flow. If not, it displays the message, if provided, prompts the user for input, and then executes the component again. The new input is passed to the component in the text property.

In a standalone skill conversation, the runtime displays the message, if provided, prompts the user for input, and then executes the component again. The new input is passed to the component in the text property.

This example code calls context.invalidInput(payload) whenever the input doesn’t convert to a number.

"use strict"
 
module.exports = {
 
    metadata: () => ({
        "name": "AgeChecker",
        "properties": {
            "minAge": { "type": "integer", "required": true }
        },
        "supportedActions": [
            "allow",
            "block",
            "unsupportedPayload"
        ]
    }),
 
    invoke: (context, done) => {
        // Parse a number out of the incoming message
        const text = context.text();
        var age = 0;
        if (text){
          const matches = text.match(/\d+/);
          if (matches) {
              age = matches[0];
          } else {
              context.invalidUserInput("Age input not understood. Please try again");
              done();
              return;
          }
        } else {
          context.transition('unsupportedPayload");
          done();
          return;
        }
 
        context.logger().info('AgeChecker: using age=' + age);
 
        // Set action based on age check
        let minAge = context.properties().minAge || 18;
        context.transition( age >= minAge ? 'allow' : 'block' );
 
        done();
    }
};

Here’s an example of how a digital assistant handles invalid input at runtime. For the first age response (twentyfive), there are no matches in any skills registered with the digital assistant so the conversation displays the specified context.invalidUserInput message. In the second age response (send money), the digital assistant finds a match so it asks if it should reroute to that flow.


Description of components-nonsequitur-conversation.png follows

You should call either context.invalidInput() or context.transition(). If you call both operations, ensure that the system.invalidUserInput variable is still set if any additional message is sent. Also note that user input components such as System.CommonResponse, System.Text, System.List, and System.ResolveEntities reset system.invalidUserInput.

Say, for example, that we modify the AgeChecker component as shown below, and call context.transition() after context.invalidInput().

if (matches) {  age = matches[0]; } else { 
      context.invalidUserInput("Age input not understood. Please try again"); 
      context.transition("invalid"); 
      context.keepTurn(true);
      done();
      return;
}

In this case, the data flow needs to transition back to askage so that the user gets two output messages – "Age input not understood. Please try again" followed by "How old are you?".

  askage:
    component: "System.Output"
    properties:
      text: "How old are you?"
    transitions:
      next: "checkage"
  checkage:
    component: "AgeChecker"
    properties:
      minAge: 18
    transitions:
      actions:
        allow: "crust"
        block: "underage"
        invalid: "askage"
Run the Component Service in a Development Environment

During the development phase, you can start a local service to expose the custom component package.

  1. From the top-level folder, open a terminal window and run these commands to start the service:
    npm install
    npm start
  2. To verify that the service is running, enter the following URL in a browser:
    localhost:3000/components

    The browser displays the component metadata.

  3. If you have direct Internet access, you can access the development environment from a skill:
    1. Install a tunnel, such as ngrok or Localtunnel.
    2. If you are behind a proxy, go to http://www.whatismyproxy.com/ to get the external IP address of your proxy, and then, in the terminal window that you will use to start the tunnel, enter these commands:
      export https_proxy=http://<external ip>:80
      export http_proxy=http://<external ip>:80
    3. Start the tunnel and configure it to expose port 3000.
    4. In Oracle Digital Assistant, go to the skill's Components Components icon tab and add an External component service with the metadata URL set to https://<tunnel-url>/components.
      You can use any value for the user name and password.
You can now add states for the service's components to the dialog flow and test them from the skill's Preview page.

Task 2: Deploy the Component Package to a Service

You can host the custom component package from a Digital Assistant embedded container, Oracle Cloud Infrastructure Functions, Mobile Hub, or an external Node.js server. For embedded component services, you deploy the package when you create the service. For Oracle Cloud Infrastructure Functions, external Node.js server, and Mobile Hub services, you must first deploy the package to the service, as described here, before you add it to a skill as a component service.

Deploy to a Node.js Server

To host a custom component package on an external Node.js server, use the bots-node-sdk pack --service express CLI to copy your component package folders and make a few changes that are specific to Express, then install the component package and start it on your server.

  1. From the custom component package's top-level folder (the one that contains the main.js file), type this command in a terminal window:

    bots-node-sdk pack --service express

    The command does the following:

    • Copies the files and subfolders to service-express-<package version>.
    • Adds an index.js service wrapper.
    • Creates an api.js file, which is an Express wrapper for main.js.
    • Modifies the package.json file to set the main file to index.js and add the dependencies.

    This step shows the basic CLI command. For more information, see https://github.com/oracle/bots-node-sdk/blob/master/bin/CLI.md.

  2. Run these commands:

    npm install
    
    npm start
Deploy to Oracle Cloud Infrastructure Functions

You can deploy your custom components to Oracle Cloud Infrastructure Functions.

Currently, Oracle Digital Assistant can't access entity event handlers in packages that you deploy to Oracle Cloud Infrastructure Functions.

Note

This feature is not available for Digital Assistant instances that are paired with a subscription to a Fusion-based Oracle Cloud Applications service, such as HCM Cloud or Sales Cloud.

Here are the high-level steps:

  1. Get Artifact Names and Permissions for Oracle Cloud Infrastructure Functions Deployment

  2. Set Up Your User Account for Oracle Functions

  3. Set Up Your Local Machine for Oracle Functions

  4. Modify the Custom Component Package for Oracle Functions

  5. Deploy the Custom Components to Oracle Cloud Infrastructure Functions

Get Artifact Names and Permissions for Oracle Cloud Infrastructure Functions Deployment

Before you can deploy custom components to Oracle Cloud Infrastructure Functions, you need to obtain the names of the artifacts that are used for deployment, and you need to verify that you have permission to use them.

To set up your instance for Oracle Cloud Infrastructure Functions deployment, your tenancy administrator completed the steps in Setup and Policies for Oracle Functions. As part of the process, they created the following artifacts. Ask your administrator for their names, which you'll need when you complete the steps in Set Up Your User Account for Oracle Functions:

  • The names of the region and compartment to use for your functions.

  • The name of the compartment for the function application's virtual network (VCN). Typically, this is the same compartment as the one used for functions.

  • The name of the VCN to use for the function application.

Also, ask your administrator to verify that you belong to a group that has the necessary permissions for function developers, which includes access to these artifacts.

Set Up Your User Account for Oracle Functions

Before you can deploy custom component packages to Oracle Cloud Infrastructure Functions, you must complete these steps in the Oracle Cloud Infrastructure Console:

Note

You'll need to know the name of the compartments and virtual network (VCN) to use and you'll need to belong to a group that allows function development as described in Get Artifact Names and Permissions for Oracle Cloud Infrastructure Functions Deployment.
  1. Sign into the Console and, in the top bar, select the region that the function-development compartment is in.

  2. You'll deploy to Oracle Cloud Infrastructure Functions through the Oracle Cloud Infrastructure Registry. If you don't already have a registry repository that you can use, then do the following to create one.

    1. Click Navigation menu icon on the top left to open the navigation menu, click Developer Services, click Container Registry, and then, in the List Scope section, select the compartment that's been set up for function development.

    2. Click Create Repository.

    3. Give the repository a name, and then click Create Repository.

  3. If you don't have a functions application for your custom component packages, you'll need to create one. From the Developer Services page, click Functions, and then click Create Application. Provide a name, select a VCN, select at least one subnet, and click Create.

    If you don't see any VCNs to choose from, you might not be in the correct region.

    There are limits to the number of applications and functions. For the default limits, see Functions Limits in Oracle Cloud Infrastructure Documentation .

  4. On the Applications page, click the application that you use for function deployment, click Getting Started in the Resources section, and then click Local Setup.

    As shown in the following screenshot, the page displays several commands that you'll need to use to set up your local computer and to deploy your custom component package. Copy and save the commands for steps 3 through 7.

    You'll use these later after you've installed the required software on your local machine and are ready to deploy your custom components. Alternatively, bookmark this page so that you can return to it when you need to use the commands.

    Don't run these commands now. Just copy them.


    Description of fn-local-setup-commands.png follows

  5. In your copied command that looks similar to the following, change [OCIR-REPO] to the name of your registry repository.

    fn update context registry phx.ocir.io/devdigital/[OCIR-REPO]
  6. Click the Profile icon in the top-right corner, and then click User Settings to go to the User Details page.

  7. In the next step you'll create a PEM file that you need to store in a .oci folder on your local machine. If your home folder on your local machine doesn't have this directory, create one from a terminal window.

    • Linux and Mac:

      cd ~
      mkdir .oci
    • Windows:

      cd C:\Users\<your-user-name>
      mkdir .oci
  8. You need a public and private PEM file for secure access. If you haven't set one up for your user account yet, then, from User Details in the Console, click API Keys from the Resources section, and then click Add API Key.

    Save the private key file (the PEM file) to the .oci directory in your home folder.

  9. Make a note of the Fingerprint that's associated with the API key. When you have multiple API keys, you must know which fingerprint to use for each private PEM file.
  10. If you haven't already set up a config file for the fingerprint on your local machine, then, from the API Keys section, do these steps:

    1. Click More in the row for your API key's fingerprint and then click View Configuration file.

    2. Copy the Configuration File Preview content.

    3. In the .oci folder on your local machine (the same folder that you saved your private key file in), create a file named config and paste the copied contents into the file.

  11. In the config file, change the key_file property to point to the location of your private PEM file. For example: key_file=/home/joe/.oci/my-private.pem

  12. If you don't have an auth token, click Auth Tokens in the Resources menu, and then click Generate Token. Copy the auth token immediately to a secure location from where you can retrieve it later because you won't see the auth token again in the console. You use the auth token as a password when you sign in to push your custom component package to the Oracle Infrastructure registry for deployment.

Set Up Your Local Machine for Oracle Functions

You'll need to install cURL, the OCI command line interface (CLI), Fn, and Docker on your local machine to enable deployment to Oracle Cloud Infrastructure Functions. If your machine runs on Windows, then you must do one of the following options to use Fn:

To set up your local machine:

  1. (Windows on VM only) If you want to use a Linux guest on Oracle VM VirtualBox to deploy your custom component package to Oracle Cloud Infrastructure Functions, follow these steps:

    1. Install VirtualBox from https://www.virtualbox.org/.

    2. Download a Linux ISO. For example, to get the ISO for Ubuntu-Mate, go to https://ubuntu-mate.org/download/ and click 64-bit PCs/Macs.

    3. In VirtualBox, create a virtual machine from the ISO. You can find instructions for creating a Ubunto-Mate virtual machine at https://itsfoss.com/install-linux-in-virtualbox/. This will be your Linux guest.

    4. Start the Linux guest.

    5. From a terminal window, run this command:

      sudo apt-get update

      This updates the package lists for new packages and packages that need upgrading.

      Tip:

      To open a terminal window in Ubuntu, press Ctrl-Alt-T.
    6. To be able to do things like copy and paste in a terminal window, you'll need the guest additions. Download http://download.virtualbox.org/virtualbox/<release>/VBoxGuestAdditions_<release>.iso and install and configure the additions using the instructions at https://itsfoss.com/virtualbox-guest-additions-ubuntu/

      Be sure to configure configured Devices > Drag and Drop to bidirectional.

    7. Enter this command in a terminal window to install node.js and npm on the guest.

      sudo apt install npm
    8. Drag the .oci folder in your home directory on your local machine into the home folder on the Linux guest.

      Because it's a hidden file, you must press Ctrl-H or select View > Show Hidden Files in the home folder to see it.

    9. From the .oci folder on the Linux guest, open the config file and change key_file to point to the location of the file in your Linux guest. For example: key_file=/home/joe/.oci/my-private.pem

    10. Complete the remaining steps in this topic from the Linux guest.

  2. (Mac only) If you haven't already, install Homebrew to enable you to install cURL, OCI CLI, and Fn. See https://docs.brew.sh/Installation. Alternatively, you can use the equivalent MacPorts commands.
  3. If your Internet access is through a VPN, then you might need to set up proxies. For example:

    export http-proxy = http://<external_ip>:80
    export https-proxy = http://<external_ip>:80
    export no_proxy = localhost,127.0.0.1,<list>
    export noproxy = localhost,127.0.0.1,<list>
    export no_proxy = localhost,127.0.0.1,<list>
    # Example for apt
    nano /etc/apt/apt.conf
    Acquire::http::Proxy "http://<external_ip>:80";
    Acquire::https::Proxy "http://<external_ip>:80";
  4. Run the appropriate command to bring the packages up to date.

    • Linux:

      sudo apt update && sudo apt upgrade
    • Mac:

      brew update && brew upgrade
  5. (Linux only) You'll use cURL to install OCI and Fn. Enter this commeand in a terminal window. The last statement is to verify that it installed successfully.

    sudo apt install curl
    curl --version
  6. Fn uses the OCI CLI to deploy the custom components to Oracle Cloud Infrastructure Functions. Run the appropriate command to install the CLI, and accept all defaults.

    • Linux:

      bash -c "$(curl -L https://raw.githubusercontent.com/oracle/oci-cli/master/scripts/install/install.sh)"
    • Mac:

      brew update && brew install oci-cli
    • Windows (if using Linux subsystem on Windows): Follow the Windows steps in Quickstart in Oracle Cloud Infrastructure Documentation.
  7. In Set Up Your User Account for Oracle Functions, you created a config file. You now need to configure the CLI to use that file. Open a new terminal window, run this command, provide the location of your config file, and then enter n for the remaining questions (your config file already has the necessary settings).

    oci setup config

    For example:

    $ oci setup config
        This command provides a walkthrough of creating a valid CLI config file.
        ...
    
    Enter a location for your config [/home/joe/.oci/config]:
    Config file: /home/joe/.oci/config already exists. Do you want add a profile here? (If no, you will be prompted to overwrite the file) [Y/n]: n
    File: /home/joe/.oci/config already exists. Do you want to overwrite (Removes existing profiles!!!)? [y/N]: n
  8. You need Docker 17.10.0-ce or later to push the custom component package to the registry.

    See https://docs.docker.com/engine/install/linux-postinstall/ if you don’t want to preface the docker command with sudo.

  9. If you are using VPN, then follow the instructions at https://docs.docker.com/network/proxy/

    If you are using Linux subsystem on Windows, you can set the proxies from Resources page in the Docker Desktop Settings.

  10. Ensure that Docker is running. You can't start Fn, which you install next, if Docker isn't running.

  11. You'll use Fn, which is a lightweight Docker-based serverless-functions platform, to configure the context and deploy the package. If you haven't installed it already, follow the instructions for installing Fn, starting the Fn server, and testing the installation at https://fnproject.io/tutorials/install/

    You don't need to configure the context or set the registry at this time. You'll do this when you complete the steps in Deploy the Custom Components to Oracle Cloud Infrastructure Functions.

Modify the Custom Component Package for Oracle Functions

Before you can deploy a custom component package to Oracle Cloud Infrastructure Functions, you'll need to add func.js and func.yaml files, add a developer dependency for the fnproject FDK, and install the FDK.

Note

(Windows VM only) If you are using a Linux guest, complete these steps on your local machine and then use drag-and-drop to copy the component package to your Linux guest. Alternatively, install node.js and the Bots Node SDK, as described in Step 1: Install the Software for Building Custom Components, on your Linux guest before you do the steps.
  1. If you used the bots-node-sdk init command to create your custom component package, it may have created a file named Dockerfile in the top folder. If so, you must delete it. Otherwise, your deployment will fail.

  2. In the top folder for your custom component package (the folder that contains main.js), create a file named func.js, and then add the following code. This is the file that Oracle Cloud Infrastructure Functions will invoke.

    /***
     
      This function handles an invocation that sets the "Oracle-Bots-Fn-Path" header to determine which component to invoke or if metadata should be returned.
     
    ***/
     
    const fdk = require('@fnproject/fdk');
    const OracleBotLib = require('@oracle/bots-node-sdk/lib');
    const path = require("path");
     
    const BOTS_FN_PATH_HEADER = "Oracle-Bots-Fn-Path";
    const METADATA_PATH = "metadata";
    const COMPONENT_PREFIX = "components/";
     
    let shell;
    let componentsRegistry;
     
    const getComponentsRegistry = function (packagePath) {
        let registry = require(packagePath);
        if (registry.components) {
            return OracleBotLib.ComponentRegistry.create(registry.components, path.join(process.cwd(), packagePath));
        }
        return null;
    }
     
    componentsRegistry = getComponentsRegistry('.');
    if (componentsRegistry && componentsRegistry.getComponents().size > 0) {
        shell = OracleBotLib.ComponentShell({logger: console}, componentsRegistry);
        if (!shell) {
            throw new Error("Failed to initialize Bots Node SDK");
        }
    } else {
        throw new Error("Unable to process component registry because no components were found in package: " + packagePath);
    }
     
    const _handle = function (input, ctx) {
        let botsFnPath = ctx.getHeader(BOTS_FN_PATH_HEADER);
        if (!botsFnPath) {
            throw new Error("Missing required header " +  BOTS_FN_PATH_HEADER);
        } else if (botsFnPath === METADATA_PATH) {
            return shell.getAllComponentMetadata();
        } else if (botsFnPath.startsWith(COMPONENT_PREFIX)) {
            let componentName = botsFnPath.substring(COMPONENT_PREFIX.length);
            if (!componentName) {
                throw new Error("The component name is missing from the header " + BOTS_FN_PATH_HEADER + ": " + botsFnPath);
            }
            return new Promise((resolve) => {
                let callback = (err, data) => {
                    if (!err) {
                        resolve(data);
                    } else {
                        console.log("Component invocation failed", err.stack);
                        throw err;
                    }
                };
                shell.invokeComponentByName(componentName, input, {logger: () => console}, callback);
                });
        }
    };
     
    fdk.handle(function (input, ctx) {
        try {
            return _handle(input, ctx);
        } catch (err) {
            console.log("Function failed", err.stack);
            throw err;
        }
    });
  3. In the same folder, create a file named func.yaml and then add the following content:

    schema_version: 20180708
    name: <custom component package name>
    version: 0.0.1
    runtime: [node11|node14]
    build_image: [fnproject/node:11-dev|fnproject/node:14-dev]
    run_image: [fnproject/node:11|fnproject/node:14]
    entrypoint: node func.js
  4. In the name property, change <custom component package name> to the name of your custom component package, and then save your changes. The name is typically the same as the name that you specify in the package.json file.

    The name should be no more than 255 characters and contain only letters, numbers, _, and -.

  5. Set these properties:

    • runtime: The Node language and version. Specify node11 or node14.

    • build_image: The build-time base image that contains the language-specific libraries and tools to build executable functions. Specify fnproject/node:11-dev or fnproject/node:14-dev.

    • run_image: The runtime base image that provides the language-specific runtime environment in which to run executable functions. Specify fnproject/node:11 or fnproject/node:14.

  6. In a terminal window, change to the package's top folder and enter this command to install the FDK and to add it as a package dependency in the package.json file:

    npm install --save-dev @fnproject/fdk
  7. (Optional – Linux, Mac, and Linux subsystem on Windows only) Run this command to install package dependencies:

    npm install

    Note that if the node_modules folder doesn't exist, then the fn deploy command that you do later will invoke npm install for you.

  8. (Windows VM only) Complete these steps to copy your custom component code to your Linux guest for deployment:

    1. Drag-and-drop the top level folder to the Linux guest.

    2. In a terminal window, change to the top-level folder (the one that contains main.js) and type this command to add execute permissions for the folder.

      chmod 755 components
    3. Delete the node_modules folder to ensure that you don't have any platform-dependent node modules.

    4. (Optional) Run this command to reinstall the node module dependencies.

      npm install

      Note that if the node_modules folder doesn't exist, then the fn deploy command that you run later will invoke npm install for you.

You are now ready to complete the steps in Deploy the Custom Components to Oracle Cloud Infrastructure Functions.

Deploy the Custom Components to Oracle Cloud Infrastructure Functions

After you create the func.js file, add fnproject to the development dependencies, and install the dependencies as described in Modify the Custom Component Package for Oracle Functions, you are ready to deploy a Docker image of the component package to Oracle Cloud Infrastructure Functions.

When you completed the steps in Set Up Your User Account for Oracle Functions, you copied commands from the Local Setup on the Getting Started page for steps 3 through 7. You'll use these copied commands to deploy your custom components.

If you're using Cloud Shell, then use the similar commands shown in Cloud Shell Setup instead.

To deploy the custom components:

  1. Ensure that Docker and the Fn server are running.

  2. In a terminal window, change to the top directory for your custom component package and enter your equivalent copied commands to configure the context.

    fn create context <context> --provider oracle
    fn use context <context>
    fn update context oracle.compartment-id <compartment-ocid>
    fn update context api-url https://functions.<region-identifier>.oci.oraclecloud.com

    You don't have to run these commands again until you need to change the context configurations.

    If you get the error "Fn: error replacing file with tempfile" when you change the context, then manually edit ~/.fn/config.yaml and change the context in that file.

  3. If your config file has multiple profiles, enter this command to point to the profile that you created in Set Up Your User Account for Oracle Functions.

    fn update context oracle.profile <profile-name>
  4. To point the registry repository that you created in Set Up Your User Account for Oracle Functions, enter your copied command that's equivalent to the following. If you haven't already, change [OCIR-REPO] to the name of your repository.

    fn update context registry <region-key>.ocir.io/<tenancy-namespace>/[OCIR-REPO]

    You don't have to run this command again until you need to change the repository configuration.

  5. If you haven't signed into Docker in your current session, run the copied command that's equivalent to the one shown here.

    When it prompts you for a password, enter your auth token, which is the token that you obtained while completing the steps in Set Up Your User Account for Oracle Functions.

    docker login -u '<tenancy-namespace>/<user-name>' <region-key>.ocir.io
  6. To deploy the custom components, run this command:

    fn deploy --app <application>

    If you see the following message, open the .oci/config file and verify that fingerprint shows the correct fingerprint for the specified key_file. If not, go to your user settings in the Console, click API Keys, view the configuration file for the correct fingerprint, and then replace the content of your config file with the displayed content.

    Fn: Service error:NotAuthenticated. The required information to complete
    authentication was not provided or was incorrect.. 
    http status code: 401.

Your custom components are ready to use in a skill as described in Add Oracle Function Service.

Deploy to Mobile Hub

To host a custom component package in Mobile Hub, use the bots-node-sdk pack --service mobile-api CLI to copy your component package folders and make a few changes that are specific to Mobile Hub, including the RAML file. Then create the custom API from the RAML file, and upload a ZIP of the component package into the custom API.

  1. From the custom component package's top-level folder (the one that contains the main.js file), type this command in a terminal window:

    bots-node-sdk pack --service mobile-api

    The command does the following:

    • Copies the files and subfolders to service-mobile-api-<package version>.
    • Adds a component.service.raml file, which contains the necessary endpoints and operations.
    • Creates an api.js file, which is a Mobile Hub wrapper for main.js.
    • Modifies the package.json file to set the main file to api.js, set the dependencies, and add the Mobile Hub node configuration.

    This step shows the basic CLI command. For more information, see https://github.com/oracle/bots-node-sdk/blob/master/bin/CLI.md.

  2. Review the package.json file and verify that the package name conforms to the following Mobile Hub constraints. Modify the name as necessary to conform.
    • The name must consist only of letters (A-Za-z), numbers (0-9), and underscores (_).
    • The name must begin with a letter.
    • The name must be 100 characters or less.
  3. From the Mobile Hub APIs page, click New API > API, and then create the custom API by uploading the component.service.raml file.

  4. From the Security tab, switch off Login Required and then click Save.

  5. Zip up the service-mobile-api-<package version> folder, and then upload the ZIP file from the custom API’s Implementation tab.

  6. From the Test page, invoke the GET request. The response should show the component metadata.

    Tip:

    If you get a status 500, and the error is that it can’t find a matching route definition, check your files for bad JavaScript syntax, as that is the typical cause.

Task 3: Add Component Package to a Skill

You add a component package to a skill by creating a component service. For component packages that you host on an external server, Oracle Cloud Infrastructure Functions, or on Oracle Mobile Hub, a component service is an active connection from the skill to host server. Alternatively, you can upload the component package and host it from your Oracle Digital Assistant instance. This is referred to as an embedded component service.

A component service has two functions:

  • It queries the component to get the package metadata, including the names of the components, their properties, and allowed actions for each one. After a service is added to the skill, you can see this information in the Components tab, which you access by clicking Components This is an image of the Components icon. in the left navbar. You can reference this page to get the component names, properties, and actions, which you will need to use the components in your dialog flow.

  • It allows the skill to invoke the components.

    The JSON payload of the call made by the Dialog Engine to the components includes input parameters, variable values, user-level context, and the user’s message text. The component returns the results by changing the values of existing variables or adding new ones (or both). The Dialog Engine parses the returned payload and proceeds.

To add a custom component package to a skill, go to the skill's Components This is an image of the Components icon. tab and click Add Service, which opens a dialog for configuring the service.

How you configure the service depends on where you are hosting the component package. These topics provide instructions for each type:

After you create the service, you can invoke the custom components from your dialog flow as described in Custom Components.

When you upload a package to the embedded container, Digital Assistant verifies that the package is valid, and can reject the package for these reasons:

  • There are JavaScript errors.

  • The package doesn't contain all the node module dependencies.

  • A component name has more than 100 characters, begins with System., or contains other than alphanumeric characters and underscores, then the service creation fails.

  • Your instance already has the maximum number of embedded component services.

  • The TGZ file is too large. This typically happens when the .npmignore file doesn't contain a *.tgz entry and therefore, every time you pack the files, a nested copy of the existing TGZ is added.

See Add Embedded Component Service for more information about these verification checks.

Add Embedded Component Service

If you want to host the custom component package from your Oracle Digital Assistant instance, complete these steps:

  1. Prepare the Package for an Embedded Container Service.

  2. Upload Package to Create an Embedded Component Service.

Prepare the Package for an Embedded Container Service

If you want to host the custom component package from Oracle Digital Assistant as an embedded component service, you must first pack the custom components into a TGZ file. Then, when you create the embedded component service, you upload this file.

This TGZ file, which you package using bots-node-sdk pack, must contain the assets and structure described in Task 1: Implement Custom Components. It also must contain all the node modules that it depends on (the bots-node-sdk pack does that for you).

Note

There's a limit to how many embedded custom component services an instance can have. If you don't know the limit, ask your service administrator to get the embedded-custom-component-service-count for you as described in View Service Limits in the Infrastructure Console. If you try to add a component service after you meet that limit, then the service creation fails.

To prepare a package for uploading to the embedded container service:

  1. Ensure that you have the latest version of the Bots Node.js command line tools.

    The embedded container requires that the TGZ file includes all dependencies. Earlier versions did not bundle the dependencies into the file. Now, the command that you'll use to create the TGZ file ensures that your package.json file contains a bundledDependencies node that lists all the dependent modules that need to be included in the TGZ file.

  2. In the directory that contains the main.js file, run the following command for each of the modules that your package depends on. You don't need to do this for devDependencies, such as the Bots Node SDK.

    This command adds the module to the node_modules folder and adds it as a dependency in package.json.

    npm install <module>

    If your package.json already names all the dependencies, then you can run npm install instead.

  3. Ensure that the top-level folder contains an .npmignore file that has a *.tgz entry. For example:

    *.tgz
    spec
    service-*

    Otherwise, when you pack the files into a TGZ file, you include the TGZ file that already exists in the top-level folder, and your TGZ file will double in size. After you pack the files a few times, the TGZ file will be to large to upload into the container.

  4. Run this command:

    bots-node-sdk pack

    This command validates the component package, updates it to include devDependencies if necessary, and then creates a TGZ file, which you'll upload when you create an embedded component service from the skill’s Components tab. Note that the files you've listed as dependencies are included as bundledDependencies, with the exception of the Bots Node SDK and Express, which are devDependencies.

Your package should be compatible with Node 14.17.0.

For more information about the pack command, see https://github.com/oracle/bots-node-sdk/blob/master/bin/CLI.md.

Upload Package to Create an Embedded Component Service

After you pack a custom component package into a TGZ file, you can upload it to create an embedded component service from the skill's Components tab.

To learn how to create the TGZ file, see Prepare the Package for an Embedded Container Service.

Note

When you upload the package to the embedded component service, it's deployed to Oracle Functions Service. If your instance is provisioned on the Oracle Cloud Platform (as all version 19.4.1 instances are), then the service is instead deployed within the Digital Assistant instance.

To create the embedded component service:

  1. From the skill, click Components Components icon.

  2. Click + Add Service button..

  3. Select Embedded Container.

  4. Click Upload a component package file and point to the TGZ file to upload, or drag the file to the Package File box.

  5. (Optional) If you want to send custom component context.logger() statements to the service's log, then switch Enable Component Logging to On. This switch is available only in instances of Oracle Digital Assistant that were provisioned on Oracle Cloud Infrastructure (sometimes referred to as the Generation 2 cloud infrastructure).

    You can see the log from the Components tab by clicking Diagnostics > View Logs.

    Note

    The skill keeps a log entry for two days. When you delete an embedded custom component service, the skill's log entries for that service are deleted.
  6. Click Create.

    Digital Assistant uploads the TGZ file and creates the embedded component service. During the upload, Digital Assistant verifies that the package is valid, and can reject the package for the reasons that are described later in this section.

    After you upload the TGZ file, the custom component service is built and its components are deployed. If the Components page displays an awaiting deployment message after you upload the TGZ file, it means that the service has been created, but is not yet available. When the service becomes available, the deployment metadata displays in place of the awaiting deployment message.

  7. Ensure that Service Enabled is switched to On.

During the upload, Digital Assistant might reject the package. Here are reasons for rejection and ways to resolve the issues.

  • JavaScript contains syntax errors: If a component's JavaScript has syntax errors, then that component is not added to the container, which results in this error message:

    Error Message: failed to start service built: Invalid component
          path:

    View the component files in an editor that detects syntax errors. Also, try hosting the package on a local server that sends error messages to a console log.

    Another reason for this message might be that the package doesn't contain all the node module dependencies. See the next item in this list.

  • Missing node modules: If the package doesn't contain all the node module dependencies, then you'll get the same error message as above:

    Error Message: failed to start service built: Invalid component
          path:

    To learn how to include node module dependencies, see Prepare the Package for an Embedded Container Service.

  • Component name is too long: If a component name has more than 100 characters, begins with System., or contains other than alphanumeric characters and underscores, then the service creation fails.

    Change the name in the component's JavaScript, repackage, and upload again.

  • Exceeded component service limit: If your instance already has the maximum number of embedded custom component services, then the service creation fails. Ask your service administrator for the embedded-custom-component-service-count limit as described in View Service Limits in the Infrastructure Console.

    If you need to raise the limit, you can request an increase. See Requesting a Service Limit Increase.

  • TGZ file is too large: This typically happens when the .npmignore file doesn't contain a *.tgz entry and therefore, every time you pack the files, a nested copy of the existing TGZ is added.

    When the top-level folder contains an .npmignore file with *.tgz, the previous version of the TGZ file isn't included when you update the package.

If you want to send custom component context.logger() statements to the service's log, then switch Enable Component Logging to On. This switch is available only in instances of Oracle Digital Assistant that were provisioned on Oracle Cloud Infrastructure (sometimes referred to as the Generation 2 cloud infrastructure).

When Enable Component Logging is switched to On, you can click the Diagnostics button for the service to access view logs and crash reports to diagnose the problem.

  • Select View Logs to view messages that the custom component sends to context.logger(). This feature is available only in instances of Oracle Digital Assistant that were provisioned on Oracle Cloud Infrastructure (sometimes referred to as the Generation 2 cloud infrastructure). The Enable Component Logging switch must be On for the log to contain these messages.
  • Select View Crash Report to view details about what may have caused the container to crash.

After you create the service, you can invoke the custom components from your dialog flow as described in Custom Components.

Add Oracle Function Service

You can deploy your custom components to Oracle Cloud Infrastructure Functions and add them to a skill as an Oracle Function service.

To add an Oracle Function service:

  1. You'll need to know the function's URL. A user with function development privileges can get the URL from the Integration Console for you.

    1. Sign in to the Integration Console.
    2. Click Navigation menu icon on the top left to open the navigation menu, click Developer Services > Functions, and then select the compartment that's been set up for function development.

    3. Click the application.

    4. In the Functions section, click the More icon for your function and click Copy Invoke Endpoint. Skill developers need this value to add the custom component package as a component service in a skill.

  2. A skill developer adds the component service to a skill in Oracle Digital Assistant. Sign into Oracle Digital Assistant, open the skill and click Components Components icon.

  3. Click + Service button..

  4. Provide a name and description for the service.

  5. Select Oracle Function.

  6. In the URL text box, enter the invoke endpoint URL that you copied from the Developer Services > Functions page in the Infrastructure Console while completing the steps in Set Up Your User Account for Oracle Functions.

  7. Click Create.

    If you get a "Can't Create the Service" error, go to Developer Services > Functions in the Infrastructure Console, select the compartment, click the application, and click Logs in the Resources menu. Then enable the log for the function, retry the deployment, and check the logs (click the log name to see the log). To learn more about logs, see Storing and Viewing Function Logs in Oracle Cloud Infrastructure Documentation.

    If you see an error like this, it's because the folder doesn't have the right permissions. On your local machine, use chmod 775 to change the folder's permissions, then redeploy:

    "Error: EACCES: permission denied, stat
    '/function/components/hello.world.js.js'"

    If you see an error like this then, on your local machine, delete node_modules, run npm install and redeploy.

    "Error: Cannot find module '@fnproject/fdk'"
  8. Ensure that Service Enabled is switched to On.

After you create the service, you can invoke the custom components from your dialog flow as described in Custom Components.

Add External Component Service

You can host your custom components on your own Node.js server and add them to a skill as an external component service.

Tip:

You can use the external service option during development, as described in Run the Component Service in a Development Environment.

To add an external component service:

  1. From the skill, click Components Components icon.

  2. Click + Service button..

  3. Select External.

  4. In the Metadata URL text box, enter the The URL that points to the GET endpoint that returns the list of components.

  5. Enter the service's user name and password.

  6. Click Create.

  7. Ensure that Service Enabled is switched to On.

After you create the service, you can invoke the custom components from your dialog flow as described in Custom Components.

Add Mobile Hub Component Service

You can host your custom components from Oracle Mobile Hub, and add them to a skill as an Oracle Mobile Cloud component service. Custom components that are hosted on Mobile Hub can integrate with remote services using connectors that are controlled by a Mobile Hub backend and they have access to the Mobile Hub platform APIs.

Because the backend that hosts the custom code handles the authentication for the custom components, you need to refer to the backend’s Settings page to get the information that you need to complete the configuration.

To add a component service for the Mobile Hub backend:

  1. From the skill, click Components Components icon.

  2. Click + Service button..

  3. Select Oracle Mobile Cloud.

  4. Enter the unique identifier assigned to the Mobile Hub backend in the Backend ID field. This ID is passed in the REST header of every call from the skill.

  5. In the MetadataURL field, enter the /components endpoint from the custom code API. For example, http://<server>:<port>/mobile/custom/ccPackage/components.

  6. Choose Use Anonymous Access if the service allows anonymous login. If you choose this option, enter the anonymous key, which is a unique string that allows your app to access anonymous APIs without sending an encoded user name and password. The anonymous key is passed in their place. You can find the anonymous key on the backend's Settings page in Mobile Hub. (You may need to click Show.)

    If the component service requires a login (meaning no anonymous access), then enter the user name and password.

  7. If the service requires specific parameters, click Add HTTP Header and then define the key-value pairs for the headers.

  8. Click Create.

  9. Ensure that Service Enabled is switched to On.

After you create the service, you can invoke the custom components from your dialog flow as described in Custom Components.

Set the Read Timeout for Custom Components

By default, the wait time allowed between bytes when a custom component reads data is 60 seconds. To change this, open the skill and go to the Settings page. The Read Timeout field is at the bottom of the General tab. The maximum value is 300 seconds.

Export and Import a REST Service Endpoint

If you have registered REST service endpoints in one Oracle Digital Assistant instance, you can make those endpoints available in a separate instance by exporting them from the first instance and importing them into the other instance. Here are the steps:

  1. In the Digital Assistant where you have the registered the REST service, click Icon to open the side menu to open the side menu, select Settings, select API Services, and then click the REST Services tab.

  2. From the More menu, select Export All REST Services.

    (Alternatively, you export individual REST services by selecting the service and then selecting More > Export Selected REST Service.)

    You should see a file downloaded to your system.

  3. In the Digital Assistant instance where you want to import the services, go to the same page (Settings > API Services > REST Services), select More > Import REST Services, and select the file that you just exported from the first instance.