Skip to content

Commit 7bc0d06

Browse files
feat(plugins): better conversation-insights scheduling (botpress#14186)
1 parent aaf94ad commit 7bc0d06

5 files changed

Lines changed: 85 additions & 49 deletions

File tree

plugins/conversation-insights/plugin.definition.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,19 @@ export default new PluginDefinition({
2929
title: 'Sentiment',
3030
description: 'The sentiment that best describes the conversation. Type: enum Sentiments',
3131
},
32+
isDirty: {
33+
title: 'Is Dirty',
34+
description:
35+
"Indicates whether a conversation's AI insight has been updated since the last message. Type: boolean",
36+
},
3237
},
3338
},
3439
events: {
3540
updateAiInsight: {
3641
schema: z.object({}),
3742
},
3843
},
44+
workflows: { updateAllConversations: { input: { schema: z.object({}) }, output: { schema: z.object({}) } } },
3945
interfaces: {
4046
llm,
4147
},
Lines changed: 53 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
1-
import * as sdk from '@botpress/sdk'
21
import { isBrowser } from 'browser-or-node'
3-
import * as updateScheduler from './summaryUpdateScheduler'
2+
import * as onNewMessageHandler from './onNewMessageHandler'
43
import * as summaryUpdater from './tagsUpdater'
54
import * as types from './types'
65
import * as bp from '.botpress'
76

8-
type CommonProps = types.CommonProps
7+
const HOUR_MILLISECONDS = 60 * 60 * 1000
98

109
const plugin = new bp.Plugin({
1110
actions: {},
@@ -16,10 +15,16 @@ plugin.on.afterIncomingMessage('*', async (props) => {
1615
return
1716
}
1817
const { conversation } = await props.client.getConversation({ id: props.data.conversationId })
19-
const { message_count } = await _onNewMessage({ ...props, conversation })
18+
await onNewMessageHandler.onNewMessage({ ...props, conversation })
2019

21-
if (props.configuration.aiEnabled && updateScheduler.isTimeToUpdate(message_count)) {
22-
await props.events.updateAiInsight.withConversationId(props.data.conversationId).emit({})
20+
if (props.configuration.aiEnabled) {
21+
const eventType = `${props.alias}#updateAiInsight`
22+
const events = await props.client.listEvents({ type: eventType, status: 'scheduled' })
23+
24+
if (events.events.length === 0) {
25+
const dateTime = new Date(Date.now() + HOUR_MILLISECONDS).toISOString()
26+
await props.events.updateAiInsight.schedule({}, { dateTime })
27+
}
2328
}
2429

2530
return undefined
@@ -30,53 +35,59 @@ plugin.on.afterOutgoingMessage('*', async (props) => {
3035
return
3136
}
3237
const { conversation } = await props.client.getConversation({ id: props.data.message.conversationId })
33-
await _onNewMessage({ ...props, conversation })
38+
await onNewMessageHandler.onNewMessage({ ...props, conversation })
3439
return undefined
3540
})
3641

37-
type OnNewMessageProps = CommonProps & {
38-
conversation: bp.ClientOutputs['getConversation']['conversation']
39-
}
40-
const _onNewMessage = async (
41-
props: OnNewMessageProps
42-
): Promise<{ message_count: number; participant_count: number }> => {
43-
const message_count = props.conversation.tags.message_count ? parseInt(props.conversation.tags.message_count) + 1 : 1
44-
45-
const participant_count = await props.client
46-
.listParticipants({ id: props.conversation.id })
47-
.then(({ participants }) => participants.length)
48-
49-
const tags = {
50-
message_count: message_count.toString(),
51-
participant_count: participant_count.toString(),
52-
}
53-
54-
await props.client.updateConversation({
55-
id: props.conversation.id,
56-
tags,
57-
})
58-
return { message_count, participant_count }
59-
}
60-
6142
plugin.on.event('updateAiInsight', async (props) => {
6243
if (isBrowser) {
6344
props.logger.error('This event is not supported by the browser')
6445
return
6546
}
66-
const firstMessagePage = await props.client
67-
.listMessages({ conversationId: props.event.conversationId })
68-
.then((res) => res.messages)
6947

70-
if (!props.event.conversationId) {
71-
throw new sdk.RuntimeError(`The conversationId cannot be null when calling the event '${props.event.type}'`)
48+
const workflows = await props.client.listWorkflows({
49+
name: 'updateAllConversations',
50+
statuses: ['in_progress', 'listening', 'pending'],
51+
})
52+
53+
if (workflows.workflows.length === 0) {
54+
props.workflows.updateAllConversations.startNewInstance({ input: {} })
7255
}
73-
const conversation = await props.client.getConversation({ id: props.event.conversationId })
56+
})
7457

75-
await summaryUpdater.updateTitleAndSummary({
76-
...props,
77-
conversation: conversation.conversation,
78-
messages: firstMessagePage,
79-
})
58+
plugin.on.workflowStart('updateAllConversations', async (props) => {
59+
props.logger.info('Starting updateAllConversations workflow')
60+
await _updateAllConversations(props)
61+
62+
return undefined
63+
})
64+
65+
plugin.on.workflowContinue('updateAllConversations', async (props) => {
66+
await _updateAllConversations(props)
67+
68+
return undefined
69+
})
70+
71+
plugin.on.workflowTimeout('updateAllConversations', async (props) => {
72+
props.logger.error('Workflow timed out')
8073
})
8174

75+
type WorkflowProps = types.CommonProps & bp.WorkflowHandlerProps['updateAllConversations']
76+
const _updateAllConversations = async (props: WorkflowProps) => {
77+
const dirtyConversations = await props.client.listConversations({ tags: { isDirty: 'true' } })
78+
79+
const promises: Promise<void>[] = []
80+
for (const conversation of dirtyConversations.conversations) {
81+
const firstMessagePage = await props.client
82+
.listMessages({ conversationId: conversation.id })
83+
.then((res) => res.messages)
84+
const promise = summaryUpdater.updateTitleAndSummary({ ...props, conversation, messages: firstMessagePage })
85+
promises.push(promise)
86+
}
87+
88+
await Promise.all(promises)
89+
await props.workflow.setCompleted()
90+
props.logger.info('updateAllConversations workflow completed')
91+
}
92+
8293
export default plugin
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import * as types from './types'
2+
import * as bp from '.botpress'
3+
4+
type OnNewMessageProps = types.CommonProps & {
5+
conversation: bp.ClientOutputs['getConversation']['conversation']
6+
}
7+
export const onNewMessage = async (props: OnNewMessageProps) => {
8+
const message_count = props.conversation.tags.message_count ? parseInt(props.conversation.tags.message_count) + 1 : 1
9+
10+
const participant_count = await props.client
11+
.listParticipants({ id: props.conversation.id })
12+
.then(({ participants }) => participants.length)
13+
14+
const tags = {
15+
message_count: message_count.toString(),
16+
participant_count: participant_count.toString(),
17+
isDirty: 'true',
18+
}
19+
20+
await props.client.updateConversation({
21+
id: props.conversation.id,
22+
tags,
23+
})
24+
return
25+
}

plugins/conversation-insights/src/summaryUpdateScheduler.ts

Lines changed: 0 additions & 7 deletions
This file was deleted.

plugins/conversation-insights/src/tagsUpdater.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ export const updateTitleAndSummary = async (props: UpdateTitleAndSummaryProps) =
4343
title: parsedSummary.json.title,
4444
summary: parsedSummary.json.summary,
4545
sentiment: parsedSentiment.json.sentiment,
46+
isDirty: 'false',
4647
},
4748
})
4849
}

0 commit comments

Comments
 (0)