Skip to content

Commit 2ebcf19

Browse files
authored
Merge pull request #37 from necyberteam/metrics-flow
Adding metrics flow
2 parents 59f51c3 + 3589c27 commit 2ebcf19

File tree

8 files changed

+160
-5
lines changed

8 files changed

+160
-5
lines changed

build/static/js/main.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

build/static/js/main.js.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/components/QABot.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,6 @@ const QABotInternal = React.forwardRef((props, botRef) => {
121121

122122
// loads plugins to be passed into chatbot, matching pattern in docs
123123
const plugins = [HtmlRenderer(), MarkdownRenderer(), InputValidator()];
124-
125124
const flow = useMemo(() => createBotFlow({
126125
welcomeMessage,
127126
isBotLoggedIn,

src/config/constants.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ export const DEFAULT_CONFIG = {
66
WELCOME_MESSAGE_LOGOUT_TRANSITION: 'You have been logged out.',
77
API_ENDPOINT: 'https://access-ai-grace1-external.ccs.uky.edu/access/chat/api/',
88
RATING_ENDPOINT: 'https://access-ai-grace1-external.ccs.uky.edu/access/chat/rating/',
9+
METRICS_API_ENDPOINT: 'https://access-ai-grace1-external.ccs.uky.edu/access/xdmod/chat/api/',
10+
METRICS_RATING_ENDPOINT: 'https://access-ai-grace1-external.ccs.uky.edu/access/xdmod/chat/rating/',
11+
METRICS_QUESTIONS_URL: 'https://metrics.access-ci.org/qa_bot_faq',
912

1013
// Netlify function URL - this should point to the Netlify functions endpoint for ticket submission
1114
// NOT the Q&A API endpoint
@@ -45,6 +48,18 @@ export const getRatingEndpoint = () => {
4548
: DEFAULT_CONFIG.RATING_ENDPOINT;
4649
};
4750

51+
export const getMetricsApiEndpoint = () => {
52+
return (typeof process !== 'undefined' && process.env?.REACT_APP_METRICS_API_ENDPOINT)
53+
? process.env.REACT_APP_METRICS_API_ENDPOINT
54+
: DEFAULT_CONFIG.METRICS_API_ENDPOINT;
55+
};
56+
57+
export const getMetricsRatingEndpoint = () => {
58+
return (typeof process !== 'undefined' && process.env?.REACT_APP_METRICS_RATING_ENDPOINT)
59+
? process.env.REACT_APP_METRICS_RATING_ENDPOINT
60+
: DEFAULT_CONFIG.METRICS_RATING_ENDPOINT;
61+
};
62+
4863
// Helper functions from strings.js
4964
export const buildWelcomeMessage = (isLoggedIn, welcomeMessage) => {
5065
if (isLoggedIn) {

src/utils/create-bot-flow.js

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import LoginButton from '../components/LoginButton';
33
import { buildWelcomeMessage } from '../config/constants';
44
import { createMainMenuFlow } from './flows/main-menu-flow';
55
import { createQAFlow } from './flows/qa-flow';
6+
import { createMetricsFlow } from './flows/metrics-flow';
67
import { createTicketFlow } from './flows/ticket-flow';
78
// import { createFeedbackFlow } from './flows/feedback-flow';
89
import { createSecurityFlow } from './flows/security-flow';
@@ -53,6 +54,27 @@ function createBotFlow({
5354
}
5455
};
5556

57+
// Create metrics flow (requires login)
58+
const metricsFlow = isBotLoggedIn
59+
? createMetricsFlow({
60+
sessionId,
61+
apiKey
62+
})
63+
: {
64+
metrics_intro: {
65+
message: "To investigate metrics, you need to log in first.",
66+
component: <LoginButton loginUrl={loginUrl} />,
67+
options: ["Back to Main Menu"],
68+
chatDisabled: true,
69+
path: (chatState) => {
70+
if (chatState.userInput === "Back to Main Menu") {
71+
return "start";
72+
}
73+
return "metrics_intro";
74+
}
75+
}
76+
};
77+
5678
// Create ticket and feedback flows (available to everyone)
5779
const ticketFlow = createTicketFlow({
5880
ticketForm,
@@ -77,6 +99,7 @@ function createBotFlow({
7799
const flow = {
78100
...(mainMenuFlow || {}),
79101
...(qaFlow || {}),
102+
...(metricsFlow || {}),
80103
...(ticketFlow || {}),
81104
//...(feedbackFlow || {}), // TODO: add feedback flow back in
82105
...(securityFlow || {})

src/utils/flows/main-menu-flow.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ export const createMainMenuFlow = ({
1818
options: [
1919
"Ask a question about ACCESS", // AI loop
2020
"Open a Help Ticket", // Jira Ticket
21+
"Usage and performance of ACCESS resources (XDMoD)", // Metrics
2122
// "Provide feedback to ACCESS", // TODO: Add feedback flow back in
2223
"Report a security issue" // Security
2324
],
@@ -29,6 +30,8 @@ export const createMainMenuFlow = ({
2930
// Reset form data
3031
setTicketForm({});
3132
return "help_ticket";
33+
} else if (chatState.userInput === "Usage and performance of ACCESS resources (XDMoD)") {
34+
return "metrics_intro";
3235
// } else if (chatState.userInput === "Provide feedback to ACCESS") {
3336
// // Reset form data
3437
// setFeedbackForm({});

src/utils/flows/metrics-flow.js

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
import { handleBotError } from '../error-handler';
2+
import { DEFAULT_CONFIG, getMetricsApiEndpoint, getMetricsRatingEndpoint } from '../../config/constants';
3+
import { v4 as uuidv4 } from 'uuid';
4+
import { getProcessedText } from '../getProcessedText';
5+
6+
/**
7+
* Creates the metrics conversation flow
8+
*
9+
* @param {Object} params Configuration
10+
* @param {string} params.sessionId Current session ID
11+
* @param {string} params.apiKey API key for authentication
12+
* @returns {Object} Metrics flow configuration
13+
*/
14+
export const createMetricsFlow = ({ sessionId, apiKey }) => {
15+
// Track the query ID for the most recent response that can receive feedback
16+
let feedbackQueryId = null;
17+
return {
18+
metrics_intro: {
19+
message: `Please type your question about usage and performance metrics (XDMoD) below. You can see some <a target="_blank" href="${DEFAULT_CONFIG.METRICS_QUESTIONS_URL}">examples here</a>.`,
20+
renderHtml: ["BOT"],
21+
path: "metrics_loop"
22+
},
23+
metrics_loop: {
24+
message: async (chatState) => {
25+
const { userInput } = chatState;
26+
27+
// Handle feedback first if it's feedback
28+
if (userInput === "👍 Helpful" || userInput === "👎 Not helpful") {
29+
30+
// Send feedback using the captured query ID
31+
if (apiKey && sessionId && feedbackQueryId) {
32+
const isPositive = userInput === "👍 Helpful";
33+
const headers = {
34+
'Content-Type': 'application/json',
35+
'X-Origin': 'metrics',
36+
'X-API-KEY': apiKey,
37+
'X-Session-ID': sessionId,
38+
'X-Query-ID': feedbackQueryId,
39+
'X-Feedback': isPositive ? 1 : 0
40+
};
41+
42+
const endpoint = getMetricsRatingEndpoint();
43+
44+
try {
45+
await fetch(endpoint, {
46+
method: 'POST',
47+
headers
48+
});
49+
} catch (error) {
50+
console.error('Error sending metrics feedback:', error);
51+
}
52+
}
53+
return "Thanks for the feedback! Ask another question about usage and performance metrics (XDMoD) or start a new chat.";
54+
} else {
55+
// Process as a question - fetch response directly
56+
try {
57+
// Generate our own query ID since we're bypassing useHandleAIQuery
58+
const queryId = uuidv4();
59+
feedbackQueryId = queryId;
60+
61+
const headers = {
62+
'Content-Type': 'application/json',
63+
'X-Origin': 'metrics',
64+
'X-API-KEY': apiKey,
65+
'X-Session-ID': sessionId,
66+
'X-Query-ID': queryId
67+
};
68+
69+
const response = await fetch(getMetricsApiEndpoint(), {
70+
method: 'POST',
71+
headers,
72+
body: JSON.stringify({
73+
query: userInput
74+
})
75+
});
76+
77+
const body = await response.json();
78+
const text = body.response;
79+
const processedText = getProcessedText(text);
80+
81+
82+
// Inject the response
83+
await chatState.injectMessage(processedText);
84+
85+
// Inject guidance message after a short delay to let options render first
86+
setTimeout(async () => {
87+
await chatState.injectMessage("Ask another question about usage and performance metrics (XDMoD) or start a new chat.");
88+
}, 100);
89+
90+
return null;
91+
} catch (error) {
92+
console.error('Error in metrics flow:', error);
93+
return handleBotError(error);
94+
}
95+
}
96+
},
97+
renderMarkdown: ["BOT"],
98+
options: (chatState) => {
99+
// Only show feedback options if the input isn't already feedback
100+
if (chatState.userInput === "👍 Helpful" || chatState.userInput === "👎 Not helpful") {
101+
return []; // No options after feedback is given
102+
}
103+
return ["👍 Helpful", "👎 Not helpful"];
104+
},
105+
chatDisabled: false,
106+
path: "metrics_loop"
107+
},
108+
};
109+
};

src/utils/flows/qa-flow.js

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ export const createQAFlow = ({ sessionId, apiKey }) => {
1717

1818
return {
1919
go_ahead_and_ask: {
20-
message: "Please type your question.",
20+
message: "Please type your question about ACCESS below.",
2121
path: "qa_loop"
2222
},
2323
qa_loop: {
@@ -50,7 +50,7 @@ export const createQAFlow = ({ sessionId, apiKey }) => {
5050
console.error('Error sending feedback:', error);
5151
}
5252
}
53-
return "Thanks for the feedback! Feel free to ask another question.";
53+
return "Thanks for the feedback! Ask another question about ACCESS or start a new chat.";
5454
} else {
5555
// Process as a question - fetch response directly
5656
try {
@@ -81,6 +81,12 @@ export const createQAFlow = ({ sessionId, apiKey }) => {
8181

8282
// Inject the response
8383
await chatState.injectMessage(processedText);
84+
85+
// Inject guidance message after a short delay to let options render first
86+
setTimeout(async () => {
87+
await chatState.injectMessage("Ask another question about ACCESS or start a new chat.");
88+
}, 100);
89+
8490
return null;
8591
} catch (error) {
8692
console.error('Error in bot flow:', error);

0 commit comments

Comments
 (0)