diff --git a/controller/fetchhistory.js b/controller/fetchhistory.js new file mode 100644 index 0000000..b1ca8ee --- /dev/null +++ b/controller/fetchhistory.js @@ -0,0 +1,89 @@ +const clickhouse = require('../database/clickhouse'); + +const fetchHistory = async (req, res) => { + try { + const { responseId } = req.body; + + const rows = await clickhouse.query({ + query: ` + SELECT response_json + FROM userdetails.chat_history + WHERE response_id = {responseId:UInt64} + LIMIT 1 + `, + query_params: { + responseId: Number(responseId) + } + }); + + const result = await rows.json(); + + if (!result.data.length) { + return res.status(404).json({ + success: false, + message: "History not found" + }); + } + + const response = JSON.parse( + result.data[0].response_json + ); + + return res.json({ + success: true, + data: response + }); + + } catch (err) { + console.error(err); + + return res.status(500).json({ + success: false, + message: err.message + }); + } +}; + +const fetchUserHistory = async (req, res) => { + try { + const userId = req.user?.client_id || null; + + const rows = await clickhouse.query({ + query: ` + SELECT + response_id, + prompt, + created_at + FROM userdetails.chat_history + WHERE user_id = {userId:UInt64} + ORDER BY created_at DESC + `, + query_params: { + userId: Number(userId) + } + }); + + const result = await rows.json(); + + return res.json({ + success: true, + totalRecords: result.data.length, + data: result.data + }); + + } catch (err) { + console.error(err); + + return res.status(500).json({ + success: false, + message: err.message + }); + } +}; + + + +module.exports = { + fetchHistory, + fetchUserHistory +}; \ No newline at end of file diff --git a/controller/handleOrchestration.js b/controller/handleOrchestration.js index 4644e40..f410673 100644 --- a/controller/handleOrchestration.js +++ b/controller/handleOrchestration.js @@ -2,6 +2,7 @@ const axios = require('axios'); require('dotenv').config(); const { OpenAI } = require('openai'); const yaml = require('js-yaml'); +const clickhouse = require('../database/clickhouse'); const azureEndpoint = "https://cpmindiayoda-resource.services.ai.azure.com"; const deploymentName = "gpt-4o-mini"; @@ -231,6 +232,7 @@ const fetchWrenData = async (prompt) => { { threadId: createThread.id, data: { question: prompt, sql: wrenSql } } ); console.log("Response ID =>", createThreadResponse.id); + const responseId = createThreadResponse.id; // Step 5: Preview data const { previewData } = await gql("PreviewData", @@ -247,6 +249,8 @@ const fetchWrenData = async (prompt) => { + + console.log(`Done — ${rows.length} rows`); console.table(rows); @@ -258,6 +262,7 @@ const fetchWrenData = async (prompt) => { totalRows: rows.length, columns, data: rows, + responseId }; } catch (err) { @@ -272,7 +277,6 @@ const fetchWrenData = async (prompt) => { } }; - const generateVegaJson = async (queryResult) => { try { const systemPrompt = `You are a data visualization expert. I will provide a user's question and a JSON array of data. Your task is to generate a strictly valid Vega-Lite JSON specification to visualize this data. The data array will be provided to the Vega spec internally. Map the JSON keys to the correct x, y, and color axes. Choose the best chart type (bar,pai,line, arc) based on the question.`; @@ -317,6 +321,8 @@ const ask = async (req, res) => { try { const { prompt } = req.body; + const userId = req.user?.client_id || null; + if (!prompt?.trim()) { return res.status(400).json({ success: false, @@ -334,12 +340,32 @@ const ask = async (req, res) => { chart: result.prompt, sql: result.sql }); - // console.log("Ask Result =>",vegaSpec); - return res.json({ + + const finalResponse = { ...result, vegaSpec + }; + + await clickhouse.insert({ + table: "userdetails.chat_history", + values: [ + { + user_id: userId, + response_id: result.responseId, + prompt: prompt, + response_json: JSON.stringify(finalResponse) + } + ], + format: "JSONEachRow" }); + return res.json(finalResponse); + + // return res.json({ + // ...result, + // vegaSpec + // }); + } catch (err) { console.error(err); @@ -381,11 +407,194 @@ const getSuggestedQuestions = async () => { return []; } }; - const suggestions = async (req, res) => { const questions = await getSuggestedQuestions(); res.json({ success: true, questions }); } +const getThreadDetails = async (req, res) => { + try { + const { threadId } = req.body; + + if (!threadId) { + return res.status(400).json({ + success: false, + message: "threadId is required", + }); + } + + const query = ` + query Thread($threadId: Int!) { + thread(threadId: $threadId) { + id + responses { + id + threadId + question + sql + + answerDetail { + queryId + status + content + numRowsUsedInLLM + error { + code + shortMessage + message + } + } + + chartDetail { + queryId + status + description + chartType + chartSchema + } + } + } + } + `; + + const { thread } = await gql( + "Thread", + query, + { + threadId: Number(threadId), + } + ); + + if (!thread) { + return res.status(404).json({ + success: false, + message: "Thread not found", + }); + } + + const latestResponse = + thread.responses?.[thread.responses.length - 1] || null; + + return res.json({ + success: true, + threadId: thread.id, + totalResponses: thread.responses.length, + + latestResponse: latestResponse + ? { + responseId: latestResponse.id, + question: latestResponse.question, + sql: latestResponse.sql, + status: latestResponse.answerDetail?.status, + queryId: latestResponse.answerDetail?.queryId, + content: latestResponse.answerDetail?.content, + } + : null, + + responses: thread.responses.map(r => ({ + responseId: r.id, + question: r.question, + status: r.answerDetail?.status, + queryId: r.answerDetail?.queryId, + content: r.answerDetail?.content, + })), + }); + } catch (err) { + console.error(err); + + return res.status(500).json({ + success: false, + message: err.message, + }); + } +}; +const getResponseDetails = async (req, res) => { + try { + const { responseId } = req.body; + + if (!responseId) { + return res.status(400).json({ + success: false, + message: "responseId is required", + }); + } + + const query = ` + query ThreadResponse($responseId: Int!) { + threadResponse(responseId: $responseId) { + id + threadId + question + sql + + answerDetail { + queryId + status + content + numRowsUsedInLLM + error { + code + shortMessage + message + } + } + + chartDetail { + queryId + status + description + chartType + chartSchema + } + } + } + `; + + const { threadResponse } = await gql( + "ThreadResponse", + query, + { + responseId: Number(responseId), + } + ); + + if (!threadResponse) { + return res.status(404).json({ + success: false, + message: "Response not found", + }); + } + + return res.json({ + success: true, + responseId: threadResponse.id, + threadId: threadResponse.threadId, + question: threadResponse.question, + sql: threadResponse.sql, + + status: threadResponse.answerDetail?.status, + queryId: threadResponse.answerDetail?.queryId, + content: threadResponse.answerDetail?.content, + + chartDetail: threadResponse.chartDetail || null, + }); + + } catch (err) { + console.error(err); + + return res.status(500).json({ + success: false, + message: err.message, + }); + } +}; + +module.exports = { ask, suggestions, getThreadDetails, getResponseDetails }; + + + + + + + -module.exports = { ask, suggestions }; diff --git a/route/route.js b/route/route.js index 592058f..2dd46ba 100644 --- a/route/route.js +++ b/route/route.js @@ -3,10 +3,20 @@ const router = express.Router(); const loginUser = require('../controller/loginUser.js'); const authMiddleware = require('../middleware/authMiddleware.js'); const handleOrchestration = require('../controller/handleOrchestration.js'); +const fetchHistory = require('../controller/fetchhistory.js'); +//*****************************loginUser**************************************************** router.post('/loginUser', loginUser.loginUser); -// router.post('/handleOrchestration', authMiddleware, handleOrchestration.handleOrchestration); -router.post('/ask', handleOrchestration.ask); + +//**********************************handleOrchestration******************************************** */ +router.post('/ask',authMiddleware, handleOrchestration.ask); router.get('/suggestions', handleOrchestration.suggestions); +router.get('/getThreadDetails', handleOrchestration.getThreadDetails); +router.get('/getResponseDetails', handleOrchestration.getResponseDetails); + + +//***********************************fetchHistory**************************************************** +router.get('/fetchHistory', authMiddleware, fetchHistory.fetchHistory); +router.get('/fetchUserHistory', authMiddleware, fetchHistory.fetchUserHistory); module.exports = router; \ No newline at end of file