Skip to content

Commit 1522164

Browse files
author
Herve Tribouilloy
committed
feat(intent-discovery): trigger AI interpretation and refine filters from Ask action
- Ask button now triggers AI intent interpretation - Returned filters automatically update layered navigation preferences - AI readiness threshold displayed in UI - Added telemetry for interpretation events
1 parent c153800 commit 1522164

26 files changed

Lines changed: 254 additions & 129 deletions

node-backend/src/controller/intent-handler.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,4 +74,40 @@ export class IntentHandler {
7474
)
7575
}
7676

77+
dummy = async (
78+
req: Request,
79+
res: Response
80+
): Promise<void> => {
81+
try {
82+
const { intent } = req.body
83+
const text: string = intent?.text ?? ""
84+
85+
const filters: Record<string, string> = {}
86+
87+
await new Promise(resolve => setTimeout(resolve, 500))
88+
89+
filters.color = "58"
90+
/*filters.climate = "202"
91+
92+
if (text.includes("red")) {
93+
filters.color = "red"
94+
}
95+
96+
if (text.includes("jacket")) {
97+
filters.category = "jackets"
98+
}
99+
100+
if (text.includes("hiking")) {
101+
filters.activity = "hiking"
102+
}*/
103+
104+
res.json({filters})
105+
106+
} catch (err) {
107+
console.error(err)
108+
res.status(500).json({
109+
error: "Server error"
110+
})
111+
}
112+
}
77113
}

