Created
April 12, 2021 11:06
-
-
Save savelee/89be48e8c603e4709a67521efb47611e to your computer and use it in GitHub Desktop.
google assistant - for CX agents
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"actions": [ | |
{ | |
"description": "Default Welcome Intent", | |
"name": "MAIN", | |
"fulfillment": { | |
"conversationName": "welcome" | |
}, | |
"intent": { | |
"name": "actions.intent.MAIN", | |
"trigger": { | |
"queryPatterns":["talk to CCAIDemo"] | |
} | |
} | |
}, | |
{ | |
"description": "Dialogflow Intents", | |
"name": "TEXT", | |
"fulfillment": { | |
"conversationName": "dialogflow_intent" | |
}, | |
"intent": { | |
"name": "actions.intent.TEXT", | |
"trigger": { | |
"queryPatterns": [] | |
} | |
} | |
} | |
], | |
"conversations": { | |
"welcome": { | |
"name": "welcome", | |
"url": "https://5d9233546d75.ngrok.io/aog/" | |
}, | |
"dialogflow_intent": { | |
"name": "dialogflow_intent", | |
"url": "https://5d9233546d75.ngrok.io/aog/" | |
} | |
}, | |
"locale": "en" | |
} | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* @license | |
* Copyright 2021 Google LLC | |
* | |
* Licensed under the Apache License, Version 2.0 (the "License"); | |
* you may not use this file except in compliance with the License. | |
* You may obtain a copy of the License at | |
* | |
* https://www.apache.org/licenses/LICENSE-2.0 | |
* | |
* Unless required by applicable law or agreed to in writing, software | |
* distributed under the License is distributed on an "AS IS" BASIS, | |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
* See the License for the specific language governing permissions and | |
* limitations under the License. | |
* ============================================================================= | |
*/ | |
import * as actionsSdk from 'actions-on-google'; | |
import { dialogflowv2beta1 } from './dialogflow-v2beta1'; | |
import { dialogflowcx } from './dialogflow-cx'; | |
import { dialogflowcxv3beta1 } from './dialogflow-cxv3beta1'; | |
import { pubsub } from './pubsub'; | |
import { debug } from './debug'; | |
import * as dotenv from 'dotenv'; | |
dotenv.config(); | |
const df = process.env.DF || 'v2beta1'; | |
export class Aog { | |
private assistant: any; | |
private dialogflow: any; | |
constructor() { | |
this.assistant = actionsSdk.actionssdk(); | |
if(df === 'cx') { | |
this.dialogflow = dialogflowcx; | |
} else if(df === 'cxv3beta1') { | |
this.dialogflow = dialogflowcxv3beta1; | |
} else { | |
this.dialogflow = dialogflowv2beta1; | |
} | |
} | |
/** | |
* Register handlers for Actions SDK intents | |
*/ | |
public registerHandlers(expressApp): void{ | |
this.assistant.intent('actions.intent.MAIN', async (conv) => { | |
return this.detectIntentEvent(conv, 'WELCOME'); | |
}); | |
this.assistant.intent('actions.intent.TEXT', async (conv, input) => { | |
if (input === 'bye' || input === 'goodbye') { | |
// TODO get this from the Dialogflow end | |
return conv.close('See you later!'); | |
} | |
// Pass everything to Dialogflow | |
debug.log(input); | |
return this.detectIntentText(conv, input) | |
}); | |
expressApp.post('/aog/', this.assistant) | |
} | |
createRichMessages(conv, response){ | |
const msg = response.responseMessages[0].text.text[0]; | |
conv.ask(msg); | |
} | |
async detectIntentEvent(conv: any, eventName: string){ | |
const aogResponse = await this.dialogflow.detectIntentEvent(eventName); | |
aogResponse.platform = 'googleassistant'; | |
console.log(aogResponse); | |
pubsub.pushToChannel(aogResponse); | |
return this.createRichMessages(conv, aogResponse); | |
} | |
async detectIntentText(conv: any, query: string) { | |
const aogResponse = await this.dialogflow.detectIntentText(query); | |
aogResponse.platform = 'googleassistant'; | |
pubsub.pushToChannel(aogResponse); | |
return this.createRichMessages(conv, aogResponse); | |
} | |
} | |
export let aog = new Aog(); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* @license | |
* Copyright 2021 Google LLC | |
* | |
* Licensed under the Apache License, Version 2.0 (the "License"); | |
* you may not use this file except in compliance with the License. | |
* You may obtain a copy of the License at | |
* | |
* https://www.apache.org/licenses/LICENSE-2.0 | |
* | |
* Unless required by applicable law or agreed to in writing, software | |
* distributed under the License is distributed on an "AS IS" BASIS, | |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
* See the License for the specific language governing permissions and | |
* limitations under the License. | |
* ============================================================================= | |
*/ | |
import * as df from '@google-cloud/dialogflow-cx'; | |
import * as dotenv from 'dotenv'; | |
import * as uuid from 'uuid'; | |
import { debug } from './debug'; | |
import { BotResponse } from './dialogflow-bot-responses'; | |
const struct = require('./structjson'); | |
dotenv.config(); | |
const langCode = process.env.LANGUAGE_CODE || 'en-US'; | |
const encoding = process.env.ENCODING || 'AUDIO_ENCODING_LINEAR_16'; | |
const sampleRateHertz = parseInt(process.env.SAMPLE_RATE_HERTZ, 10) || 16000; | |
const location = process.env.LOCATION || 'global'; | |
const agentId = process.env.AGENT_ID; | |
const projectId = process.env.GCP_PROJECT; | |
export interface QueryInputCX { | |
text?: { | |
text: string, | |
} | |
intent?: { | |
intent: string // projects/<Project ID>/locations/<Location ID>/agents/<Agent ID>/intents/<Intent ID>. | |
}, | |
event?: { | |
event: string | |
}, | |
audio?: { | |
config: { | |
audioEncoding: any, | |
sampleRateHertz: number | |
}, | |
audio?: any | |
}, | |
dtmf?: { | |
digits: string, | |
finish_digit: string | |
}, | |
languageCode: string | |
} | |
export class DialogflowCX { | |
protected sessionClient: df.SessionsClient | df.v3beta1.SessionsClient; | |
private projectId: string; | |
private agentId: string; | |
private location: string; | |
private sessionId: string; | |
private sessionPath: string; | |
constructor() { | |
this.projectId = projectId; | |
this.agentId = agentId; | |
this.location = location; | |
this.sessionId = uuid.v4(); | |
this.sessionClient = new df.SessionsClient( | |
{ apiEndpoint: `${this.location}-dialogflow.googleapis.com` } | |
); | |
this.sessionPath = this.sessionClient.projectLocationAgentSessionPath( | |
this.projectId, | |
this.location, | |
this.agentId, | |
this.sessionId | |
); | |
} | |
detectIntentText(query: string, lang = langCode, contexts?: Array<string>) { | |
const qInput:QueryInputCX = { | |
text: { | |
text: query, | |
}, | |
languageCode: lang | |
}; | |
return this.detectIntent(qInput, query, contexts); | |
} | |
detectIntentEvent(eventName: string, lang = langCode) { | |
const qInput:QueryInputCX = { | |
event: { | |
event: eventName, | |
}, | |
languageCode: lang | |
}; | |
return this.detectIntent(qInput, eventName); | |
} | |
detectIntentAudioStream(stream, lang = langCode){ | |
const qInput:QueryInputCX = { | |
audio: { | |
config: { | |
audioEncoding: encoding, | |
sampleRateHertz, | |
} | |
}, | |
languageCode: lang | |
}; | |
return this.detectIntent(qInput, 'audio'); | |
} | |
async detectIntent(qInput:QueryInputCX, input?: string, contexts?: Array<string>) { | |
const request = { | |
session: this.sessionPath, | |
queryInput: qInput, | |
queryParams: null | |
}; | |
if (contexts && contexts.length > 0) { | |
request.queryParams.contexts = contexts; | |
} | |
var botResponse; | |
try { | |
const [response] = await this.sessionClient.detectIntent(request); | |
debug.log(response); | |
botResponse = this.beautifyResponses(response, input); | |
} catch(e) { | |
debug.error(e); | |
botResponse = this.beautifyResponses(null, input, e); | |
} | |
debug.log(botResponse); | |
return botResponse; | |
} | |
beautifyResponses(response: any, input: string, e?: any): BotResponse{ | |
var botResponse: BotResponse; | |
const dialogflowConfig = { | |
sessionId: this.sessionId, | |
sessionPath: this.sessionPath, | |
projectId: this.projectId, | |
agentId: this.agentId, | |
location: this.location, | |
dateTimeStamp: new Date().getTime()/1000, | |
text: input, // in case DF doesn't respond anything, we can still capture these | |
languageCode: langCode, // in case DF doesn't respond anything, we can still capture these | |
} | |
if(e) { | |
debug.error(e); | |
dialogflowConfig['error'] = e.message; | |
} | |
if(response && response.queryResult){ | |
var dialogflowResponses = { | |
languageCode: response.queryResult.languageCode, // override | |
sentiment: response.queryResult.sentimentAnalysisResult, | |
currentPage: response.queryResult.currentPage, | |
query: response.queryResult.query, | |
text: response.queryResult.text, // override | |
responseMessages: response.queryResult.responseMessages, | |
webhookPayloads: response.queryResult.webhookPayloads, | |
webhookStatuses: response.queryResult.webhookStatuses, | |
outputAudio: response.outputAudio, | |
responseId: response.responseId | |
} | |
if(response.queryResult.intent){ | |
const intentDetectionObj = { | |
intentDetection: { | |
intent: { | |
displayName: response.queryResult.intent.displayName, | |
name: response.queryResult.intent.name, | |
parameters: response.queryResult.intent.parameters, | |
priority: response.queryResult.intent.priority, | |
trainingPhrases: response.queryResult.intent.trainingPhrases, | |
isFallback: response.queryResult.intent.isFallback, | |
intentDetectionConfidence: response.queryResult.intentDetectionConfidence | |
} | |
} | |
}; | |
dialogflowResponses = {...dialogflowResponses, ...intentDetectionObj } | |
} | |
botResponse = {...dialogflowConfig, ...dialogflowResponses } | |
} else { | |
botResponse = dialogflowConfig; | |
} | |
return botResponse; | |
} | |
} | |
export let dialogflowcx = new DialogflowCX(); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* @license | |
* Copyright 2021 Google LLC | |
* | |
* Licensed under the Apache License, Version 2.0 (the "License"); | |
* you may not use this file except in compliance with the License. | |
* You may obtain a copy of the License at | |
* | |
* https://www.apache.org/licenses/LICENSE-2.0 | |
* | |
* Unless required by applicable law or agreed to in writing, software | |
* distributed under the License is distributed on an "AS IS" BASIS, | |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
* See the License for the specific language governing permissions and | |
* limitations under the License. | |
* ============================================================================= | |
*/ | |
import * as http from 'http'; | |
import { Server } from 'socket.io'; | |
import * as express from 'express'; | |
import * as cors from 'cors'; | |
import { aog } from './aog'; | |
import { debug } from './debug'; | |
import * as dotenv from 'dotenv'; | |
dotenv.config(); | |
const projectId = process.env.GCP_PROJECT; | |
const port = process.env.PORT; | |
export class App { | |
public static readonly PORT:number = parseInt(port, 10); | |
private app: express.Application; | |
private server: http.Server; | |
private io: Server; | |
constructor() { | |
this.createApp(); | |
this.createServer(); | |
this.sockets(); | |
this.listen(); | |
} | |
private createApp(): void { | |
this.app = express(); | |
this.app.use(cors()); | |
this.app.use(express.urlencoded({ | |
extended: true | |
})); | |
this.app.use(express.json()); | |
this.app.use(function(req: any, res: any, next: any) { | |
res.setHeader('Strict-Transport-Security', 'max-age=31536000; includeSubDomains'); | |
next(); | |
}); | |
this.app.get('/', function(req, res) { | |
res.json({success: 'true'}); | |
}); | |
aog.registerHandlers(this.app); | |
} | |
private createServer(): void { | |
this.server = http.createServer(this.app); | |
} | |
private listen(): void { | |
this.server.listen(App.PORT, () => { | |
debug.log('Running chat server on port ' + App.PORT); | |
debug.log('Project ID: ' + projectId); | |
}); | |
} | |
public getApp(): express.Application { | |
return this.app; | |
} | |
} | |
export let app = new App(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment