@@ -12,6 +12,10 @@ const requireJwtAuth = require('~/server/middleware/requireJwtAuth');
12
12
const { importConversations } = require ( '~/server/utils/import' ) ;
13
13
const { deleteToolCalls } = require ( '~/models/ToolCall' ) ;
14
14
const getLogStores = require ( '~/cache/getLogStores' ) ;
15
+ const {
16
+ getConversationCostDisplayFromMessages,
17
+ getMultipleConversationCosts,
18
+ } = require ( '~/server/services/ConversationCostDynamic' ) ;
15
19
16
20
const assistantClients = {
17
21
[ EModelEndpoint . azureAssistants ] : require ( '~/server/services/Endpoints/azureAssistants' ) ,
@@ -229,4 +233,102 @@ router.post('/duplicate', async (req, res) => {
229
233
}
230
234
} ) ;
231
235
236
+ /**
237
+ * GET /:conversationId/cost
238
+ * Get cost summary for a specific conversation
239
+ */
240
+ router . get ( '/:conversationId/cost' , async ( req , res ) => {
241
+ try {
242
+ const { conversationId } = req . params ;
243
+ const { detailed : _detailed = false } = req . query ;
244
+ const userId = req . user . id ;
245
+
246
+ console . log ( 'Cost API called:' , { conversationId, userId } ) ;
247
+
248
+ // Get the conversation
249
+ const conversation = await getConvo ( userId , conversationId ) ;
250
+
251
+ if ( ! conversation ) {
252
+ return res . status ( 404 ) . json ( {
253
+ error : 'Conversation not found' ,
254
+ } ) ;
255
+ }
256
+
257
+ // Get actual messages from the messages collection
258
+ const { getMessages } = require ( '~/models/Message' ) ;
259
+ const messages = await getMessages ( {
260
+ user : userId ,
261
+ conversationId : conversationId ,
262
+ } ) ;
263
+
264
+ if ( messages . length === 0 ) {
265
+ return res . status ( 404 ) . json ( {
266
+ error : 'No messages found in this conversation' ,
267
+ } ) ;
268
+ }
269
+
270
+ console . log ( 'Messages found:' , messages . length ) ;
271
+ console . log ( 'Sample message:' , JSON . stringify ( messages [ 0 ] , null , 2 ) ) ;
272
+
273
+ // Calculate cost from actual messages
274
+ const costDisplay = getConversationCostDisplayFromMessages ( messages ) ;
275
+
276
+ console . log ( 'Cost calculated:' , costDisplay ) ;
277
+
278
+ if ( ! costDisplay ) {
279
+ return res . json ( {
280
+ conversationId,
281
+ totalCost : '$0.00' ,
282
+ totalCostRaw : 0 ,
283
+ primaryModel : 'Unknown' ,
284
+ totalTokens : 0 ,
285
+ lastUpdated : new Date ( ) ,
286
+ error : 'No cost data available' ,
287
+ } ) ;
288
+ }
289
+
290
+ // Add conversationId to response
291
+ costDisplay . conversationId = conversationId ;
292
+
293
+ res . json ( costDisplay ) ;
294
+ } catch ( error ) {
295
+ logger . error ( 'Error getting conversation cost:' , error ) ;
296
+ res . status ( 500 ) . json ( {
297
+ error : 'Failed to calculate conversation cost' ,
298
+ } ) ;
299
+ }
300
+ } ) ;
301
+
302
+ /**
303
+ * POST /costs
304
+ * Get cost summaries for multiple conversations
305
+ * Body: { conversationIds: string[] }
306
+ */
307
+ router . post ( '/costs' , async ( req , res ) => {
308
+ try {
309
+ const { conversationIds } = req . body ;
310
+ const userId = req . user . id ;
311
+
312
+ if ( ! Array . isArray ( conversationIds ) ) {
313
+ return res . status ( 400 ) . json ( {
314
+ error : 'conversationIds must be an array' ,
315
+ } ) ;
316
+ }
317
+
318
+ if ( conversationIds . length > 50 ) {
319
+ return res . status ( 400 ) . json ( {
320
+ error : 'Maximum 50 conversations allowed per request' ,
321
+ } ) ;
322
+ }
323
+
324
+ const costs = await getMultipleConversationCosts ( conversationIds , userId ) ;
325
+ res . json ( costs ) ;
326
+ } catch ( error ) {
327
+ logger . error ( 'Error getting multiple conversation costs:' , error ) ;
328
+ res . status ( 500 ) . json ( {
329
+ error : 'Failed to calculate conversation costs' ,
330
+ } ) ;
331
+ }
332
+ } ) ;
333
+
232
334
module . exports = router ;
0 commit comments