Skip to content

Commit f08b519

Browse files
committed
fix(chatbot): simplified logic
1 parent 1ed30d2 commit f08b519

File tree

2 files changed

+111
-163
lines changed

2 files changed

+111
-163
lines changed

src/fulfillment/fulfillment.ts

Lines changed: 35 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -20,46 +20,42 @@ export const handler: LexV2Handler = async (event): Promise<LexV2Result> => {
2020
sessionId,
2121
})
2222

23-
if (slots) {
24-
try {
25-
const suggestLunchSlots: SuggestLunchSlots = {
26-
OfficeLocation: isSlotValue(slots.OfficeLocation)
27-
? slots.OfficeLocation
28-
: null,
29-
CuisineType: isSlotValue(slots.CuisineType) ? slots.CuisineType : null,
30-
DietaryRestrictions: isSlotValue(slots.DietaryRestrictions)
31-
? slots.DietaryRestrictions
32-
: null,
33-
Budget: isSlotValue(slots.Budget) ? slots.Budget : null,
34-
}
35-
if (
36-
suggestLunchSlots.OfficeLocation &&
37-
isSlotValue(suggestLunchSlots.OfficeLocation)
38-
) {
39-
const result = await processSlots(
40-
sessionId,
41-
inputTranscript,
42-
suggestLunchSlots,
43-
intent,
44-
sessionAttributes,
45-
)
46-
return result
47-
}
48-
} catch (e) {
49-
const error = ensureError(e)
50-
logger.error('Error processing slot', { error })
51-
return delegate(
52-
sessionAttributes,
53-
intent,
54-
[
55-
{
56-
contentType: 'PlainText',
57-
content: 'An error occurred while processing your request.',
58-
} as LexV2ContentMessage,
59-
],
60-
'Close',
61-
)
23+
if (!slots) {
24+
logger.error('Missing slots in the input data')
25+
return delegate(
26+
sessionAttributes,
27+
intent,
28+
[
29+
{
30+
contentType: 'PlainText',
31+
content: 'An error occurred while processing your request.',
32+
} as LexV2ContentMessage,
33+
],
34+
'Close',
35+
)
36+
}
37+
try {
38+
const suggestLunchSlots: SuggestLunchSlots = {
39+
OfficeLocation: isSlotValue(slots.OfficeLocation)
40+
? slots.OfficeLocation
41+
: null,
42+
CuisineType: isSlotValue(slots.CuisineType) ? slots.CuisineType : null,
43+
DietaryRestrictions: isSlotValue(slots.DietaryRestrictions)
44+
? slots.DietaryRestrictions
45+
: null,
46+
Budget: isSlotValue(slots.Budget) ? slots.Budget : null,
6247
}
48+
const result = await processSlots(
49+
sessionId,
50+
inputTranscript,
51+
suggestLunchSlots,
52+
intent,
53+
sessionAttributes,
54+
)
55+
return result
56+
} catch (e) {
57+
const error = ensureError(e)
58+
logger.error('Error processing slot', { error })
6359
return delegate(
6460
sessionAttributes,
6561
intent,
@@ -72,17 +68,4 @@ export const handler: LexV2Handler = async (event): Promise<LexV2Result> => {
7268
'Close',
7369
)
7470
}
75-
logger.error('Missing slots in the input data')
76-
77-
return delegate(
78-
sessionAttributes,
79-
intent,
80-
[
81-
{
82-
contentType: 'PlainText',
83-
content: 'An error occurred while processing your request.',
84-
} as LexV2ContentMessage,
85-
],
86-
'Close',
87-
)
8871
}

src/fulfillment/processSlots.ts

Lines changed: 76 additions & 111 deletions
Original file line numberDiff line numberDiff line change
@@ -117,83 +117,6 @@ const createCloseAction = (
117117
}
118118
}
119119

120-
/**
121-
* Handles the OfficeLocation slot in a LexV2 intent.
122-
* Then it handles the CuisineType.
123-
* @param {string} sessionId - The unique identifier for the session.
124-
* @param {string} inputTranscript - The user's input.
125-
* @param {string} slotKey - The key of the current slot.
126-
* @param {LexV2ScalarSlotValue} slotValue - The value of the current slot.
127-
* @param {LexV2Intent} intent - The intent object.
128-
* @param {Record<string, string> | undefined} sessionAttributes - The session attributes.
129-
* @returns {Promise<LexV2Result | null>} The result of handling the OfficeLocation slot.
130-
* @throws {Error} If the office location is missing.
131-
* @throws {Error} If the cuisine type is missing.
132-
*/
133-
const handleOfficeLocation: SlotHandler = async (
134-
sessionId: string,
135-
inputTranscript: string,
136-
slotKey: string,
137-
slotValue: LexV2ScalarSlotValue,
138-
intent: LexV2Intent,
139-
sessionAttributes: Record<string, string> | undefined,
140-
): Promise<LexV2Result | null> => {
141-
const slot = CustomSlot[slotKey as keyof typeof CustomSlot]
142-
logger.debug(`Slot type detected`, { slotKey, slot })
143-
// Fetch the CuisineType from slots
144-
const nextSlotValue = intent.slots?.CuisineType
145-
if (nextSlotValue && isSlotValue(nextSlotValue)) {
146-
const cuisineType = nextSlotValue.value.interpretedValue
147-
if (!cuisineType) {
148-
throw new Error('Cuisine type missing')
149-
}
150-
await storeState({
151-
sessionId,
152-
slot: CustomSlot.CuisineType,
153-
slotValue: cuisineType,
154-
})
155-
return handleCuisineType(
156-
sessionId,
157-
inputTranscript,
158-
slotKey,
159-
nextSlotValue,
160-
intent,
161-
sessionAttributes,
162-
)
163-
}
164-
const officeSlotValue = intent.slots?.OfficeLocation
165-
if (officeSlotValue) {
166-
const officeLocation = slotValue.value.interpretedValue
167-
168-
if (!officeLocation) {
169-
throw new Error('Office location missing')
170-
}
171-
await storeState({
172-
sessionId,
173-
slot: CustomSlot.OfficeLocation,
174-
slotValue: officeLocation,
175-
})
176-
177-
// if office location was given and
178-
// new state was stored we'll fetch possible cuisine types from dynamoDb
179-
const supportedCuisineTypes =
180-
await getCuisineTypesForOfficeLocation(officeLocation)
181-
logger.info('Found cuisineTypes', { supportedCuisineTypes })
182-
183-
const messages = createLexMessages(officeLocation, supportedCuisineTypes)
184-
if (supportedCuisineTypes.length) {
185-
return createElicitSlotAction(
186-
'CuisineType',
187-
sessionAttributes,
188-
intent,
189-
messages,
190-
)
191-
}
192-
}
193-
194-
return createCloseAction(sessionAttributes, intent, [])
195-
}
196-
197120
const handleCuisineType: SlotHandler = async (
198121
sessionId: string,
199122
inputTranscript: string,
@@ -229,14 +152,31 @@ const handleCuisineType: SlotHandler = async (
229152
return createCloseAction(sessionAttributes, intent, [])
230153
}
231154

155+
const createErrorResponse = (
156+
sessionAttributes: Record<string, string> | undefined,
157+
intent: LexV2Intent,
158+
): LexV2Result => {
159+
return delegate(
160+
sessionAttributes,
161+
intent,
162+
[
163+
{
164+
contentType: 'PlainText',
165+
content: 'An error occurred while processing your request.',
166+
} as LexV2ContentMessage,
167+
],
168+
'Close',
169+
)
170+
}
171+
232172
type SlotHandler = (
233173
sessionId: string,
234174
slotKey: string,
235175
inputTranscript: string,
236176
slotValue: LexV2ScalarSlotValue,
237177
intent: LexV2Intent,
238178
sessionAttributes: Record<string, string> | undefined,
239-
) => Promise<LexV2Result | null>
179+
) => Promise<LexV2Result>
240180

241181
export const processSlots: NextSlotHandler = async (
242182
sessionId: string,
@@ -245,41 +185,66 @@ export const processSlots: NextSlotHandler = async (
245185
intent: LexV2Intent,
246186
sessionAttributes: Record<string, string> | undefined,
247187
): Promise<LexV2Result> => {
248-
const slotHandlers: Partial<Record<keyof SuggestLunchSlots, SlotHandler>> = {
249-
OfficeLocation: handleOfficeLocation,
250-
CuisineType: handleCuisineType,
251-
// Add other handlers here if needed
252-
}
253-
254-
// Use reduce to process each slot and handle them accordingly
255-
const finalResult = await Object.entries(slots).reduce(
256-
async (prevPromise, [slotKey, slotValue]) => {
257-
const previousResult = await prevPromise
258-
if (previousResult) {
259-
return previousResult // Exit early if we already have a valid result
188+
const { OfficeLocation } = slots
189+
190+
if (OfficeLocation && isSlotValue(OfficeLocation)) {
191+
const slotKey = 'OfficeLocation'
192+
const slot = CustomSlot[slotKey as keyof typeof CustomSlot]
193+
logger.debug(`Slot type detected`, { slotKey, slot })
194+
// Fetch the CuisineType from slots
195+
const nextSlotValue = intent.slots?.CuisineType
196+
if (nextSlotValue && isSlotValue(nextSlotValue)) {
197+
const cuisineType = nextSlotValue.value.interpretedValue
198+
if (!cuisineType) {
199+
throw new Error('Cuisine type missing')
260200
}
201+
await storeState({
202+
sessionId,
203+
slot: CustomSlot.CuisineType,
204+
slotValue: cuisineType,
205+
})
206+
return handleCuisineType(
207+
sessionId,
208+
inputTranscript,
209+
slotKey,
210+
nextSlotValue,
211+
intent,
212+
sessionAttributes,
213+
)
214+
}
215+
const officeSlotValue = intent.slots?.OfficeLocation
216+
if (officeSlotValue) {
217+
const officeLocation = OfficeLocation.value.interpretedValue
261218

262-
if (slotValue && isSlotValue(slotValue)) {
263-
const handler = slotHandlers[slotKey as keyof SuggestLunchSlots]
264-
if (handler) {
265-
const handlerResult = await handler(
266-
sessionId,
267-
inputTranscript,
268-
slotKey,
269-
slotValue,
270-
intent,
271-
sessionAttributes,
272-
)
273-
if (handlerResult) {
274-
return handlerResult // Return the first valid result found
275-
}
276-
}
219+
if (!officeLocation) {
220+
throw new Error('Office location missing')
277221
}
278-
return null
279-
},
280-
Promise.resolve<LexV2Result | null>(null), // Initial value for reduce
281-
)
222+
await storeState({
223+
sessionId,
224+
slot: CustomSlot.OfficeLocation,
225+
slotValue: officeLocation,
226+
})
227+
228+
// if office location was given and
229+
// new state was stored we'll fetch possible cuisine types from dynamoDb
230+
const supportedCuisineTypes =
231+
await getCuisineTypesForOfficeLocation(officeLocation)
232+
logger.debug('Found cuisineTypes', { supportedCuisineTypes })
233+
234+
const messages = createLexMessages(officeLocation, supportedCuisineTypes)
235+
if (supportedCuisineTypes.length) {
236+
return createElicitSlotAction(
237+
'CuisineType',
238+
sessionAttributes,
239+
intent,
240+
messages,
241+
)
242+
}
243+
}
244+
245+
return createCloseAction(sessionAttributes, intent, [])
246+
}
282247

283-
// If no specific handler returned a valid result, delegate the action
284-
return finalResult ?? delegate(sessionAttributes, intent)
248+
logger.error('OfficeLocation slot is missing or invalid')
249+
return createErrorResponse(sessionAttributes, intent)
285250
}

0 commit comments

Comments
 (0)