tango-app-api-audio-analytics 1.0.14 → 1.0.16
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tango-app-api-audio-analytics",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.16",
|
|
4
4
|
"description": "audioAnalytics",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
"license": "ISC",
|
|
15
15
|
"dependencies": {
|
|
16
16
|
"@aws-sdk/client-secrets-manager": "^3.1003.0",
|
|
17
|
+
"@google/generative-ai": "^0.24.1",
|
|
17
18
|
"aws-sdk": "^2.1693.0",
|
|
18
19
|
"body-parser": "^2.2.2",
|
|
19
20
|
"cors": "^2.8.6",
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { insertWithId, updateOpenSearchData, searchOpenSearchData, logger } from 'tango-app-api-middleware';
|
|
2
2
|
import { randomUUID } from 'crypto';
|
|
3
|
+
import { GoogleGenerativeAI } from '@google/generative-ai';
|
|
3
4
|
|
|
4
5
|
const EXTERNAL_STREAM_API = 'http://172.236.179.51:8000/stream';
|
|
5
6
|
|
|
@@ -31,7 +32,7 @@ export async function createCohort( req, res ) {
|
|
|
31
32
|
updatedAt: new Date().toISOString(),
|
|
32
33
|
};
|
|
33
34
|
|
|
34
|
-
const result = await insertWithId( 'tango-audio-config', cohortId, cohortData );
|
|
35
|
+
const result = await insertWithId( 'tango-audio-config-test', cohortId, cohortData );
|
|
35
36
|
logger.info( { result } );
|
|
36
37
|
|
|
37
38
|
if ( result && result.body && result.body.result === 'created' ) {
|
|
@@ -67,7 +68,7 @@ export async function createBulkCohort( req, res ) {
|
|
|
67
68
|
updatedAt: new Date().toISOString(),
|
|
68
69
|
};
|
|
69
70
|
|
|
70
|
-
const result = await insertWithId( 'tango-audio-config', cohortId, cohortData );
|
|
71
|
+
const result = await insertWithId( 'tango-audio-config-test', cohortId, cohortData );
|
|
71
72
|
logger.info( { result } );
|
|
72
73
|
|
|
73
74
|
if ( result && result.body && result.body.result === 'created' ) {
|
|
@@ -118,7 +119,7 @@ export async function updateCohort( req, res ) {
|
|
|
118
119
|
doc_as_upsert: true,
|
|
119
120
|
};
|
|
120
121
|
|
|
121
|
-
const result = await updateOpenSearchData( 'tango-audio-config', cohortId, updatePayload );
|
|
122
|
+
const result = await updateOpenSearchData( 'tango-audio-config-test', cohortId, updatePayload );
|
|
122
123
|
logger.info( { result } );
|
|
123
124
|
|
|
124
125
|
if ( result && result.body && result.body.result === 'updated' ) {
|
|
@@ -144,7 +145,7 @@ export async function deleteCohort( req, res ) {
|
|
|
144
145
|
},
|
|
145
146
|
};
|
|
146
147
|
|
|
147
|
-
const result = await updateOpenSearchData( 'tango-audio-config', cohortId, updatePayload );
|
|
148
|
+
const result = await updateOpenSearchData( 'tango-audio-config-test', cohortId, updatePayload );
|
|
148
149
|
logger.info( { result } );
|
|
149
150
|
|
|
150
151
|
if ( result && result.body && result.body.result === 'updated' ) {
|
|
@@ -169,7 +170,7 @@ export async function getCohort( req, res ) {
|
|
|
169
170
|
},
|
|
170
171
|
};
|
|
171
172
|
|
|
172
|
-
const result = await searchOpenSearchData( 'tango-audio-config', query );
|
|
173
|
+
const result = await searchOpenSearchData( 'tango-audio-config-test', query );
|
|
173
174
|
logger.info( { result } );
|
|
174
175
|
|
|
175
176
|
if ( !result || result?.body?.hits?.hits?.length === 0 ) {
|
|
@@ -204,7 +205,7 @@ export async function listCohortsByClient( req, res ) {
|
|
|
204
205
|
sort: [ { createdAt: { order: 'desc' } } ],
|
|
205
206
|
};
|
|
206
207
|
|
|
207
|
-
const result = await searchOpenSearchData( 'tango-audio-config', query );
|
|
208
|
+
const result = await searchOpenSearchData( 'tango-audio-config-test', query );
|
|
208
209
|
logger.info( { result } );
|
|
209
210
|
|
|
210
211
|
const total = result?.body?.hits?.total?.value || 0;
|
|
@@ -234,6 +235,94 @@ export async function listCohortsByClient( req, res ) {
|
|
|
234
235
|
}
|
|
235
236
|
}
|
|
236
237
|
|
|
238
|
+
|
|
239
|
+
export async function chatHistoryList( req, res ) {
|
|
240
|
+
try {
|
|
241
|
+
/* eslint-disable camelcase */
|
|
242
|
+
const { userId, limit = 10, offset = 0 } = req.body;
|
|
243
|
+
|
|
244
|
+
logger.info( { message: 'Fetching chat history', userId, limit, offset } );
|
|
245
|
+
|
|
246
|
+
// Build OpenSearch query to filter by user_id
|
|
247
|
+
const query = {
|
|
248
|
+
query: {
|
|
249
|
+
match: {
|
|
250
|
+
user_id: userId,
|
|
251
|
+
},
|
|
252
|
+
},
|
|
253
|
+
_source: [ 'session_id', 'session_title' ], // Only fetch required fields
|
|
254
|
+
size: Number( limit ),
|
|
255
|
+
from: Number( offset ),
|
|
256
|
+
sort: [ { session_date: { order: 'desc' } } ], // Sort by creation date, most recent first
|
|
257
|
+
};
|
|
258
|
+
|
|
259
|
+
// Search in tango-ai-session index
|
|
260
|
+
const result = await searchOpenSearchData( 'tango-ai-sessions', query );
|
|
261
|
+
logger.info( { message: 'Chat history search result', resultHits: result?.body?.hits?.hits?.length } );
|
|
262
|
+
|
|
263
|
+
if ( !result || !result.body || !result.body.hits ) {
|
|
264
|
+
return res.sendError( 'Failed to fetch chat history', 500 );
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
const total = result.body.hits.total?.value || 0;
|
|
268
|
+
const sessions = ( result.body.hits.hits || [] ).map( ( hit ) => ( {
|
|
269
|
+
session_id: hit._source.session_id,
|
|
270
|
+
session_title: hit._source.session_title,
|
|
271
|
+
} ) );
|
|
272
|
+
/* eslint-enable camelcase */
|
|
273
|
+
|
|
274
|
+
return res.sendSuccess( {
|
|
275
|
+
sessions,
|
|
276
|
+
count: total,
|
|
277
|
+
// pagination: {
|
|
278
|
+
// total,
|
|
279
|
+
// limit: Number( limit ),
|
|
280
|
+
// offset: Number( offset ),
|
|
281
|
+
// hasMore: Number( offset ) + Number( limit ) < total,
|
|
282
|
+
// },
|
|
283
|
+
} );
|
|
284
|
+
} catch ( error ) {
|
|
285
|
+
const err = error.message || 'Internal Server Error';
|
|
286
|
+
logger.error( { error, message: req.body, function: 'chatHistoryList' } );
|
|
287
|
+
return res.sendError( err, 500 );
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
export async function getChat( req, res ) {
|
|
292
|
+
try {
|
|
293
|
+
/* eslint-disable camelcase */
|
|
294
|
+
const { sessionId } = req.body;
|
|
295
|
+
|
|
296
|
+
logger.info( { message: 'Fetching chat details', sessionId } );
|
|
297
|
+
|
|
298
|
+
// Build OpenSearch query to find by session_id
|
|
299
|
+
const query = {
|
|
300
|
+
query: {
|
|
301
|
+
match: {
|
|
302
|
+
session_id: sessionId,
|
|
303
|
+
},
|
|
304
|
+
},
|
|
305
|
+
};
|
|
306
|
+
|
|
307
|
+
// Search in tango-ai-sessions index
|
|
308
|
+
const result = await searchOpenSearchData( 'tango-ai-sessions', query );
|
|
309
|
+
logger.info( { message: 'Chat details search result', found: result?.body?.hits?.hits?.length > 0 } );
|
|
310
|
+
|
|
311
|
+
if ( !result || !result.body || !result.body.hits || result.body.hits.hits.length === 0 ) {
|
|
312
|
+
return res.sendError( 'Chat session not found', 404 );
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
const chatSession = result.body.hits.hits[0]._source;
|
|
316
|
+
/* eslint-enable camelcase */
|
|
317
|
+
|
|
318
|
+
return res.sendSuccess( chatSession );
|
|
319
|
+
} catch ( error ) {
|
|
320
|
+
const err = error.message || 'Internal Server Error';
|
|
321
|
+
logger.error( { error, message: req.body, function: 'getChat' } );
|
|
322
|
+
return res.sendError( err, 500 );
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
237
326
|
export const callExternalStreamAPI = async ( req, res ) => {
|
|
238
327
|
try {
|
|
239
328
|
const { prompt, storeId, productModule, country, region, fromDate, toDate, clusters } = req.body;
|
|
@@ -305,6 +394,52 @@ export const callExternalStreamAPI = async ( req, res ) => {
|
|
|
305
394
|
}
|
|
306
395
|
};
|
|
307
396
|
|
|
397
|
+
export const getGeminiResponse = async ( req, res ) => {
|
|
398
|
+
try {
|
|
399
|
+
const { context, topic, sessionData } = req.body;
|
|
400
|
+
|
|
401
|
+
// Build a prompt to get suggestions from Gemini
|
|
402
|
+
const suggestionPrompt = `Based on the following context, provide helpful suggestion prompts that the user could ask for autocomplete:
|
|
403
|
+
Context: ${context || 'General assistance'}
|
|
404
|
+
${topic ? `Topic: ${topic}` : ''}
|
|
405
|
+
${sessionData ? `Session Information: ${JSON.stringify( sessionData )}` : ''}
|
|
406
|
+
|
|
407
|
+
Generate 5-8 concise and relevant suggestion prompts. Format exactly as a JSON array of objects with this structure:
|
|
408
|
+
[
|
|
409
|
+
{"id": 1, "text": "suggestion text here", "category": "category_name"},
|
|
410
|
+
{"id": 2, "text": "suggestion text here", "category": "category_name"}
|
|
411
|
+
]
|
|
412
|
+
Only return the JSON array, no other text.`;
|
|
413
|
+
|
|
414
|
+
const genAI = new GoogleGenerativeAI( process.env.GOOGLE_API_KEY );
|
|
415
|
+
|
|
416
|
+
const model = genAI.getGenerativeModel( {
|
|
417
|
+
model: 'gemini-2.5-pro',
|
|
418
|
+
} );
|
|
419
|
+
|
|
420
|
+
const result = await model.generateContent( suggestionPrompt );
|
|
421
|
+
const responseText = result.response.text().trim();
|
|
422
|
+
logger.info( { responseText } );
|
|
423
|
+
// Parse the JSON array response
|
|
424
|
+
let suggestionsArray = [];
|
|
425
|
+
suggestionsArray = JSON.parse( responseText );
|
|
426
|
+
logger.info( { suggestionsArray } );
|
|
427
|
+
|
|
428
|
+
if ( !Array.isArray( suggestionsArray ) ) {
|
|
429
|
+
suggestionsArray = [];
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
|
|
433
|
+
logger.info( { message: 'Gemini autocomplete suggestions generated', context, suggestionCount: suggestionsArray.length } );
|
|
434
|
+
|
|
435
|
+
return res.sendSuccess( { suggestions: suggestionsArray } );
|
|
436
|
+
} catch ( error ) {
|
|
437
|
+
logger.error( `Error getting Gemini suggestions: ${error.message}` );
|
|
438
|
+
const err = error.message || 'Internal server error while getting Gemini suggestions';
|
|
439
|
+
return res.sendError( err, 500 );
|
|
440
|
+
}
|
|
441
|
+
};
|
|
442
|
+
|
|
308
443
|
// ======================= CHAT STREAM API =======================
|
|
309
444
|
|
|
310
445
|
const CHAT_STREAM_API = 'http://13.232.38.210:8000/api/chat/stream';
|
|
@@ -380,3 +515,4 @@ export async function chatStream( req, res ) {
|
|
|
380
515
|
}
|
|
381
516
|
}
|
|
382
517
|
|
|
518
|
+
|
|
@@ -686,3 +686,60 @@ const chatStreamSchema = joi.object( {
|
|
|
686
686
|
export const chatStreamValid = {
|
|
687
687
|
body: chatStreamSchema,
|
|
688
688
|
};
|
|
689
|
+
|
|
690
|
+
// ======================= CHAT HISTORY LIST API SCHEMA =======================
|
|
691
|
+
|
|
692
|
+
/**
|
|
693
|
+
* Chat History List Request Schema
|
|
694
|
+
* For fetching session history by user_id from tango-ai-session index
|
|
695
|
+
*/
|
|
696
|
+
const chatHistoryListSchema = joi.object( {
|
|
697
|
+
userId: joi.string().required().description( 'User ID to fetch session history for' ),
|
|
698
|
+
limit: joi.number().integer().min( 1 ).max( 1000 ).optional().default( 10 ).description( 'Pagination limit' ),
|
|
699
|
+
offset: joi.number().integer().min( 0 ).optional().default( 0 ).description( 'Pagination offset' ),
|
|
700
|
+
} ).strict();
|
|
701
|
+
|
|
702
|
+
export const chatHistoryListValid = {
|
|
703
|
+
body: chatHistoryListSchema,
|
|
704
|
+
};
|
|
705
|
+
|
|
706
|
+
// ======================= GET SINGLE CHAT API SCHEMA =======================
|
|
707
|
+
|
|
708
|
+
/**
|
|
709
|
+
* Get Single Chat Request Schema
|
|
710
|
+
* For fetching a single session by session_id from tango-ai-sessions index
|
|
711
|
+
*/
|
|
712
|
+
const getChatSchema = joi.object( {
|
|
713
|
+
sessionId: joi.string().required().description( 'Session ID to fetch' ),
|
|
714
|
+
} ).strict();
|
|
715
|
+
|
|
716
|
+
export const getChatValid = {
|
|
717
|
+
body: getChatSchema,
|
|
718
|
+
};
|
|
719
|
+
|
|
720
|
+
// ======================= GEMINI SUGGESTION PROMPTS API SCHEMA =======================
|
|
721
|
+
|
|
722
|
+
/**
|
|
723
|
+
* Gemini Suggestion Prompts Request Schema
|
|
724
|
+
* For generating autocomplete suggestions based on context using Gemini AI
|
|
725
|
+
*
|
|
726
|
+
* Response format:
|
|
727
|
+
* {
|
|
728
|
+
* "status": "success",
|
|
729
|
+
* "data": {
|
|
730
|
+
* "suggestions": [
|
|
731
|
+
* { "id": 1, "text": "suggestion text", "category": "category_name" },
|
|
732
|
+
* { "id": 2, "text": "suggestion text", "category": "category_name" }
|
|
733
|
+
* ]
|
|
734
|
+
* }
|
|
735
|
+
* }
|
|
736
|
+
*/
|
|
737
|
+
const getGeminiResponseSchema = joi.object( {
|
|
738
|
+
context: joi.string().optional().description( 'Context for generating suggestions' ),
|
|
739
|
+
topic: joi.string().optional().description( 'Topic for suggestion generation' ),
|
|
740
|
+
sessionData: joi.object().optional().unknown( true ).description( 'Session data for context' ),
|
|
741
|
+
} ).strict();
|
|
742
|
+
|
|
743
|
+
export const getGeminiResponseValid = {
|
|
744
|
+
body: getGeminiResponseSchema,
|
|
745
|
+
};
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
import express from 'express';
|
|
3
3
|
import { validate } from 'tango-app-api-middleware';
|
|
4
4
|
import { createCohortValid, createBulkCohortValid, updateCohortValid, getCohortValid, listCohortsByClientValid, deleteCohortValid } from '../dtos/audioAnalytics.dtos.js';
|
|
5
|
-
import { cohortAnalysisCardValid, conversationsListValid, conversationDetailsValid, chatStreamValid } from '../dtos/audioAnalytics.dtos.js';
|
|
6
|
-
import { createCohort, createBulkCohort, updateCohort, deleteCohort, getCohort, listCohortsByClient, chatStream } from '../controllers/audioAnalytics.controller.js';
|
|
5
|
+
import { cohortAnalysisCardValid, conversationsListValid, conversationDetailsValid, chatStreamValid, chatHistoryListValid, getChatValid, getGeminiResponseValid } from '../dtos/audioAnalytics.dtos.js';
|
|
6
|
+
import { createCohort, createBulkCohort, updateCohort, deleteCohort, getCohort, listCohortsByClient, chatStream, chatHistoryList, getChat, getGeminiResponse } from '../controllers/audioAnalytics.controller.js';
|
|
7
7
|
import { getCohortAnalysisCard } from '../controllers/cohortAnalytics.controller.js';
|
|
8
8
|
import { getConversationsList, getConversationDetails } from '../controllers/conversationAnalytics.controller.js';
|
|
9
9
|
|
|
@@ -23,6 +23,8 @@ audioAnalyticsrouter.post( '/update-cohort', validate( updateCohortValid ), upda
|
|
|
23
23
|
audioAnalyticsrouter.get( '/get-cohort/:cohortId', validate( getCohortValid ), getCohort );
|
|
24
24
|
audioAnalyticsrouter.get( '/list-cohorts', validate( listCohortsByClientValid ), listCohortsByClient );
|
|
25
25
|
|
|
26
|
+
audioAnalyticsrouter.post( '/chat-history-list', validate( chatHistoryListValid ), chatHistoryList );
|
|
27
|
+
audioAnalyticsrouter.post( '/get-chat', validate( getChatValid ), getChat );
|
|
26
28
|
// Cohort Analytics Routes
|
|
27
29
|
audioAnalyticsrouter.post( '/cohort-analysis-card', validate( cohortAnalysisCardValid ), getCohortAnalysisCard );
|
|
28
30
|
|
|
@@ -33,5 +35,8 @@ audioAnalyticsrouter.post( '/conversations/:conversationId', validate( conversat
|
|
|
33
35
|
// Chat Stream API Route
|
|
34
36
|
audioAnalyticsrouter.post( '/chat/stream', validate( chatStreamValid ), chatStream );
|
|
35
37
|
|
|
38
|
+
// Gemini Suggestion Prompts API Route
|
|
39
|
+
audioAnalyticsrouter.post( '/gemini/suggestions', validate( getGeminiResponseValid ), getGeminiResponse );
|
|
40
|
+
|
|
36
41
|
|
|
37
42
|
export default audioAnalyticsrouter;
|