@@ -0,0 +1,77 @@
|
|||||||
|
const postgre = require('../database/postgre');
|
||||||
|
|
||||||
|
const analytics = async (req, res) => {
|
||||||
|
try {
|
||||||
|
const query = `
|
||||||
|
SELECT
|
||||||
|
'24h' AS period,
|
||||||
|
COUNT(DISTINCT session_id) AS sessions,
|
||||||
|
COUNT(*) FILTER (WHERE status = 1) AS chats,
|
||||||
|
COUNT(DISTINCT user_id) AS users,
|
||||||
|
COUNT(*) AS queries
|
||||||
|
FROM useraskquestion
|
||||||
|
WHERE created >= NOW() - INTERVAL '24 hours'
|
||||||
|
|
||||||
|
UNION ALL
|
||||||
|
|
||||||
|
SELECT
|
||||||
|
'7d',
|
||||||
|
COUNT(DISTINCT session_id),
|
||||||
|
COUNT(*) FILTER (WHERE status = 1),
|
||||||
|
COUNT(DISTINCT user_id),
|
||||||
|
COUNT(*)
|
||||||
|
FROM useraskquestion
|
||||||
|
WHERE created >= NOW() - INTERVAL '7 days'
|
||||||
|
|
||||||
|
UNION ALL
|
||||||
|
|
||||||
|
SELECT
|
||||||
|
'30d',
|
||||||
|
COUNT(DISTINCT session_id),
|
||||||
|
COUNT(*) FILTER (WHERE status = 1),
|
||||||
|
COUNT(DISTINCT user_id),
|
||||||
|
COUNT(*)
|
||||||
|
FROM useraskquestion
|
||||||
|
WHERE created >= NOW() - INTERVAL '30 days'
|
||||||
|
|
||||||
|
UNION ALL
|
||||||
|
|
||||||
|
SELECT
|
||||||
|
'all',
|
||||||
|
COUNT(DISTINCT session_id),
|
||||||
|
COUNT(*) FILTER (WHERE status = 1),
|
||||||
|
COUNT(DISTINCT user_id),
|
||||||
|
COUNT(*)
|
||||||
|
FROM useraskquestion;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const result = await postgre.query(query);
|
||||||
|
|
||||||
|
const response = {};
|
||||||
|
|
||||||
|
result.rows.forEach(row => {
|
||||||
|
response[row.period] = {
|
||||||
|
sessions: Number(row.sessions),
|
||||||
|
chats: Number(row.chats),
|
||||||
|
users: Number(row.users),
|
||||||
|
queries: Number(row.queries),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
return res.status(200).json({
|
||||||
|
success: true,
|
||||||
|
data: response,
|
||||||
|
});
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Analytics Error:", error);
|
||||||
|
return res.status(500).json({
|
||||||
|
success: false,
|
||||||
|
message: error.message,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
analytics,
|
||||||
|
};
|
||||||
+129
-32
@@ -6,6 +6,51 @@ const OpenAI = require("openai");
|
|||||||
const { pipeline } = require("@xenova/transformers");
|
const { pipeline } = require("@xenova/transformers");
|
||||||
const { QdrantClient } = require("@qdrant/js-client-rest");
|
const { QdrantClient } = require("@qdrant/js-client-rest");
|
||||||
|
|
||||||
|
const { v4: uuidv4 } = require("uuid");
|
||||||
|
|
||||||
|
async function getOrCreateSession(user_id) {
|
||||||
|
const result = await postgre.query(
|
||||||
|
`
|
||||||
|
SELECT *
|
||||||
|
FROM user_sessions
|
||||||
|
WHERE user_id = $1
|
||||||
|
AND is_active = true
|
||||||
|
AND last_activity > NOW() - INTERVAL '30 minutes'
|
||||||
|
ORDER BY last_activity DESC
|
||||||
|
LIMIT 1
|
||||||
|
`,
|
||||||
|
[user_id]
|
||||||
|
);
|
||||||
|
|
||||||
|
if (result.rows.length) {
|
||||||
|
const session = result.rows[0];
|
||||||
|
|
||||||
|
await postgre.query(
|
||||||
|
`UPDATE user_sessions
|
||||||
|
SET last_activity = NOW()
|
||||||
|
WHERE id = $1`,
|
||||||
|
[session.id]
|
||||||
|
);
|
||||||
|
|
||||||
|
return session.session_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
const session_id = uuidv4();
|
||||||
|
|
||||||
|
await postgre.query(
|
||||||
|
`
|
||||||
|
INSERT INTO user_sessions
|
||||||
|
(user_id, session_id)
|
||||||
|
VALUES ($1, $2)
|
||||||
|
`,
|
||||||
|
[user_id, session_id]
|
||||||
|
);
|
||||||
|
|
||||||
|
return session_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const CONFIG = {
|
const CONFIG = {
|
||||||
azure: {
|
azure: {
|
||||||
endpoint: process.env.AZURE_OPENAI_ENDPOINT,
|
endpoint: process.env.AZURE_OPENAI_ENDPOINT,
|
||||||
@@ -80,32 +125,73 @@ function buildContext(results) {
|
|||||||
.join("\n\n---\n\n");
|
.join("\n\n---\n\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// const SYSTEM_PROMPT = `
|
||||||
|
// You are CPM AI Assistant.
|
||||||
|
|
||||||
|
// Rules:
|
||||||
|
// - Answer only from the provided information.
|
||||||
|
// - If the answer is not available, reply exactly:
|
||||||
|
// "❌ I could not find this information in the uploaded documents."
|
||||||
|
// - Do not make up information.
|
||||||
|
// - Do not mention documents, context, or chunks.
|
||||||
|
// - Reply in the same language and style as the user's question.
|
||||||
|
// - If the user asks in Hindi, answer in Hindi.
|
||||||
|
// - If the user asks in Hinglish, answer in Hinglish.
|
||||||
|
// - If the user asks in English, answer in English.
|
||||||
|
|
||||||
|
// Response Style:
|
||||||
|
// - Use simple and easy-to-understand language.
|
||||||
|
// - Keep answers short and clear.
|
||||||
|
// - Use headings and bullet points when helpful.
|
||||||
|
// - Highlight important words in **bold**.
|
||||||
|
|
||||||
|
// Format:
|
||||||
|
|
||||||
|
// # 📋 Topic
|
||||||
|
|
||||||
|
// ## 🎯 Summary
|
||||||
|
// Short answer in the user's language.
|
||||||
|
|
||||||
|
// ## ✅ Details
|
||||||
|
// - Point 1
|
||||||
|
// - Point 2
|
||||||
|
// - Point 3
|
||||||
|
|
||||||
|
// ## ⚠️ Notes
|
||||||
|
// - Extra information (if available).
|
||||||
|
// `.trim();
|
||||||
|
|
||||||
const SYSTEM_PROMPT = `
|
const SYSTEM_PROMPT = `
|
||||||
You are CPM AI Assistant.
|
You are CPM AI Assistant.
|
||||||
|
|
||||||
Rules:
|
RULES:
|
||||||
- Answer only from the provided information.
|
- Answer only using the provided context.
|
||||||
- If the answer is not available, reply exactly:
|
- If the answer is not available in the context, reply exactly:
|
||||||
"❌ I could not find this information in the uploaded documents."
|
"❌ I could not find this information in the uploaded documents."
|
||||||
- Do not make up information.
|
- Do not make up information.
|
||||||
- Do not mention documents, context, or chunks.
|
- Do not use external knowledge.
|
||||||
- Reply in the same language and style as the user's question.
|
- Do not mention context, documents, chunks, or sources.
|
||||||
- If the user asks in Hindi, answer in Hindi.
|
|
||||||
- If the user asks in Hinglish, answer in Hinglish.
|
|
||||||
- If the user asks in English, answer in English.
|
|
||||||
|
|
||||||
Response Style:
|
STRICT OUTPUT RULE:
|
||||||
- Use simple and easy-to-understand language.
|
- NEVER include words like "documents.", "context", "chunk", or similar metadata in the final answer.
|
||||||
- Keep answers short and clear.
|
- NEVER end the response with the word "documents." or any system-related word.
|
||||||
- Use headings and bullet points when helpful.
|
- Ensure the final sentence always ends naturally and cleanly.
|
||||||
|
|
||||||
|
LANGUAGE RULE:
|
||||||
|
- Reply in the same language as the user (English, Hindi, Hinglish).
|
||||||
|
|
||||||
|
RESPONSE STYLE:
|
||||||
|
- Use simple, clear language.
|
||||||
|
- Keep answers short and structured.
|
||||||
|
- Use headings and bullet points when needed.
|
||||||
- Highlight important words in **bold**.
|
- Highlight important words in **bold**.
|
||||||
|
|
||||||
Format:
|
FORMAT:
|
||||||
|
|
||||||
# 📋 Topic
|
# 📋 Topic
|
||||||
|
|
||||||
## 🎯 Summary
|
## 🎯 Summary
|
||||||
Short answer in the user's language.
|
Short answer.
|
||||||
|
|
||||||
## ✅ Details
|
## ✅ Details
|
||||||
- Point 1
|
- Point 1
|
||||||
@@ -113,9 +199,10 @@ Short answer in the user's language.
|
|||||||
- Point 3
|
- Point 3
|
||||||
|
|
||||||
## ⚠️ Notes
|
## ⚠️ Notes
|
||||||
- Extra information (if available).
|
- Only if needed
|
||||||
`.trim();
|
`.trim();
|
||||||
|
|
||||||
|
|
||||||
async function askLLM(question, context) {
|
async function askLLM(question, context) {
|
||||||
const completion = await llm.chat.completions.create({
|
const completion = await llm.chat.completions.create({
|
||||||
model: CONFIG.azure.deployment,
|
model: CONFIG.azure.deployment,
|
||||||
@@ -184,7 +271,8 @@ const askstream = async (req, res) => {
|
|||||||
|
|
||||||
const { question } = req.body ?? {};
|
const { question } = req.body ?? {};
|
||||||
const user_id = req.user.id;
|
const user_id = req.user.id;
|
||||||
console.log("Received question:", user_id);
|
const session_id = await getOrCreateSession(user_id);
|
||||||
|
|
||||||
|
|
||||||
if (!question?.trim()) {
|
if (!question?.trim()) {
|
||||||
return res.status(400).json({ success: false, error: "question is required" });
|
return res.status(400).json({ success: false, error: "question is required" });
|
||||||
@@ -207,10 +295,7 @@ const askstream = async (req, res) => {
|
|||||||
|
|
||||||
|
|
||||||
if (!results.length) {
|
if (!results.length) {
|
||||||
const result = await postgre.query(
|
console.log("No results found for question:", question);
|
||||||
'insert into useraskquestion (user_id, questions) values ($1, $2)',
|
|
||||||
[user_id, question]
|
|
||||||
);
|
|
||||||
send("token", { token: "❌", isWord: true });
|
send("token", { token: "❌", isWord: true });
|
||||||
send("token", { token: "I", isWord: true });
|
send("token", { token: "I", isWord: true });
|
||||||
send("token", { token: "could", isWord: true });
|
send("token", { token: "could", isWord: true });
|
||||||
@@ -224,14 +309,25 @@ const askstream = async (req, res) => {
|
|||||||
send("token", { token: "documents.", isWord: true });
|
send("token", { token: "documents.", isWord: true });
|
||||||
send("done", { sources: [] });
|
send("done", { sources: [] });
|
||||||
|
|
||||||
|
await postgre.query(
|
||||||
|
`
|
||||||
|
INSERT INTO useraskquestion
|
||||||
|
(user_id, session_id, questions, status)
|
||||||
|
VALUES ($1, $2, $3, $4)
|
||||||
|
`,
|
||||||
|
[user_id, session_id, question, '0']
|
||||||
|
);
|
||||||
|
|
||||||
return res.end();
|
return res.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = await postgre.query(
|
await postgre.query(
|
||||||
'insert into useraskquestion (user_id, questions,status) values ($1, $2, $3)',
|
`
|
||||||
[user_id, question,'1']
|
INSERT INTO useraskquestion
|
||||||
|
(user_id, session_id, questions, status)
|
||||||
|
VALUES ($1, $2, $3, $4)
|
||||||
|
`,
|
||||||
|
[user_id, session_id, question, '1']
|
||||||
);
|
);
|
||||||
|
|
||||||
const sources = results.map(r => ({
|
const sources = results.map(r => ({
|
||||||
@@ -255,8 +351,6 @@ const askstream = async (req, res) => {
|
|||||||
{ role: "user", content: `Context:\n${context}\n\nQuestion:\n${question}` },
|
{ role: "user", content: `Context:\n${context}\n\nQuestion:\n${question}` },
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
// Buffer to handle tokens that may be split mid-word
|
|
||||||
let wordBuffer = "";
|
let wordBuffer = "";
|
||||||
|
|
||||||
for await (const chunk of stream) {
|
for await (const chunk of stream) {
|
||||||
@@ -264,12 +358,7 @@ const askstream = async (req, res) => {
|
|||||||
if (!rawToken) continue;
|
if (!rawToken) continue;
|
||||||
|
|
||||||
wordBuffer += rawToken;
|
wordBuffer += rawToken;
|
||||||
|
|
||||||
// Split on whitespace — emit complete words, keep trailing partial
|
|
||||||
// We preserve newlines/markdown as separate tokens so markdown renders correctly
|
|
||||||
const parts = wordBuffer.split(/(\s+)/);
|
const parts = wordBuffer.split(/(\s+)/);
|
||||||
|
|
||||||
// Last element might be an incomplete word — buffer it
|
|
||||||
wordBuffer = parts.pop() ?? "";
|
wordBuffer = parts.pop() ?? "";
|
||||||
|
|
||||||
for (const part of parts) {
|
for (const part of parts) {
|
||||||
@@ -279,8 +368,16 @@ const askstream = async (req, res) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Flush any remaining buffered text
|
|
||||||
|
|
||||||
if (wordBuffer) {
|
if (wordBuffer) {
|
||||||
|
console.log("Emitting buffered token:", wordBuffer);
|
||||||
|
if (wordBuffer.trim() == "documents.") {
|
||||||
|
const result = await postgre.query(
|
||||||
|
`UPDATE useraskquestion SET status = $1 WHERE session_id = $2 RETURNING *`
|
||||||
|
, ['0', session_id]
|
||||||
|
);
|
||||||
|
}
|
||||||
send("token", { token: wordBuffer, isWord: true });
|
send("token", { token: wordBuffer, isWord: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Generated
+14
@@ -28,6 +28,7 @@
|
|||||||
"pdf-parse": "^2.4.5",
|
"pdf-parse": "^2.4.5",
|
||||||
"pdfjs-dist": "^6.0.227",
|
"pdfjs-dist": "^6.0.227",
|
||||||
"pg": "^8.21.0",
|
"pg": "^8.21.0",
|
||||||
|
"uuid": "^14.0.0",
|
||||||
"yaml": "^2.9.0"
|
"yaml": "^2.9.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -5530,6 +5531,19 @@
|
|||||||
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
|
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/uuid": {
|
||||||
|
"version": "14.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/uuid/-/uuid-14.0.0.tgz",
|
||||||
|
"integrity": "sha512-Qo+uWgilfSmAhXCMav1uYFynlQO7fMFiMVZsQqZRMIXp0O7rR7qjkj+cPvBHLgBqi960QCoo/PH2/6ZtVqKvrg==",
|
||||||
|
"funding": [
|
||||||
|
"https://github.com/sponsors/broofa",
|
||||||
|
"https://github.com/sponsors/ctavan"
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"bin": {
|
||||||
|
"uuid": "dist-node/bin/uuid"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/validate-npm-package-name": {
|
"node_modules/validate-npm-package-name": {
|
||||||
"version": "5.0.1",
|
"version": "5.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-5.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-5.0.1.tgz",
|
||||||
|
|||||||
@@ -30,6 +30,7 @@
|
|||||||
"pdf-parse": "^2.4.5",
|
"pdf-parse": "^2.4.5",
|
||||||
"pdfjs-dist": "^6.0.227",
|
"pdfjs-dist": "^6.0.227",
|
||||||
"pg": "^8.21.0",
|
"pg": "^8.21.0",
|
||||||
|
"uuid": "^14.0.0",
|
||||||
"yaml": "^2.9.0"
|
"yaml": "^2.9.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-1
@@ -5,6 +5,7 @@ const authMiddleware = require('../middleware/authMiddleware.js');
|
|||||||
const askQuestion = require('../controller/askQuestion.js');
|
const askQuestion = require('../controller/askQuestion.js');
|
||||||
const answerUnanswerdQuestion = require('../controller/answerUnanswerdQuestion.js');
|
const answerUnanswerdQuestion = require('../controller/answerUnanswerdQuestion.js');
|
||||||
const qdrantinsert = require('../controller/qdrantinsert.js');
|
const qdrantinsert = require('../controller/qdrantinsert.js');
|
||||||
|
const Dashboard = require('../controller/Dashboard.js');
|
||||||
const multer = require("multer");
|
const multer = require("multer");
|
||||||
|
|
||||||
const upload = multer({
|
const upload = multer({
|
||||||
@@ -21,7 +22,7 @@ router.post('/loginUser', loginUser.loginUser);
|
|||||||
|
|
||||||
//*****************************askQuestion****************************************************
|
//*****************************askQuestion****************************************************
|
||||||
router.post('/ask', authMiddleware, askQuestion.ask);
|
router.post('/ask', authMiddleware, askQuestion.ask);
|
||||||
router.post('/ask/stream', askQuestion.askstream);
|
router.post('/ask/stream', authMiddleware, askQuestion.askstream);
|
||||||
router.get('/health', askQuestion.health);
|
router.get('/health', askQuestion.health);
|
||||||
|
|
||||||
//*****************************answerUnanswerdQuestion****************************************************
|
//*****************************answerUnanswerdQuestion****************************************************
|
||||||
@@ -32,5 +33,9 @@ router.get('/UnansweredQuestions', authMiddleware, answerUnanswerdQuestion.Unans
|
|||||||
router.post('/upload', upload.single("pdf"), authMiddleware, qdrantinsert.upload);
|
router.post('/upload', upload.single("pdf"), authMiddleware, qdrantinsert.upload);
|
||||||
|
|
||||||
|
|
||||||
|
//*********************************Dashboard************************************************
|
||||||
|
router.get('/analytics',authMiddleware, Dashboard.analytics);
|
||||||
|
|
||||||
|
|
||||||
module.exports = router;
|
module.exports = router;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user