window.sapcai.webclientBridge.sttGetConfig
object, you will get the microphone displayedwindow.sapcai.webclientBridge.sttStartListening
.onSTTResult
. The method takes the text you want to display and a boolean to indicate whether these are interim results (displayed in the interim transcription window) or final (sent to the chatbot conversation as the next utterance).sttAbort
window.sapcai.webclientBridge.sttGetConfig
method. We'll see an example where we handle the audio capture ourselves in the example below.sttOnInterimAudioData
,which receives an interim blob which you can pass on to your STT servicesttOnFinalAudioData
, which receives a final blob when the user stops talking and which you can pass on to your STT servicesttStopListening
, which is called when the user stops talking and in which you close websockets and do any other cleanup workwebclient.js
with just the tokens I needed. In real life I would have hidden the tokens, and created a service to generate the token for calling the service, which expires every 30 minutes.const data_expander_preferences = "<chatbot preferences>";
const data_channel_id = "<chatbot channel ID>";
const data_token = "<chatbot token>";
const ibmtoken = "<IBM token>";
const ibmurl = "<IBM service URL for your tenant>";
webClientBridge.js
with just the tokens I needed. In real life I would have hidden the tokens, and created a service to generate the tokens for calling the service, which expire every 30 minutes.const webclientBridge = {
// ALL THE STT METHODS
//--------------------
callImplMethod: async (name, ...args) => {
console.log(name)
if (window.webclientBridgeImpl && window.webclientBridgeImpl[name]) {
return window.webclientBridgeImpl[name](...args)
}
},
// if this function returns an object, WebClient will enable the microphone button.
sttGetConfig: async (...args) => {
return webclientBridge.callImplMethod('sttGetConfig', ...args)
},
sttStartListening: async (...args) => {
return webclientBridge.callImplMethod('sttStartListening', ...args)
},
sttStopListening: async (...args) => {
return webclientBridge.callImplMethod('sttStopListening', ...args)
},
sttAbort: async (...args) => {
return webclientBridge.callImplMethod('sttAbort', ...args)
},
// only called if useMediaRecorder = true in sttGetConfig
sttOnFinalAudioData: async (...args) => {
return webclientBridge.callImplMethod('sttOnFinalAudioData', ...args)
},
// only called if useMediaRecorder = true in sttGetConfig
sttOnInterimAudioData: async (...args) => {
// send interim blob to STT service
return webclientBridge.callImplMethod('sttOnInterimAudioData', ...args)
},
// OTHER BRIDGE METHODS
//--------------------
// called on each message to update the memory
// called on each message
onMessage: (payload) => {
payload.messages.forEach(element => {
if (element.participant.isBot && element.attachment.content.text.startsWith("SENDING AVATAR")) {
profile = element.attachment.content.text.substring(19);
window.sapcai.webclientBridge.imageeditor.setSrc("https://avatars.services.sap.com/images/" + profile + ".png")
}
});
}
}
window.sapcai = {
webclientBridge,
}
webClientBridgeImpl.js
with the implementation (I could have combined the last 2 files. This file opens a websocket to the IBM service when the user clicks the microphone and sends interim and final audio blobs to the service. The websocket callbacks put the transcribed texts into the interim transcription area or the conversation.const IBM_URL = ibmurl
const access_token = ibmtoken
let wsclient = null
const sttIBMWebsocket = {
sttGetConfig: async () => {
return {
useMediaRecorder: true,
interimResultTime: 50,
}
},
sttStartListening: async (params) => {
const [metadata] = params
const sttConfig = await sttIBMWebsocket.sttGetConfig()
const interim_results = sttConfig.interimResultTime
wsclient = new WebSocket(`wss://${IBM_URL}?access_token=${access_token}`)
wsclient.onopen = (event) => {
wsclient.send(JSON.stringify({
action: 'start',
interim_results,
'content-type': `audio/${metadata.audioMetadata.fileFormat}`,
}))
}
wsclient.onmessage = (event) => {
const data = JSON.parse(event.data)
const results = _.get(data, 'results', [])
if (results.length > 0) {
const lastresult = _.get(results, `[${results.length - 1}]`)
const m = {
text: _.get(lastresult, 'alternatives[0].transcript', ''),
final: _.get(lastresult, 'final'),
}
window.sap.cai.webclient.onSTTResult(m)
}
}
wsclient.onclose = (event) => {
console.log('OnClose')
}
wsclient.onerror = (event) => {
console.log('OnError', JSON.stringify(event.data))
}
},
sttStopListening: async () => {
const client = wsclient
setTimeout(() => {
if (client) {
client.close()
}
}, 5000)
},
sttAbort: async () => {
if (wsclient) {
wsclient.close()
wsclient = null
}
},
sttOnInterimAudioData: async (params) => {
if (wsclient) {
const [blob, metadata] = params
wsclient.send(blob)
}
},
sttOnFinalAudioData: async (params) => {
if (wsclient) {
const [blob, metadata] = params
wsclient.send(blob)
wsclient.send(JSON.stringify({
action: 'stop',
}))
}
},
}
window.webclientBridgeImpl = sttIBMWebsocket
sap.ui.define
.onMessage
method captures the text, checks what it says, and then updates the image editor picture with the proper avatar.You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
User | Count |
---|---|
13 | |
10 | |
10 | |
9 | |
8 | |
7 | |
6 | |
5 | |
5 | |
5 |