wechaty-qnamaker
Version:
QnAMaker.ai Plugin for Wechaty
137 lines (110 loc) • 3.61 kB
text/typescript
import { log } from 'wechaty'
import {
Args,
CommandContext,
Vorpal,
} from 'wechaty-vorpal'
import {
matchers,
} from 'wechaty-plugin-contrib'
import { QnAMakerOptions } from './qnamaker'
import { normalizeConfig } from './normalize-config'
import { asker } from './asker'
import {
QnASearchResult,
QueryDTO,
} from '@azure/cognitiveservices-qnamaker-runtime/esm/models'
function Faq (config: Partial<QnAMakerOptions> | Partial<QnAMakerOptions>[]) {
log.verbose('WechatyQnAMaker', 'Faq(%s)', JSON.stringify(config))
const normalizedConfigList: QnAMakerOptions[] = []
if (!Array.isArray(config)) {
normalizedConfigList.push(normalizeConfig(config))
} else {
normalizedConfigList.push(
...config.map(c => normalizeConfig(c))
)
}
return function FaqExtension (vorpal: Vorpal) {
log.verbose('WechatyQnAMaker', 'FaqExtension(vorpal)')
vorpal
.command('faq <question...>', 'Get an answer from Frequent Asked Questions (FAQ)')
.option('-v --verbose', 'Show verbose informations')
.option('-n --number <number>', 'Show maximum <number> related answers. (default: 1)')
.action(faqAction(normalizedConfigList))
}
}
interface FaqOptions {
verbose? : boolean,
number? : number,
}
const faqAction = (configList : QnAMakerOptions[]) => {
log.verbose('WechatyQnAMaker', 'Faq() faqAction("%s")', JSON.stringify(configList))
const askerDictList = configList.map(config => ({
ask : asker(config),
matchLanguage : (typeof config.language === 'undefined')
? () => true // match all language by default
: matchers.languageMatcher(config.language),
}))
const askAll = async (
question : string,
dto : QueryDTO = {},
) => {
const resultList = await Promise.all(
askerDictList
.filter(dict => dict.matchLanguage(question))
.map(dict => dict.ask(question, dto))
)
return resultList
.flat()
.filter(i => i.score)
.sort((a, b) => b.score! - a.score!)
}
return async function faqActionExector (
this: CommandContext,
args: Args
): Promise<void> {
log.verbose('WechatyQnAMaker', 'Faq() faqAction() faqActionExecutor("%s")', JSON.stringify(args))
const options: FaqOptions = args.options
let question: string
if (Array.isArray(args.question)) {
question = args.question.join(' ')
} else {
question = args.question
}
const queryDto: QueryDTO = {
scoreThreshold : 1,
top : options.number,
}
const searchResultList = await askAll(question, queryDto)
if (searchResultList.length <= 0) {
this.log('Sorry, I did not find any answer in my KB (Knowledge Base) for your question: "' + question + '".')
return
}
log.verbose('WechatyQnAMaker', 'Faq() faqAction() faqActionExecutor() found %s answers', searchResultList.length)
searchResultList.forEach((result, idx) => {
if (!result.answer) { return }
if (idx > (options.number ?? 0)) { return }
this.log(
toReply(result, idx + 1, options.verbose)
)
})
}
}
function toReply (
result : QnASearchResult,
index : number,
verbose = false,
): string {
if (!verbose) {
return result.answer!
}
const list = [
`A${index}:(score:${Math.floor(result.score || 0)}) ${result.answer}`,
]
if (result.questions) {
list.unshift('')
list.unshift(`Q${index}: ` + result.questions[0])
}
return list.join('\n')
}
export { Faq }