myavana-bot-test-core 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +0 -0
- package/package.json +16 -0
- package/src/ai.js +13 -0
- package/src/config.js +26 -0
- package/src/conversation.js +156 -0
- package/src/database.js +26 -0
- package/src/hairIssues.js +40 -0
- package/src/index.js +28 -0
- package/src/postResponseProcessing.js +205 -0
- package/src/session.js +41 -0
- package/src/user.js +37 -0
- package/src/utils.js +32 -0
package/README.md
ADDED
|
File without changes
|
package/package.json
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "myavana-bot-test-core",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Shared bot functionality",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
8
|
+
},
|
|
9
|
+
"dependencies": {
|
|
10
|
+
"@genkit-ai/googleai": "^0.9.12",
|
|
11
|
+
"dotenv": "^16.4.7",
|
|
12
|
+
"genkit": "^0.9.12",
|
|
13
|
+
"pg": "^8.13.1",
|
|
14
|
+
"redis": "^4.7.0"
|
|
15
|
+
}
|
|
16
|
+
}
|
package/src/ai.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// ai.js
|
|
2
|
+
const { genkit } = require('genkit');
|
|
3
|
+
const { googleAI, gemini15Flash, gemini20FlashExp } = require('@genkit-ai/googleai');
|
|
4
|
+
const config = require('./config');
|
|
5
|
+
|
|
6
|
+
const ai = genkit({
|
|
7
|
+
plugins: [googleAI({
|
|
8
|
+
apiKey: config.genkitApiKey,
|
|
9
|
+
})],
|
|
10
|
+
model: gemini20FlashExp,
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
module.exports = ai;
|
package/src/config.js
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// config.js
|
|
2
|
+
require('dotenv').config();
|
|
3
|
+
|
|
4
|
+
module.exports = {
|
|
5
|
+
dbConfig: {
|
|
6
|
+
host: process.env.DB_HOST,
|
|
7
|
+
user: process.env.DB_USER,
|
|
8
|
+
password: process.env.DB_PASSWORD,
|
|
9
|
+
database: process.env.DB_NAME,
|
|
10
|
+
port: parseInt(process.env.DB_PORT, 10) || 5432, // Default PostgreSQL port
|
|
11
|
+
},
|
|
12
|
+
redisConfig: {
|
|
13
|
+
username: process.env.REDIS_USERNAME,
|
|
14
|
+
password: process.env.REDIS_PASSWORD,
|
|
15
|
+
socket: {
|
|
16
|
+
host: process.env.REDIS_HOST,
|
|
17
|
+
port: parseInt(process.env.REDIS_PORT, 10) || 6379, // Default Redis port
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
},
|
|
21
|
+
genkitApiKey: process.env.GENKIT_API_KEY,
|
|
22
|
+
openaiApiKey: process.env.OPENAI_API_KEY,
|
|
23
|
+
defaultModel: 'gemini15Flash',
|
|
24
|
+
experimentalModel: 'gemini20FlashExp',
|
|
25
|
+
temperature: 1.1,
|
|
26
|
+
};
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
// conversation.js
|
|
2
|
+
const { pgClient, redisClient } = require('./database');
|
|
3
|
+
const ai = require('./ai');
|
|
4
|
+
const {gemini15Flash} = require("@genkit-ai/googleai");
|
|
5
|
+
|
|
6
|
+
const saveConversationSummary = async (conversationId, userId, summary) => {
|
|
7
|
+
const query = `
|
|
8
|
+
INSERT INTO conversations (conversation_id, user_id, summary)
|
|
9
|
+
VALUES ($1, $2, $3)
|
|
10
|
+
ON CONFLICT (conversation_id)
|
|
11
|
+
DO UPDATE SET summary = EXCLUDED.summary
|
|
12
|
+
RETURNING *;
|
|
13
|
+
`;
|
|
14
|
+
const values = [conversationId, userId, summary];
|
|
15
|
+
try {
|
|
16
|
+
const res = await pgClient.query(query, values);
|
|
17
|
+
await redisClient.set(`summary:${userId}`, summary, { EX: 3600 });
|
|
18
|
+
return res.rows[0];
|
|
19
|
+
} catch (err) {
|
|
20
|
+
console.error('Error saving conversation summary:', err);
|
|
21
|
+
throw err;
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const generateConversationSummary = async (chatHistory) => {
|
|
26
|
+
if (!chatHistory || chatHistory.length === 0) {
|
|
27
|
+
return "No conversation history available.";
|
|
28
|
+
}
|
|
29
|
+
const summaryPrompt = `Summarize the following conversation, getting all important information, especially the user's hair details, name, and location: ${JSON.stringify(chatHistory)}`;
|
|
30
|
+
const { text } = await ai.chat({
|
|
31
|
+
model: gemini15Flash,
|
|
32
|
+
system: summaryPrompt,
|
|
33
|
+
});
|
|
34
|
+
return text;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const getAllConversationsSummary = async (userId) => {
|
|
38
|
+
try {
|
|
39
|
+
// Check Redis cache first
|
|
40
|
+
const cachedSummary = await redisClient.get(`summary:${userId}`);
|
|
41
|
+
if (cachedSummary) return cachedSummary;
|
|
42
|
+
|
|
43
|
+
// If not in Redis, query PostgreSQL
|
|
44
|
+
const query = `SELECT summary FROM conversations WHERE user_id = $1 ORDER BY created_at DESC LIMIT 1;`;
|
|
45
|
+
const values = [userId];
|
|
46
|
+
const res = await pgClient.query(query, values);
|
|
47
|
+
|
|
48
|
+
if (Array.isArray(res.rows) && res.rows.length > 0 && res.rows[0]?.summary) {
|
|
49
|
+
// Cache the summary in Redis
|
|
50
|
+
await redisClient.set(`summary:${userId}`, res.rows[0].summary, { EX: 3600 }); // Cache for 1 hour
|
|
51
|
+
return res.rows[0].summary;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return 'conversation summary not available.'; // No summary available
|
|
55
|
+
} catch (err) {
|
|
56
|
+
console.error('Error retrieving all conversations summary:', err);
|
|
57
|
+
return 'conversation summary not available.';
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
const getConversationHistory = async (conversationId, userId) => {
|
|
62
|
+
try {
|
|
63
|
+
// Check Redis cache first
|
|
64
|
+
const cachedConversation = await redisClient.get(`conversation:${conversationId}`);
|
|
65
|
+
if (cachedConversation) return JSON.parse(cachedConversation).chatHistory;
|
|
66
|
+
|
|
67
|
+
// If not in Redis, query PostgreSQL
|
|
68
|
+
const query = `SELECT chat_history FROM conversations WHERE conversation_id = $1 AND user_id = $2;`;
|
|
69
|
+
const values = [conversationId, userId];
|
|
70
|
+
const res = await pgClient.query(query, values);
|
|
71
|
+
|
|
72
|
+
if (res.rows[0]) {
|
|
73
|
+
// Cache the conversation history in Redis
|
|
74
|
+
await redisClient.set(`conversation:${conversationId}`, JSON.stringify({ chatHistory: res.rows[0].chat_history }));
|
|
75
|
+
return res.rows[0].chat_history;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return []; // New conversation
|
|
79
|
+
} catch (err) {
|
|
80
|
+
console.error('Error retrieving conversation history:', err);
|
|
81
|
+
return []; // New conversation
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
const saveConversation = async (conversationId, userId, chatHistory, summary) => {
|
|
86
|
+
const query = `
|
|
87
|
+
INSERT INTO conversations (conversation_id, user_id, chat_history, summary)
|
|
88
|
+
VALUES ($1, $2, $3, $4)
|
|
89
|
+
ON CONFLICT (conversation_id)
|
|
90
|
+
DO UPDATE SET chat_history = EXCLUDED.chat_history, summary = EXCLUDED.summary
|
|
91
|
+
RETURNING *;
|
|
92
|
+
`;
|
|
93
|
+
const res = await pgClient.query(query, [conversationId, userId, JSON.stringify(chatHistory), summary]);
|
|
94
|
+
await redisClient.set(`conversation:${conversationId}`, JSON.stringify({ chatHistory: chatHistory }));
|
|
95
|
+
return res.rows[0];
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
const getAllChatsAsSingleArray = async (userId) => {
|
|
99
|
+
try {
|
|
100
|
+
const query = `
|
|
101
|
+
SELECT chat_history
|
|
102
|
+
FROM conversations
|
|
103
|
+
WHERE user_id = $1
|
|
104
|
+
ORDER BY created_at ASC;
|
|
105
|
+
`;
|
|
106
|
+
const values = [userId];
|
|
107
|
+
|
|
108
|
+
const res = await pgClient.query(query, values);
|
|
109
|
+
|
|
110
|
+
if (!res.rows || res.rows.length === 0) {
|
|
111
|
+
return [];
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
let allChats = [];
|
|
115
|
+
|
|
116
|
+
for (const row of res.rows) {
|
|
117
|
+
if (row.chat_history && Array.isArray(row.chat_history)) {
|
|
118
|
+
allChats = allChats.concat(row.chat_history);
|
|
119
|
+
} else if (row.chat_history) {
|
|
120
|
+
try {
|
|
121
|
+
const parsedHistory = JSON.parse(row.chat_history);
|
|
122
|
+
if (Array.isArray(parsedHistory)) {
|
|
123
|
+
allChats = allChats.concat(parsedHistory);
|
|
124
|
+
} else {
|
|
125
|
+
console.warn(`Unexpected chat_history format for user ${userId}:`, row.chat_history);
|
|
126
|
+
}
|
|
127
|
+
} catch (parseError) {
|
|
128
|
+
console.error(`Error parsing chat_history for user ${userId}:`, parseError, row.chat_history);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return allChats;
|
|
134
|
+
|
|
135
|
+
} catch (err) {
|
|
136
|
+
console.error('Error retrieving all chats:', err);
|
|
137
|
+
return [];
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
const generateAndSaveSummary = async (userId, conversationId, chatHistory) => {
|
|
142
|
+
const allChatz = await getAllChatsAsSingleArray(userId);
|
|
143
|
+
const newSummary = await generateConversationSummary(allChatz.length ? allChatz : chatHistory);
|
|
144
|
+
await saveConversationSummary(conversationId, userId, newSummary);
|
|
145
|
+
return newSummary;
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
module.exports = {
|
|
149
|
+
saveConversationSummary,
|
|
150
|
+
generateConversationSummary,
|
|
151
|
+
getAllConversationsSummary,
|
|
152
|
+
getConversationHistory,
|
|
153
|
+
saveConversation,
|
|
154
|
+
getAllChatsAsSingleArray,
|
|
155
|
+
generateAndSaveSummary
|
|
156
|
+
};
|
package/src/database.js
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// database.js
|
|
2
|
+
const { Client } = require('pg');
|
|
3
|
+
const { createClient } = require('redis');
|
|
4
|
+
const config = require('./config');
|
|
5
|
+
require('dotenv').config();
|
|
6
|
+
|
|
7
|
+
const pgClient = new Client(config.dbConfig);
|
|
8
|
+
const redisClient = createClient(config.redisConfig);
|
|
9
|
+
redisClient.on('error', (err) => console.error('Redis error:', err));
|
|
10
|
+
|
|
11
|
+
const connectDatabases = async () => {
|
|
12
|
+
try {
|
|
13
|
+
await pgClient.connect();
|
|
14
|
+
await redisClient.connect();
|
|
15
|
+
console.log('Connected to PostgreSQL and Redis');
|
|
16
|
+
} catch (err) {
|
|
17
|
+
console.error('Database connection error:', err);
|
|
18
|
+
process.exit(1);
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
module.exports = {
|
|
23
|
+
pgClient,
|
|
24
|
+
redisClient,
|
|
25
|
+
connectDatabases,
|
|
26
|
+
};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
// hairIssues.js
|
|
2
|
+
const { pgClient } = require('./database');
|
|
3
|
+
|
|
4
|
+
const saveHairIssue = async (userId, issueDescription, botAdvice) => {
|
|
5
|
+
const query = `
|
|
6
|
+
INSERT INTO hair_issues (user_id, issue_description, bot_advice, advice_given_at)
|
|
7
|
+
VALUES ($1, $2, $3, NOW())
|
|
8
|
+
RETURNING *;
|
|
9
|
+
`;
|
|
10
|
+
const values = [userId, issueDescription, botAdvice];
|
|
11
|
+
try {
|
|
12
|
+
const res = await pgClient.query(query, values);
|
|
13
|
+
return res.rows[0];
|
|
14
|
+
} catch (err) {
|
|
15
|
+
console.error('Error saving hair issue:', err);
|
|
16
|
+
return [];
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const getHairIssuesForUser = async (userId) => {
|
|
21
|
+
const query = `
|
|
22
|
+
SELECT *
|
|
23
|
+
FROM hair_issues
|
|
24
|
+
WHERE user_id = $1
|
|
25
|
+
ORDER BY reported_at DESC;
|
|
26
|
+
`;
|
|
27
|
+
const values = [userId];
|
|
28
|
+
try {
|
|
29
|
+
const res = await pgClient.query(query, values);
|
|
30
|
+
return res.rows; // Returns an array of hair issue objects
|
|
31
|
+
} catch (err) {
|
|
32
|
+
console.error('Error retrieving hair issues for user:', err);
|
|
33
|
+
return []
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
module.exports = {
|
|
38
|
+
saveHairIssue,
|
|
39
|
+
getHairIssuesForUser
|
|
40
|
+
}
|
package/src/index.js
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// Re-export all shared modules
|
|
2
|
+
module.exports = {
|
|
3
|
+
// Database and utilities
|
|
4
|
+
connectDatabases: require('./src/database').connectDatabases,
|
|
5
|
+
pgClient: require('./src/database').pgClient,
|
|
6
|
+
redisClient: require('./src/database').redisClient,
|
|
7
|
+
|
|
8
|
+
// AI and session management
|
|
9
|
+
ai: require('./src/ai'),
|
|
10
|
+
DatabaseSessionStore: require('./src/session'),
|
|
11
|
+
|
|
12
|
+
// User and conversation management
|
|
13
|
+
getUserDetails: require('./src/user').getUserDetails,
|
|
14
|
+
getAllConversationsSummary: require('./src/conversation').getAllConversationsSummary,
|
|
15
|
+
getConversationHistory: require('./src/conversation').getConversationHistory,
|
|
16
|
+
saveConversation: require('./src/conversation').saveConversation,
|
|
17
|
+
generateConversationSummary: require('./src/conversation').generateConversationSummary,
|
|
18
|
+
|
|
19
|
+
// Post-processing and hair issues
|
|
20
|
+
postProcessConversation: require('./src/postResponseProcessing').postProcessConversation,
|
|
21
|
+
postResponseProcessing: require('./src/postResponseProcessing').postResponseProcessing,
|
|
22
|
+
saveHairIssue: require('./src/hairIssues').saveHairIssue,
|
|
23
|
+
getHairIssuesForUser: require('./src/hairIssues').getHairIssuesForUser,
|
|
24
|
+
|
|
25
|
+
// Utilities
|
|
26
|
+
getAllProducts: require('./src/utils').getAllProducts,
|
|
27
|
+
getAllFaqs: require('./src/utils').getAllFaqs,
|
|
28
|
+
};
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
// postResponseProcessing.js
|
|
2
|
+
const ai = require("./ai");
|
|
3
|
+
const {gemini15Flash} = require("@genkit-ai/googleai");
|
|
4
|
+
const DatabaseSessionStore = require("./session");
|
|
5
|
+
const { pgClient, redisClient } = require('./database');
|
|
6
|
+
const {generateAndSaveSummary, saveConversationSummary, saveConversation} = require("./conversation");
|
|
7
|
+
const {saveHairIssue} = require("./hairIssues");
|
|
8
|
+
const {postProcessingPrompt} = require("./constructPrompt");
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
const processBotResponse = (botResponse) => {
|
|
12
|
+
if (!botResponse || !Array.isArray(botResponse) || botResponse.length === 0) {
|
|
13
|
+
return "No bot response received.";
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
let processedResponse = "";
|
|
17
|
+
for (const responseItem of botResponse) {
|
|
18
|
+
const messageType = responseItem.messageType;
|
|
19
|
+
const message = responseItem.message;
|
|
20
|
+
const metadata = responseItem.metadata;
|
|
21
|
+
|
|
22
|
+
if (messageType === 'html') {
|
|
23
|
+
// Extract plain text from HTML message (basic - can be improved for complex HTML)
|
|
24
|
+
const plainText = message ? message.replace(/<[^>]*>?/gm, '').trim() : '';
|
|
25
|
+
processedResponse += `Text Response: ${plainText}\n`;
|
|
26
|
+
} else if (metadata && metadata.templateId) {
|
|
27
|
+
const templateId = metadata.templateId;
|
|
28
|
+
if (templateId === '10') { // card
|
|
29
|
+
const payload = metadata.payload && metadata.payload[0];
|
|
30
|
+
if (payload) {
|
|
31
|
+
processedResponse += `Service Card: ${payload.title || 'No Title'}, ${payload.subtitle || 'No Subtitle'}, URL: ${payload.buttons && payload.buttons[0] && payload.buttons[0].action && payload.buttons[0].action.payload ? payload.buttons[0].action.payload.url : 'No URL'}\n`;
|
|
32
|
+
} else {
|
|
33
|
+
processedResponse += "Service Card: (No details extracted)\n";
|
|
34
|
+
}
|
|
35
|
+
} else if (templateId === '9') { // Image carousel
|
|
36
|
+
const payload = metadata.payload;
|
|
37
|
+
if (payload && Array.isArray(payload)) {
|
|
38
|
+
processedResponse += "Image Carousel: ";
|
|
39
|
+
payload.forEach((image, index) => {
|
|
40
|
+
processedResponse += `Image ${index + 1} - Caption: ${image.caption || 'No Caption'}, URL: ${image.url || 'No URL'}; `;
|
|
41
|
+
});
|
|
42
|
+
processedResponse += "\n";
|
|
43
|
+
} else {
|
|
44
|
+
processedResponse += "Image Carousel: (No images extracted)\n";
|
|
45
|
+
}
|
|
46
|
+
} else if (templateId === '3') { // Button template
|
|
47
|
+
const payload = metadata.payload && metadata.payload[0];
|
|
48
|
+
if (payload) {
|
|
49
|
+
processedResponse += `Button: ${payload.name || 'No Name'}, URL: ${payload.url || 'No URL'}\n`;
|
|
50
|
+
} else {
|
|
51
|
+
processedResponse += "Button: (No button details extracted)\n";
|
|
52
|
+
}
|
|
53
|
+
} else if (templateId === '6') { // Suggested replies
|
|
54
|
+
const payload = metadata.payload;
|
|
55
|
+
if (payload && Array.isArray(payload)) {
|
|
56
|
+
processedResponse += "Suggested Replies: ";
|
|
57
|
+
payload.forEach((reply, index) => {
|
|
58
|
+
processedResponse += `${reply.title || 'Reply ' + (index + 1)}; `;
|
|
59
|
+
});
|
|
60
|
+
processedResponse += "\n";
|
|
61
|
+
} else {
|
|
62
|
+
processedResponse += "Suggested Replies: (No replies extracted)\n";
|
|
63
|
+
}
|
|
64
|
+
} else if (templateId === '7') { // List template (Welcome message)
|
|
65
|
+
processedResponse += "Welcome Message Card (List Template)\n"; // Simplified for brevity
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
processedResponse += `Unknown Card Template (ID: ${templateId}): Message: ${message}\n`;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
else if (message) { // Fallback for any other message type (or just plain text if no html)
|
|
72
|
+
processedResponse += `Raw Message: ${message}\n`;
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
processedResponse += `Unknown Response Type: No message content found.\n`;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return processedResponse.trim(); // Remove trailing newline and whitespace
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
const postProcessConversation = async (chatHistory, message) => {
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
const extractionPrompt = await postProcessingPrompt(chatHistory, message);
|
|
86
|
+
console.log('Extraction prompt: ', extractionPrompt);
|
|
87
|
+
try {
|
|
88
|
+
const chat = ai.chat({
|
|
89
|
+
model: gemini15Flash,
|
|
90
|
+
config: {
|
|
91
|
+
temperature: 1.1,
|
|
92
|
+
},
|
|
93
|
+
|
|
94
|
+
});
|
|
95
|
+
const { output } = await chat.send(extractionPrompt);
|
|
96
|
+
console.log("Raw post-processing output:", JSON.stringify(output));
|
|
97
|
+
|
|
98
|
+
if (!output) {
|
|
99
|
+
console.warn("Post-processing output is empty. Returning default empty JSON.");
|
|
100
|
+
return {}; // Or return a default empty JSON object based on your schema if needed
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return output;
|
|
104
|
+
} catch (error) {
|
|
105
|
+
console.error("Error in postProcessConversation:", error);
|
|
106
|
+
return {}; // Return empty object in case of error
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
const updateUserProfile = async (userId, updates) => {
|
|
111
|
+
if (!updates || Object.keys(updates).length === 0) {
|
|
112
|
+
console.log("No user profile updates to apply.");
|
|
113
|
+
return null; // No updates to apply
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Filter out null or undefined values from updates
|
|
117
|
+
const validUpdates = Object.entries(updates)
|
|
118
|
+
.filter(([key, value]) => value !== null && value !== undefined)
|
|
119
|
+
.reduce((obj, [key, value]) => {
|
|
120
|
+
obj[key] = value;
|
|
121
|
+
return obj;
|
|
122
|
+
}, {});
|
|
123
|
+
|
|
124
|
+
if (Object.keys(validUpdates).length === 0) {
|
|
125
|
+
console.log("No valid user profile updates to apply (all values were null or undefined).");
|
|
126
|
+
return null; // No valid updates to apply
|
|
127
|
+
}
|
|
128
|
+
// Update chat session state here
|
|
129
|
+
try {
|
|
130
|
+
const session = await ai.loadSession(userId, {
|
|
131
|
+
store: new DatabaseSessionStore(),
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
// Update the session state
|
|
135
|
+
await session.updateState({
|
|
136
|
+
userProfile: validUpdates,
|
|
137
|
+
lastInteractionTime: new Date().toISOString(),
|
|
138
|
+
// ... any other state properties you need to update
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
}catch (e){
|
|
142
|
+
console.log(e)
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const updateKeys = Object.keys(validUpdates);
|
|
146
|
+
const updateValues = updateKeys.map(key => validUpdates[key]);
|
|
147
|
+
|
|
148
|
+
const setClauses = updateKeys.map((key, index) => `${key} = $${index + 2}`).join(', '); // Start parameter index from 2
|
|
149
|
+
|
|
150
|
+
const query = {
|
|
151
|
+
text: `
|
|
152
|
+
UPDATE users
|
|
153
|
+
SET ${setClauses}, updated_at = NOW()
|
|
154
|
+
WHERE user_id = $1
|
|
155
|
+
RETURNING *;
|
|
156
|
+
`,
|
|
157
|
+
values: [userId, ...updateValues], // userId is $1, updates are $2, $3, ...
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
try {
|
|
161
|
+
const res = await pgClient.query(query);
|
|
162
|
+
if (res.rows.length > 0) {
|
|
163
|
+
// Update Redis cache with the updated user profile
|
|
164
|
+
await redisClient.set(`user:${userId}`, JSON.stringify(res.rows[0]));
|
|
165
|
+
console.log(`User profile updated successfully for user ${userId} with fields: ${updateKeys.join(', ')}`);
|
|
166
|
+
return res.rows[0]; // Return the updated user object
|
|
167
|
+
} else {
|
|
168
|
+
console.warn(`User profile update failed for user ${userId}: User not found.`);
|
|
169
|
+
return null; // User not found or update failed
|
|
170
|
+
}
|
|
171
|
+
} catch (err) {
|
|
172
|
+
console.error(`Error updating user profile for user ${userId}:`, err);
|
|
173
|
+
throw err; // Propagate the error
|
|
174
|
+
}
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
const postResponseProcessing = async (postProcessData, userId, output, allchatsSummary, conversationId, chatHistory )=>{
|
|
178
|
+
if (postProcessData.user_profile_updates) {
|
|
179
|
+
// TODO: Update user profile in database based on postProcessData.user_profile_updates
|
|
180
|
+
console.log("User profile updates to be applied:", postProcessData.user_profile_updates);
|
|
181
|
+
await updateUserProfile(userId, postProcessData.user_profile_updates);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
if (postProcessData.hair_issue_extractions && postProcessData.hair_issue_extractions.hair_issues_reported && postProcessData.hair_issue_extractions.hair_issues_reported.length > 0) {
|
|
185
|
+
for (const issue of postProcessData.hair_issue_extractions.hair_issues_reported) {
|
|
186
|
+
await saveHairIssue(userId, issue, JSON.stringify(output)); // Or refine advice from output
|
|
187
|
+
console.log(`Hair issue reported and saved: ${issue}`);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
if (!allchatsSummary) {
|
|
193
|
+
await generateAndSaveSummary(userId, conversationId, chatHistory);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
await saveConversationSummary(conversationId, userId, allchatsSummary);
|
|
197
|
+
return await saveConversation(conversationId, userId, chatHistory, allchatsSummary);
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
module.exports = {
|
|
201
|
+
postResponseProcessing,
|
|
202
|
+
postProcessConversation,
|
|
203
|
+
processBotResponse,
|
|
204
|
+
updateUserProfile
|
|
205
|
+
}
|
package/src/session.js
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
// session.js
|
|
2
|
+
const { pgClient } = require('./database');
|
|
3
|
+
|
|
4
|
+
class DatabaseSessionStore {
|
|
5
|
+
async get(sessionId) {
|
|
6
|
+
const query = `
|
|
7
|
+
SELECT session_data
|
|
8
|
+
FROM sessions
|
|
9
|
+
WHERE session_id = $1;
|
|
10
|
+
`;
|
|
11
|
+
const values = [sessionId];
|
|
12
|
+
try {
|
|
13
|
+
const res = await pgClient.query(query, values);
|
|
14
|
+
if (res.rows.length > 0 && res.rows[0].session_data) {
|
|
15
|
+
return res.rows[0].session_data;
|
|
16
|
+
}
|
|
17
|
+
return undefined; // Session not found or no data
|
|
18
|
+
} catch (err) {
|
|
19
|
+
console.error('Error retrieving session data from database:', err);
|
|
20
|
+
return undefined;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async save(sessionId, sessionData) {
|
|
25
|
+
const query = `
|
|
26
|
+
INSERT INTO sessions (session_id, user_id, session_data, last_accessed_at)
|
|
27
|
+
VALUES ($1, $2, $3, NOW())
|
|
28
|
+
ON CONFLICT (session_id)
|
|
29
|
+
DO UPDATE SET session_data = $3, last_accessed_at = NOW();
|
|
30
|
+
`;
|
|
31
|
+
const values = [sessionId, sessionId, sessionData];
|
|
32
|
+
try {
|
|
33
|
+
await pgClient.query(query, values);
|
|
34
|
+
console.log(`Session data saved to database for sessionId: ${sessionId}`);
|
|
35
|
+
} catch (err) {
|
|
36
|
+
console.error('Error saving session data to database:', err);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
module.exports = DatabaseSessionStore;
|
package/src/user.js
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// user.js
|
|
2
|
+
const { pgClient, redisClient } = require('./database');
|
|
3
|
+
|
|
4
|
+
const getUserDetails = async (userId) => {
|
|
5
|
+
try {
|
|
6
|
+
const cachedUser = await redisClient.get(`user:${userId}`);
|
|
7
|
+
if (cachedUser) return JSON.parse(cachedUser);
|
|
8
|
+
|
|
9
|
+
const query = `SELECT * FROM users WHERE user_id = $1;`;
|
|
10
|
+
const values = [userId];
|
|
11
|
+
const res = await pgClient.query(query, values);
|
|
12
|
+
|
|
13
|
+
if (res.rows[0]) {
|
|
14
|
+
await redisClient.set(`user:${userId}`, JSON.stringify(res.rows[0]));
|
|
15
|
+
return res.rows[0];
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const createUserQuery = `
|
|
19
|
+
INSERT INTO users (user_id)
|
|
20
|
+
VALUES ($1)
|
|
21
|
+
RETURNING *;
|
|
22
|
+
`;
|
|
23
|
+
const createUserRes = await pgClient.query(createUserQuery, [userId]);
|
|
24
|
+
if (createUserRes.rows[0]) {
|
|
25
|
+
await redisClient.set(`user:${userId}`, JSON.stringify(createUserRes.rows[0]));
|
|
26
|
+
return createUserRes.rows[0];
|
|
27
|
+
}
|
|
28
|
+
return null;
|
|
29
|
+
} catch (err) {
|
|
30
|
+
console.error('Error retrieving user details:', err);
|
|
31
|
+
throw err;
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
module.exports = {
|
|
36
|
+
getUserDetails,
|
|
37
|
+
};
|
package/src/utils.js
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
const { pgClient } = require('./database'); // Import pgClient from core's database.js
|
|
2
|
+
|
|
3
|
+
async function getAllProducts() {
|
|
4
|
+
try {
|
|
5
|
+
const result = await pgClient.query('SELECT * FROM products');
|
|
6
|
+
return result.rows;
|
|
7
|
+
} catch (error) {
|
|
8
|
+
console.error("Error fetching all products:", error);
|
|
9
|
+
return [];
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
async function getAllFaqs() {
|
|
14
|
+
try {
|
|
15
|
+
const result = await pgClient.query('SELECT * FROM faqs');
|
|
16
|
+
return result.rows;
|
|
17
|
+
} catch (error) {
|
|
18
|
+
console.error("Error fetching all FAQs:", error);
|
|
19
|
+
return [];
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
async function getHairIssuesForUser(userId) {
|
|
24
|
+
// Return empty for now (or implement logic later)
|
|
25
|
+
return [];
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
module.exports = {
|
|
29
|
+
getAllProducts,
|
|
30
|
+
getAllFaqs,
|
|
31
|
+
getHairIssuesForUser,
|
|
32
|
+
};
|