Skip to content

Commit

Permalink
feat: Integrated Optimizely Prompt Experiment
Browse files Browse the repository at this point in the history
  • Loading branch information
rijuma committed Jun 26, 2024
1 parent c44db4a commit 10c5989
Show file tree
Hide file tree
Showing 5 changed files with 41 additions and 3 deletions.
5 changes: 5 additions & 0 deletions src/components/Sidebar/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@ import React, { useRef, useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import PropTypes from 'prop-types';
import { sendTrackEvent } from '@edx/frontend-platform/analytics';
import { getAuthenticatedUser } from '@edx/frontend-platform/auth';
import {
Button,
Icon,
IconButton,
} from '@openedx/paragon';
import { Close } from '@openedx/paragon/icons';

import { usePromptExperiment } from '../../utils/optimizelyExperiment';
import APIError from '../APIError';
import ChatBox from '../ChatBox';
import Disclosure from '../Disclosure';
Expand All @@ -31,6 +33,8 @@ const Sidebar = ({
} = useSelector(state => state.learningAssistant);
const chatboxContainerRef = useRef(null);
const dispatch = useDispatch();
const { userId } = getAuthenticatedUser();
const [ variationKey, experimentName ] = usePromptExperiment(userId);

Check failure on line 37 in src/components/Sidebar/index.jsx

View workflow job for this annotation

GitHub Actions / test

There should be no space after '['

Check failure on line 37 in src/components/Sidebar/index.jsx

View workflow job for this annotation

GitHub Actions / test

There should be no space before ']'

// this use effect is intended to scroll to the bottom of the chat window, in the case
// that a message is larger than the chat window height.
Expand Down Expand Up @@ -80,6 +84,7 @@ const Sidebar = ({
dispatch(clearMessages());
sendTrackEvent('edx.ui.lms.learning_assistant.clear', {
course_id: courseId,
...(variationKey ? { experiment_name: experimentName, variation_key: variationKey } : {}),
});
};

Expand Down
7 changes: 6 additions & 1 deletion src/components/ToggleXpertButton/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
} from '@openedx/paragon';
import { Close } from '@openedx/paragon/icons';

import { usePromptExperiment } from '../../utils/optimizelyExperiment';
import { ReactComponent as XpertLogo } from '../../assets/xpert-logo.svg';
import './index.scss';

Expand All @@ -25,6 +26,7 @@ const ToggleXpert = ({
const [isModalOpen, setIsModalOpen] = useState(true);
const [target, setTarget] = useState(null);
const { userId } = getAuthenticatedUser();
const [ variationKey, experimentName ] = usePromptExperiment(userId);

Check failure on line 29 in src/components/ToggleXpertButton/index.jsx

View workflow job for this annotation

GitHub Actions / test

There should be no space after '['

Check failure on line 29 in src/components/ToggleXpertButton/index.jsx

View workflow job for this annotation

GitHub Actions / test

There should be no space before ']'

const handleClick = (event) => {
// log event if the tool is opened
Expand All @@ -35,6 +37,7 @@ const ToggleXpert = ({
course_id: courseId,
user_id: userId,
source: event.target.id === 'toggle-button' ? 'toggle' : 'cta',
...(variationKey ? { experiment_name: experimentName, variation_key: variationKey } : {}),
},
);
}
Expand All @@ -51,6 +54,7 @@ const ToggleXpert = ({
localStorage.setItem('dismissedLearningAssistantCallToAction', 'true');
sendTrackEvent('edx.ui.lms.learning_assistant.dismiss_action_message', {
course_id: courseId,
...(variationKey ? { experiment_name: experimentName, variation_key: variationKey } : {}),
});
};

Expand All @@ -63,6 +67,7 @@ const ToggleXpert = ({
course_id: courseId,
user_id: userId,
source: 'product-tour',
...(variationKey ? { experiment_name: experimentName, variation_key: variationKey } : {}),
},
);
};
Expand All @@ -78,7 +83,7 @@ const ToggleXpert = ({
(!isOpen && (
<div
className={
`toggle position-fixed closed d-flex flex-column justify-content-end align-items-end mx-3 border-0
`toggle position-fixed closed d-flex flex-column justify-content-end align-items-end mx-3 border-0
${chatMargin}`
}
>
Expand Down
14 changes: 12 additions & 2 deletions src/data/api.js
Original file line number Diff line number Diff line change
@@ -1,21 +1,31 @@
import { getConfig, snakeCaseObject } from '@edx/frontend-platform';
import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
import { getAuthenticatedHttpClient, getAuthenticatedUser } from '@edx/frontend-platform/auth';
import { usePromptExperiment, trackChatBotMessageOptimizely } from '../utils/optimizelyExperiment'

Check failure on line 3 in src/data/api.js

View workflow job for this annotation

GitHub Actions / test

Missing semicolon

async function fetchChatResponse(courseId, messageList, unitId) {
const payload = messageList.map((message) => ({
role: message?.role,
content: message?.content,
}));

const { userId } = getAuthenticatedUser();
const [ variationKey ] = usePromptExperiment(userId);

Check failure on line 12 in src/data/api.js

View workflow job for this annotation

GitHub Actions / test

There should be no space after '['

Check failure on line 12 in src/data/api.js

View workflow job for this annotation

GitHub Actions / test

There should be no space before ']'

Check failure on line 12 in src/data/api.js

View workflow job for this annotation

GitHub Actions / test

React Hook "usePromptExperiment" is called in function "fetchChatResponse" that is neither a React function component nor a custom React Hook function. React component names must start with an uppercase letter. React Hook names must start with the word "use"

let queryParams = { unitId };

if (variationKey) {
queryParams.responseVariation = variationKey

Check failure on line 17 in src/data/api.js

View workflow job for this annotation

GitHub Actions / test

Missing semicolon
trackChatBotMessageOptimizely(userId)

Check failure on line 18 in src/data/api.js

View workflow job for this annotation

GitHub Actions / test

Missing semicolon
}

queryParams = snakeCaseObject(queryParams);

let queryString = new URLSearchParams(queryParams);
queryString = queryString.toString();

const url = new URL(`${getConfig().CHAT_RESPONSE_URL}/${courseId}?${queryString}`);

const { data } = await getAuthenticatedHttpClient().post(url.href, payload);

return data;
}

Expand Down
5 changes: 5 additions & 0 deletions src/data/thunks.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { sendTrackEvent } from '@edx/frontend-platform/analytics';
import { getAuthenticatedUser } from '@edx/frontend-platform/auth';

import { usePromptExperiment } from '../utils/optimizelyExperiment';
import fetchChatResponse, { fetchLearningAssistantEnabled } from './api';
import {
setCurrentMessage,
Expand Down Expand Up @@ -33,13 +35,16 @@ export function addChatMessage(role, content, courseId) {
dispatch(resetApiError());

const { userId } = getAuthenticatedUser();
const [ variationKey, experimentName ] = usePromptExperiment(userId);

sendTrackEvent('edx.ui.lms.learning_assistant.message', {
id: conversationId,
course_id: courseId,
user_id: userId,
timestamp: message.timestamp,
role: message.role,
content: message.content,
...(variationKey ? { experiment_name: experimentName, variation_key: variationKey } : {}),
});
};
}
Expand Down
13 changes: 13 additions & 0 deletions src/utils/optimizelyExperiment.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { useDecision } from '@optimizely/react-sdk'
import optimizelyInstance from '../data/optimizely';

const PRODUCT_TOUR_EXP_KEY = 'la_product_tour';
Expand All @@ -11,6 +12,17 @@ const activateProductTourExperiment = (userId) => {
return variant === PRODUCT_TOUR_EXP_VARIATION;
};

const PROMPT_EXPERIMENT_FLAG = '_cosmo__xpert_gpt_4_0_prompt'
const PROMPT_EXPERIMENT_KEY = 'updated_prompt'

const usePromptExperiment = (userId) => {
const [{ enabled, variationKey }] = useDecision(PROMPT_EXPERIMENT_FLAG, { autoUpdate: true }, { id: userId })

const active = enabled && variationKey === PROMPT_EXPERIMENT_KEY

return active ? [variationKey, PROMPT_EXPERIMENT_FLAG] : []
}

const trackChatBotLaunchOptimizely = (userId, userAttributes = {}) => {
optimizelyInstance.track('learning_assistant_chat_click', userId, userAttributes);
};
Expand All @@ -23,4 +35,5 @@ export {
activateProductTourExperiment,
trackChatBotLaunchOptimizely,
trackChatBotMessageOptimizely,
usePromptExperiment,
};

0 comments on commit 10c5989

Please sign in to comment.