tango-app-api-audio-analytics 1.0.17 → 1.0.19

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.17",
3
+ "version": "1.0.19",
4
4
  "description": "audioAnalytics",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -455,7 +455,7 @@ Only return the JSON array, no other text.`;
455
455
 
456
456
  // ======================= CHAT STREAM API =======================
457
457
 
458
- const CHAT_STREAM_API = 'http://13.232.38.210:8000/api/chat/stream';
458
+ const CHAT_STREAM_API = 'http://65.2.124.154:8000/api/chat/stream';
459
459
 
460
460
  /**
461
461
  * Chat Stream API - Streams response from external AI chat API
@@ -528,4 +528,86 @@ export async function chatStream( req, res ) {
528
528
  }
529
529
  }
530
530
 
531
+ /**
532
+ * AI Stream Response - Calls external chat stream API and returns all chunks as JSON array
533
+ * @param {Object} req - Express request object
534
+ * @param {Object} res - Express response object
535
+ * Returns: [{ text: "..." }, { text: "..." }, ...]
536
+ */
537
+ export async function aiStreamResponse( req, res ) {
538
+ try {
539
+ /* eslint-disable camelcase */
540
+ const { user_id, session_id, session_date, session_timezone, message, config } = req.body;
541
+
542
+ logger.info( { message: 'Received ai-stream-response request', user_id, session_id } );
543
+
544
+ // Map camelCase to snake_case for external API
545
+ const payload = {
546
+ user_id: user_id,
547
+ session_id: session_id,
548
+ session_date: session_date,
549
+ session_timezone: session_timezone,
550
+ message,
551
+ config: {
552
+ selected_stores: config.selected_stores,
553
+ date_range: config.date_range,
554
+ selected_products: config.selected_products,
555
+ },
556
+ };
557
+ /* eslint-enable camelcase */
558
+
559
+ const response = await fetch( CHAT_STREAM_API, {
560
+ method: 'POST',
561
+ headers: {
562
+ 'Content-Type': 'application/json',
563
+ 'Accept': 'text/event-stream',
564
+ 'Referer': '',
565
+ },
566
+ body: JSON.stringify( payload ),
567
+ } );
568
+
569
+ if ( !response.ok ) {
570
+ logger.error( response, `External Chat Stream API error: ${response.status} ${response.statusText}` );
571
+ return res.sendError( `External API error: ${response.statusText}`, response.status );
572
+ }
573
+
574
+ const chunks = [];
575
+ const decoder = new TextDecoder();
576
+
577
+ for await ( const chunk of response.body ) {
578
+ const text = decoder.decode( chunk, { stream: true } );
579
+
580
+ // Parse SSE lines: "data: {...}" or plain text
581
+ const lines = text.split( '\n' );
582
+ for ( const line of lines ) {
583
+ const trimmed = line.trim();
584
+ if ( !trimmed ) continue;
585
+
586
+ if ( trimmed.startsWith( 'data:' ) ) {
587
+ const content = trimmed.slice( 5 ).trim();
588
+ if ( content && content !== '[DONE]' ) {
589
+ try {
590
+ const parsed = JSON.parse( content );
591
+ // Extract text from common SSE formats
592
+ const textValue = parsed.text ?? parsed.content ?? parsed.delta ?? parsed.message ?? JSON.stringify( parsed );
593
+ if ( textValue ) chunks.push( { text: textValue } );
594
+ } catch {
595
+ if ( content ) chunks.push( { text: content } );
596
+ }
597
+ }
598
+ } else {
599
+ chunks.push( { text: trimmed } );
600
+ }
601
+ }
602
+ }
603
+
604
+ logger.info( { message: 'ai-stream-response completed', chunkCount: chunks.length } );
605
+
606
+ return res.sendSuccess( chunks );
607
+ } catch ( error ) {
608
+ logger.error( { error: error, function: 'aiStreamResponse' } );
609
+ return res.sendError( error.message || 'Internal Server Error', 500 );
610
+ }
611
+ }
612
+
531
613
 
@@ -1,5 +1,5 @@
1
1
  // import { sampleConversationData, sampleConversationsList } from '../models/conversationAnalysis.model.js';
2
- import { logger, downloadint } from 'tango-app-api-middleware';
2
+ import { logger } from 'tango-app-api-middleware';
3
3
  import {
4
4
  getConversationsListFromLambda,
5
5
  exportConversationsFromLambda,
@@ -82,7 +82,7 @@ export const getConversationsList = async ( req, res ) => {
82
82
 
83
83
  logger.info( {
84
84
  message: 'Export Lambda response received',
85
- recordCount: exportResponse.data.length,
85
+ recordCount: exportResponse,
86
86
  } );
87
87
 
88
88
  // Apply search filter if provided
@@ -93,9 +93,10 @@ export const getConversationsList = async ( req, res ) => {
93
93
  // // Apply sorting
94
94
  // conversations = sortConversations( conversations, 'date', 'desc' );
95
95
 
96
- res.setHeader( 'Content-Type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' );
97
- res.setHeader( 'Content-Disposition', `attachment; filename=conversations-${Date.now()}.xlsx` );
98
- return downloadint( exportResponse.data, res );
96
+ // res.setHeader( 'Content-Type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' );
97
+ // res.setHeader( 'Content-Disposition', `attachment; filename=conversations-${Date.now()}.xlsx` );
98
+ // return downloadint( exportResponse.data, res );
99
+ return res.sendSuccess( exportResponse.download_url );
99
100
  } catch ( error ) {
100
101
  logger.error( { error, message: 'Error in export Lambda call', body: req.body } );
101
102
  throw error;
@@ -3,7 +3,7 @@ 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
5
  import { cohortAnalysisCardValid, conversationsListValid, conversationDetailsValid, chatHistoryListValid, getChatValid, getGeminiResponseValid } from '../dtos/audioAnalytics.dtos.js';
6
- import { createCohort, createBulkCohort, updateCohort, deleteCohort, getCohort, listCohortsByClient, chatHistoryList, getChat, getGeminiResponse } from '../controllers/audioAnalytics.controller.js';
6
+ import { createCohort, createBulkCohort, updateCohort, deleteCohort, getCohort, listCohortsByClient, chatHistoryList, getChat, getGeminiResponse, aiStreamResponse } 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
 
@@ -36,5 +36,7 @@ audioAnalyticsrouter.post( '/conversations/:conversationId', validate( conversat
36
36
  // Gemini Suggestion Prompts API Route
37
37
  audioAnalyticsrouter.post( '/gemini/suggestions', validate( getGeminiResponseValid ), getGeminiResponse );
38
38
 
39
+ // AI streaming response route
40
+ audioAnalyticsrouter.post( '/ai-stream-response', aiStreamResponse );
39
41
 
40
42
  export default audioAnalyticsrouter;