Integrate Einstein Bots into Any Channel Using the New SDK and Framework : Rajasekar Elango
by: Rajasekar Elango
blow post content copied from Salesforce Developers Blog
click here to view original post
Einstein Bots is a conversational chatbot solution that is powered by Salesforce’s Einstein AI Platform. Its job is to interact with customers and provide them with the information they need quickly without human intervention. It can also handles simple, repetitive tasks, freeing up agents to manage more complex cases.
In Spring ‘22, we released the Einstein Bots Platform API (Beta) to help customers leverage power of Einstein Bots on any digital channel. In Summer ’22, we have made the Java SDK and open-source Channel Connector available to simplify the bot developer experience. This gives you the tools you need to easily integrate Einstein Bots into any of your conversational channels on top of existing the digital engagement channels that are supported by Service Cloud.
In this post, we’ll look at how you can use the Einstein Bots Platform API, and we’ll also cover how to use the SDK and its benefits. Check out this previous blog post to get even more familiar with the Einstein Bots Platform API.
Using the Einstein Bots Platform API
The Einstein Bots Platform API is a REST API, and you can use it without the SDK. The Einstein Bots Platform API Client Guide provides instructions on how to integrate Einstein Bots with your channel using CURL or Postman. Let’s look at the actual Java code required to work with the Einstein Bots Platform API.
1. Create the JSON Web Token (JWT)
Einstein Bots require requests to be authenticated using OAuth. Any OAuth flows can be used to get the access token. Since this is a service-to-service integration, we will use the JWT bearer OAuth flow to mint the JWT and get the OAuth access token. Use your private key that you created in your connected app setup to create the algorithm for signing the JWT.
File f = new File(privateKeyFile); DataInputStream dis = new DataInputStream(new FileInputStream(f)); byte[] keyBytes = new byte[(int) f.length()]; dis.readFully(keyBytes); dis.close(); PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes); KeyFactory kf = KeyFactory.getInstance("RSA"); PrivateKey privateKey = kf.generatePrivate(spec); Map<String, Object> headers = new HashMap<String, Object>(); headers.put("alg", "RS256"); Algorithm algorithm = Algorithm.RSA256(null, (RSAPrivateKey) privateKey);
Then, create the JWT with appropriate values.
Instant now = Instant.now(); String jwt = JWT.create() .withHeader(headers) .withAudience(loginEndpoint) .withExpiresAt(Date.from(now.plus(jwtExpiryMinutes, ChronoUnit.MINUTES))) .withIssuer(connectedAppId) .withSubject(userId) .sign(algorithm);
2. Get the OAuth access token
Send an HTTP post request to the https://login.salesforce.com/
services/oauth2/token
endpoint with jwt
in the request body and using the appropriate HTTP headers.
// Create Http Post Form data. MultiValueMap<String, String> formData= new LinkedMultiValueMap<>(); formData.add("grant_type", "urn:ietf:params:oauth:grant-type:jwt-bearer"); formData.add("assertion", jwt); // Create Http Headers. HttpHeaders httpHeaders = new HttpHeaders(); httpHeaders.setContentType(MediaType.APPLICATION_FORM_URLENCODED); // Create Http Request. HttpEntity<Map> oAuthHttpRequest = new HttpEntity<>(formData, httpHeaders); // Post Request to OAuth Endpoint ResponseEntity<String> response = restTemplate .postForEntity(OAUTH_URL, oAuthHttpRequest, String.class);
Then, parse the response to get the access_token
:
ObjectNode node = new ObjectMapper().readValue(response.getBody(), ObjectNode.class); String token = node.get("access_token").asText();
Now, we have the token
required for authentication and are ready to make requests to the Einstein Bots Platform API.
3. Send a start chat session request
Send an HTTP post request to the https://<RUNTIME_BASE_URL>/
v5.0.0/bots/{botId}/sessions
endpoint with the authentication token
and orgId
in the HTTP headers. The RUNTIME_BASE_URL
can be obtained from the Bot Overview page documented in the client guide.
// Create Http Headers HttpHeaders requestHeaders = new HttpHeaders(); requestHeaders.setContentType(MediaType.APPLICATION_JSON); requestHeaders.setBearerAuth(token); requestHeaders.add("X-Org-Id", orgId); String message = "Hello"; // Create Request Body in TextMessage format defined in Schema. // We are using string concatenation here just for sake of simplicity. // You will likely create a DTO classes that can help // with JSON serialization/deserialization in real production code. String requestBody = "{\n" + " \"externalSessionKey\": \"" + UUID.randomUUID().toString + "\",\n" + " \"message\": {\n" + " \"text\": \"" + message + "\"\n" + " },\n" + " \"forceConfig\": {\n" + " \"endpoint\": \"" + forceConfigEndPoint + "\"\n" + " }\n" + "}"; // Create HTTP Request HttpEntity<String> httpRequest = new HttpEntity<>(requestBody, requestHeaders); // Create URL with URI format v5.0.0/bots/{botId}/sessions String url = RUNTIME_BASE_URL + "/v5.0.0/bots/" + botId + "/sessions"; // Send Start Chat Session Request ResponseEntity<String> startSessionResponse = restTemplate .postForEntity(url, httpRequest , String.class);
You can parse the response to show the bot’s message to the user depending on the channel. For this example, we will just output to console which will look like this:
{ "sessionId" : "9168d0c9-bbe2-4be1-a1a1-8a3902921e87", "botVersion" : "0X9SB00000007cb0AA", "messages" : [ { "id" : "c0a95f75-06b2-4269-b88d-c119e038781f", "schedule" : { "responseDelayMilliseconds" : 1200 }, "type" : "text", "text" : "Hi," }, { "id" : "d45cdaa8-b278-42d1-b954-53f4db9ea204", "schedule" : { "responseDelayMilliseconds" : 1200 }, "type" : "text", "text" : "I’m a demo bot for the user guide." }, { "id" : "4c484926-c6c5-4944-b650-cce011b0f3b6", "schedule" : { "responseDelayMilliseconds" : 1200 }, "type" : "text", "text" : "Choose one of the options below" }, { "id" : "d05f2b3a-2453-4acb-9c33-43774487c76c", "schedule" : { "responseDelayMilliseconds" : 1200 }, "type" : "choices", "widget" : "menu", "choices" : [ { "label" : "Order Status", "alias" : "1", "id" : "2319a485-5c5c-4c27-8239-abb8f7366ff6" }, { "label" : "Frequently Asked Questions", "alias" : "2", "id" : "664d72d9-2a54-477b-9f9b-da2d01c58552" }, { "label" : "Transfer To Agent", "alias" : "3", "id" : "dccd1287-8fc0-426f-ae0b-0a8b6a41b011" }, { "label" : "End Chat", "alias" : "4", "id" : "c359eaea-f981-49ba-a424-18b8b7572d20" } ] } ], "processedSequenceIds" : [ 0 ], "_links" : { "session" : { "href" : "https://runtime-api-na-west.stg.chatbots.sfdc.sh/v5.0.0/sessions/9168d0c9-bbe2-4be1-a1a1-8a3902921e87" }, "self" : { "href" : "https://runtime-api-na-west.stg.chatbots.sfdc.sh/v5.0.0/bots/0XxSB00000007UX0AY/sessions" }, "messages" : { "href" : "https://runtime-api-na-west.stg.chatbots.sfdc.sh/v5.0.0/sessions/9168d0c9-bbe2-4be1-a1a1-8a3902921e87/messages" } } }
Next, we will need to extract the sessionId
from the response to continue sending messages to the same session.
// Print Response Body that has response from Chatbot. System.out.println("Bot Start Session Response : " + startSessionResponse.getBody()); // Get SessionId from Response to send message to existing Chat Session. JsonNode responseNode = mapper .readValue(startSessionResponse.getBody(), JsonNode.class); String sessionId = responseNode.get("sessionId").asText();
As you can see, there is a lot of boilerplate code required to use Einstein Bots Platform API directly. To simplify the integration and reduce much of the boilerplate code, we created an Einstein Bots SDK for Java.
Using the Java SDK to simplify an Einstein Bots integration
The SDK is a wrapper around the Einstein Bots Platform API that simplifies the integration by providing added features, such as authorization support and session management. Let’s look at some code for implementing the same example using the Einstein Bots SDK.
1. Add a POM dependency
Find the latest einstein-bot-sdk-java
version from Maven Central and add this dependency to your pom.xml
.
<dependency> <groupId>com.salesforce.einsteinbot</groupId> <artifactId>einstein-bot-sdk-java</artifactId> <version>${einstein-bot-sdk-java-version}</version> </dependency>
2. Create a chatbot client
The chatbot client provides JwtBearerFlow
for OAuth, so create AuthMechanism
with appropriate parameters. Then, create BasicChatbotClient
with the auth mechanism and basePath
of the Bot runtime URL.
//Create JwtBearer Auth Mechanism. AuthMechanism oAuth = JwtBearerOAuth.with() .privateKeyFilePath(privateKeyFilePath) .loginEndpoint(loginEndpoint) .connectedAppId(connectedAppId) .connectedAppSecret(secret) .userId(userId) .build(); //Create Basic Chatbot Client BasicChatbotClient client = ChatbotClients.basic() .basePath(basePath) .authMechanism(oAuth) .build();
3. Send start chat session request
First, create a RequestConfig
with your botId
, orgId
, and forceConfigEndPoint
. You can refer to the client guide to find these values. Typically, you want to create a config once per bot in your org and reuse it for every request.
//Create Request Config RequestConfig config = RequestConfig.with() .botId(botId) .orgId(orgId) .forceConfigEndpoint(forceConfigEndPoint) .build();
Then, create a BotSendMessageRequest
with TextMessage
.
// We can use statically typed Java classes for Request Body. AnyRequestMessage message = new TextMessage() .text("Hello") .type(TextMessage.TypeEnum.TEXT) .sequenceId(System.currentTimeMillis()); BotSendMessageRequest botSendInitMessageRequest = BotRequest .withMessage(message) .build();
Use the startChatSession
method to start a session.
ExternalSessionId externalSessionKey = new ExternalSessionId(UUID.randomUUID().toString()); BotResponse resp = client .startChatSession(config, externalSessionKey, botSendInitMessageRequest); // Get SessionId from Response. String sessionId = resp.getResponseEnvelope().getSessionId();
Parse the response envelope and display the message to the user depending on the channel. The SDK will automatically deserialize the JSON to a Java model to make it easier. The code below shows how to parse the response as text for TextResponseMessage
and ChoiceResponseMessage
types. For all supported types and code, refer to the schema.
List<AnyResponseMessage> messages = resp.getResponseEnvelope().getMessages(); StringBuilder sb = new StringBuilder(); for(AnyResponseMessage message : messages){ if (message instanceof TextResponseMessage){ sb.append(((TextResponseMessage) message).getText()) .append("\n"); }else if (message instanceof ChoicesResponseMessage){ List<ChoicesResponseMessageChoices> choices = ((ChoicesResponseMessage) message) .getChoices(); for (ChoicesResponseMessageChoices choice : choices){ sb.append(choice.getAlias()) .append(".") .append(choice.getLabel()) .append("\n"); } } //Similarly handle other Response Message Types. } String responseMessageAsText = sb.toString(); System.out.println(responseMessageAsText);
The output will look like this:
Hi, I’m a demo bot for the user guide. Choose one of the options below 1.Order Status 2.Frequently Asked Questions 3.Transfer To Agent 4.End Chat
We have different Einstein Bots Platform API endpoints for continuing an existing session and ending a chat session. For completeness, let’s look at code examples for them.
4. Send a message to an existing chat session
The example code shows how to use the sendMessage
method to send a message to an existing open chat session.
// SDK also provides utility methods to create a text message. // Let's say, we want to respond to the menu choice with "Order Status". AnyRequestMessage userInputMessage = RequestFactory .buildTextMessage("Order Status"); // Build Bot Send Message Request with user's response message. BotSendMessageRequest botSendMessageRequest = BotRequest .withMessage(userInputMessage) .build(); //Create RuntimeSessionId with sessionId you got from start chat session Response. RuntimeSessionId runtimeSessionId = new RuntimeSessionId(sessionId); // Send a message to existing Session with sessionId BotResponse textMsgResponse = client .sendMessage(config, runtimeSessionId, botSendMessageRequest); System.out.println("Text Message Response :" + textMsgResponse);
We used TextMessage
in this example, but you can also send other types (e.g., ChoiceMessage
) supported by the Einstein Bot Runtime Open API Schema.
5. End a chat session
Finally, here is example code for ending the session using the endChatSession
method.
// Build Bot End Session Message Request BotEndSessionRequest botEndSessionRequest = BotRequest .withEndSession(EndSessionReason.USERREQUEST).build(); //Create RuntimeSessionId with sessionId you got from start chat session Response. RuntimeSessionId runtimeSessionId = new RuntimeSessionId(sessionId); // Send Request to End Chat session BotResponse endSessionResponse = client .endChatSession(config, runtimeSessionId, botEndSessionRequest); System.out.println("End Session Response :" + endSessionResponse);
Benefits of using the Einstein Bots SDK
The Einstein Bots SDK provides lots of great features that can save developers time.
- It abstracts out the details of loading private key and minting JWT.
- It abstracts out token exchange to get OAuth access token.
- It provides model classes with strict type checking.
- You don’t have to use convoluted JSON string serializing/deserializing of request/response bodies.
- We used
TextMessage
in this example. Similarly, model classes are available for all schema objects defined in Bot Runtime Open API Schema.
- The code is more readable due to the dsl-like methods.
- If you use
BasicChatbotClient
demonstrated in the blog, you will need to keep track of sessions and callstartChatSession
orsendMessage
method appropriately. Instead, useSessionManagedChatbotClient
, which eliminates thestartChatSession
method. It will automatically create a new session based on the user-providedExternalSessionId
. We will publish a follow-up blog post on usingSessionManagedChatbotClient
.
And there’s more: the Channel Connector framework
In addition to the Einstein Bots SDK, we also released the Einstein Bots Channel Connector framework to simplify building a channel connector service using Spring Boot. It auto-configures Spring beans for foundational services, such as caching, authentication, and metrics, and makes it ready to use out of the box.
The Channel Connector framework includes a working example application and a maven archetype for creating a new bot channel connector application.
The image below summarizes the tools that we are releasing and the benefits that they provide.
Where to go from here?
- Find the full code example for using the SDK in the GitHub repo
- Refer to the SDK and Channel Connector user guides for documentation
- Learn more from the GitHub README and source code:
- GitHub repo for the SDK
- GitHub repo for the Channel Connector Framework
About the author
Rajasekar Elango is a Principal Software Engineer at Salesforce working on the Einstein Bots Platform. You can follow him on LinkedIn or Twitter.
The post Integrate Einstein Bots into Any Channel Using the New SDK and Framework appeared first on Salesforce Developers Blog.
May 25, 2022 at 08:40PM
Click here for more details...
=============================
The original post is available in Salesforce Developers Blog by Rajasekar Elango
this post has been published as it is through automation. Automation script brings all the top bloggers post under a single umbrella.
The purpose of this blog, Follow the top Salesforce bloggers and collect all blogs in a single place through automation.
============================
Post a Comment