node-backend/src/routes/intentRouter.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ export const setupIntentRoutes = (app: Application) => {
2020

2121
router.post("/interpret", intentHandlerController.validateIntentInput)
2222

23+
router.post("/dummy", intentHandlerController.dummy)
24+
2325
router.options('*', options);
2426

2527
app.use(config.route.intentPrefix, router)

vite_project/package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

vite_project/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "widget-intent-discovery",
33
"private": true,
4-
"version": "0.3.2",
4+
"version": "0.4.0",
55
"type": "module",
66
"scripts": {
77
"dev": "vite",

vite_project/public/cdn/default.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
},
3434
"ai": {
3535
"enabled": true,
36+
"activationThreshold": 5,
3637
"matchThreshold": 30,
3738
"minIntentScore": 4,
3839
"maxProductsForAnalysis": 40

vite_project/public/cdn/magento_fr_store.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
},
3434
"ai": {
3535
"enabled": true,
36+
"activationThreshold": 30,
3637
"matchThreshold": 30,
3738
"minIntentScore": 4,
3839
"maxProductsForAnalysis": 40
@@ -44,7 +45,10 @@
4445
"Tell us what matters most for your purchase": "Décrivez ce qui compte le plus pour votre achat",
4546
"View all": "Afficher tout",
4647
"Show less": "Afficher moins",
47-
"AI suggestions": "Suggestions de l’IA"
48+
"AI suggestions": "Suggestions de l’IA",
49+
"AI can be activated in %s more characters": "L’IA pourra s’activer dans %s caractères",
50+
"AI ready to interpret your request": "L’IA est prête à interpréter votre demande",
51+
"Searching your best match…": "Recherche des produits les plus adaptés à votre demande…"
4852
},
4953
"integrations": {
5054
"require": ["magentoGraphql", "intentApi"]

vite_project/src/components/EvaluationOverlay.tsx

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

vite_project/src/components/IntentDiscovery/AttributeLayer.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ export const AttributeLayer = ({
2222
const { setActiveAttributeCode } = useActiveAttributeState();
2323
const {intentState} = useSystemState()
2424
const { valueFor: prefValue } =
25-
useSelectedPreferences(attributeLayerData, intentState);
25+
useSelectedPreferences(attributeLayerData?.aggregations, intentState);
2626
const {t} = useTranslationState()
2727

2828
const [showAll, setShowAll] = useState(false);

vite_project/src/components/IntentDiscovery/IntentDiscoveryLayout.tsx

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import type {IntentDiscoveryDataConfig} from "../../domain/intent-discovery.type
22
import type {CategoryData} from "../../types/infra/magento/category.types.ts";
33
import {useIntentLayoutState} from "../../hooks/domain/useIntentLayoutState.tsx";
44
import {useIntentController} from "../../hooks/domain/useIntentController.tsx";
5-
import {EvaluationOverlay} from "../EvaluationOverlay.tsx";
5+
import {SearchOverlay} from "../SearchOverlay.tsx";
66
import {IntentMessage} from "./IntentMessage.tsx";
77
import {AttributeLayer} from "./AttributeLayer.tsx";
88
import {IntentDiscoveryOptions} from "./IntentDiscoveryOptions.tsx";
@@ -19,15 +19,15 @@ export const IntentDiscoveryLayout = ({ config, categoryData, attributeLayerData
1919
const {
2020
showRightColumn,
2121
setShowRightColumn,
22-
isEvaluating,
23-
setIsEvaluating
22+
isSearching,
23+
setIsSearching
2424
} = useIntentLayoutState()
2525

26-
const { intent} = useIntentController(attributeLayerData, config)
26+
const { intent } = useIntentController(attributeLayerData, config)
2727

2828
return (
2929
<div className="intent-widget">
30-
{isEvaluating && <EvaluationOverlay/>}
30+
{isSearching && <SearchOverlay />}
3131
<div className={showRightColumn ? "re-intent-layout re-intent-layout--two" : "re-intent-layout"}>
3232
<div className="re-intent-col re-intent-col--left">
3333
<IntentMessage
@@ -38,7 +38,7 @@ export const IntentDiscoveryLayout = ({ config, categoryData, attributeLayerData
3838
<AttributeLayer
3939
config={config}
4040
attributeLayerData={attributeLayerData}
41-
disabled={isEvaluating}
41+
disabled={isSearching}
4242
/>
4343
<IntentDiscoveryOptions
4444
config={config}
@@ -49,11 +49,11 @@ export const IntentDiscoveryLayout = ({ config, categoryData, attributeLayerData
4949

5050
<div className="re-intent-col re-intent-col--right">
5151
<ProductRecommendations
52+
config={config}
5253
categoryData={categoryData}
5354
attributeLayerData={attributeLayerData}
5455
search={{
55-
shouldRun: intent.shouldSearch,
56-
setIsEvaluating
56+
setIsSearching
5757
}}
5858
onVisibilityChange={setShowRightColumn}
5959
/>

vite_project/src/components/IntentDiscovery/IntentMessage.tsx

Lines changed: 54 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,60 @@
1-
import {useEvaluateMessageIntent} from "../../hooks/domain/useEvaluateMessageIntent.tsx";
21
import type {MagentoProducts} from "../../hooks/infra/useProductAttributeLayer.tsx";
3-
import {Spinner} from "../global/Spinner.tsx";
4-
import {ErrorState} from "../global/ErrorState.tsx";
52
import type {IntentDiscoveryDataConfig} from "../../domain/intent-discovery.types.ts";
63
import {useTranslationState} from "../../state/Translation/useTranslationState.ts";
74
import type {IntentControllerState} from "../../domain/intent.types.ts";
5+
import {activity} from "../../activity";
6+
import {buildAiInterpretationPayload} from "../../lib/ai-recommendations.ts";
7+
import {useSystemState} from "../../state/System/useSystemState.ts";
8+
import {useOptionLabelMap} from "../../hooks/domain/useOptionLabelMap.ts";
89

910
type Props = {
1011
config: IntentDiscoveryDataConfig,
1112
intent: IntentControllerState,
1213
attributeLayerData: MagentoProducts
1314
};
14-
export const IntentMessage = ({config, intent, attributeLayerData}: Props) => {
15-
const { evaluationLoading, evaluationError } = useEvaluateMessageIntent(
16-
config,
17-
intent,
18-
attributeLayerData?.aggregations
19-
)
15+
export const IntentMessage = ({intent, attributeLayerData}: Props) => {
16+
const { intentState, setIntentText, setPreference, intentApiClient } = useSystemState()
17+
const optionLabelMap = useOptionLabelMap(attributeLayerData?.aggregations);
18+
2019
const {t} = useTranslationState()
2120

22-
if (evaluationLoading) return <Spinner />
23-
if (evaluationError) return <ErrorState error={evaluationError} />
21+
const handleAsk = async () => {
22+
try {
23+
const payload = buildAiInterpretationPayload(
24+
intentState,
25+
attributeLayerData?.aggregations,
26+
intent.text,
27+
optionLabelMap
28+
)
29+
30+
const json = await intentApiClient.interpret(payload)
31+
//const json = await intentApiClient.dummy(payload)
32+
33+
activity('ai-interpretation', 'AI interpretation API ran', json)
34+
35+
if (json?.filters) {
36+
setIntentText(intent.text)
37+
38+
for (const [attribute, value] of Object.entries(json?.filters)) {
39+
setPreference(attribute, value)
40+
}
41+
}
42+
43+
activity(
44+
'intent-evaluated',
45+
'Intent evaluated and search triggered'
46+
)
47+
48+
} catch (err) {
49+
50+
activity(
51+
'intent-error',
52+
'Intent evaluation failed',
53+
{ error: err }
54+
)
55+
56+
}
57+
}
2458

2559
return (
2660
<div className="finder">
@@ -33,10 +67,17 @@ export const IntentMessage = ({config, intent, attributeLayerData}: Props) => {
3367
onChange={(e) => intent.setIntent(e.target.value)}
3468
className="intent-input"
3569
/>
70+
<button
71+
className="intent-input-button"
72+
onClick={handleAsk}
73+
disabled={intent.remainingChars > 0}
74+
>
75+
Ask
76+
</button>
3677
<div className={`intent-ai-threshold ${intent.remainingChars === 0 ? "ready" : ""}`}>
3778
{intent.remainingChars > 0
38-
? `AI activates in ${intent.remainingChars} more characters`
39-
: "AI ready — interpreting request"}
79+
? t("AI can be activated in %s more characters", intent.remainingChars)
80+
: t("AI ready to interpret your request")}
4081
</div>
4182
</div>
4283
</div>

0 commit comments

Comments
 (0)