Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion api/app/clients/BaseClient.js
Original file line number Diff line number Diff line change
Expand Up @@ -779,13 +779,25 @@
}
}

// Persist usage metadata on the assistant message if available for accurate costing
if (this.getStreamUsage != null) {
const streamUsage = this.getStreamUsage();
if (streamUsage && (Number(streamUsage[this.inputTokensKey]) > 0 || Number(streamUsage[this.outputTokensKey]) > 0)) {

Check failure

Code scanning / ESLint

Ensure code is properly formatted, use insertion, deletion, or replacement to obtain desired formatting. Error

Replace streamUsage·&&·(Number(streamUsage[this.inputTokensKey])·>·0·||·Number(streamUsage[this.outputTokensKey])·>·0) with ⏎········streamUsage·&&⏎········(Number(streamUsage[this.inputTokensKey])·>·0·||⏎··········Number(streamUsage[this.outputTokensKey])·>·0)⏎······

Copilot Autofix

AI 11 days ago

The best fix is to reformat the conditional at line 785 so that:

  • The logical AND (&&) and logical OR (||) expressions each begin on a new line with appropriate indentation.
  • Each operand of the logical operators is clearly separated, following ESLint’s suggestion for multiline formatting of long logical statements.
    This change only affects formatting, not logic, and applies only to the conditional in line 785 of api/app/clients/BaseClient.js.
Suggested changeset 1
api/app/clients/BaseClient.js

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/api/app/clients/BaseClient.js b/api/app/clients/BaseClient.js
--- a/api/app/clients/BaseClient.js
+++ b/api/app/clients/BaseClient.js
@@ -782,7 +782,13 @@
     // Persist usage metadata on the assistant message if available for accurate costing
     if (this.getStreamUsage != null) {
       const streamUsage = this.getStreamUsage();
-      if (streamUsage && (Number(streamUsage[this.inputTokensKey]) > 0 || Number(streamUsage[this.outputTokensKey]) > 0)) {
+      if (
+        streamUsage &&
+        (
+          Number(streamUsage[this.inputTokensKey]) > 0 ||
+          Number(streamUsage[this.outputTokensKey]) > 0
+        )
+      ) {
         responseMessage.usage = {
           prompt_tokens: streamUsage[this.inputTokensKey],
           completion_tokens: streamUsage[this.outputTokensKey],
EOF
@@ -782,7 +782,13 @@
// Persist usage metadata on the assistant message if available for accurate costing
if (this.getStreamUsage != null) {
const streamUsage = this.getStreamUsage();
if (streamUsage && (Number(streamUsage[this.inputTokensKey]) > 0 || Number(streamUsage[this.outputTokensKey]) > 0)) {
if (
streamUsage &&
(
Number(streamUsage[this.inputTokensKey]) > 0 ||
Number(streamUsage[this.outputTokensKey]) > 0
)
) {
responseMessage.usage = {
prompt_tokens: streamUsage[this.inputTokensKey],
completion_tokens: streamUsage[this.outputTokensKey],
Copilot is powered by AI and may make mistakes. Always verify output.
responseMessage.usage = {
prompt_tokens: streamUsage[this.inputTokensKey],
completion_tokens: streamUsage[this.outputTokensKey],
reasoning_tokens: streamUsage.reasoning_tokens,
input_token_details: streamUsage.input_token_details,
};
}
}

responseMessage.databasePromise = this.saveMessageToDatabase(
responseMessage,
saveOptions,
user,
);
this.savedMessageIds.add(responseMessage.messageId);
delete responseMessage.tokenCount;
return responseMessage;
}

Expand Down
73 changes: 73 additions & 0 deletions api/server/routes/convos.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ const assistantClients = {
};

const router = express.Router();
const {
getConversationCostDisplayFromMessages,
getMultipleConversationCosts,
} = require('~/server/services/ConversationCostDynamic');
router.use(requireJwtAuth);

router.get('/', async (req, res) => {
Expand Down Expand Up @@ -230,3 +234,72 @@ router.post('/duplicate', async (req, res) => {
});

module.exports = router;

/**
* GET /:conversationId/cost
* Get cost summary for a specific conversation
*/
router.get('/:conversationId/cost', async (req, res) => {
try {
const { conversationId } = req.params;
const userId = req.user.id;

const { getConvo } = require('~/models/Conversation');
const { getMessages } = require('~/models/Message');

const conversation = await getConvo(userId, conversationId);
if (!conversation) {
return res.status(404).json({ error: 'Conversation not found' });
}

const messages = await getMessages({ user: userId, conversationId });
if (messages.length === 0) {
return res.status(404).json({ error: 'No messages found in this conversation' });
}

const costDisplay = getConversationCostDisplayFromMessages(messages);
if (!costDisplay) {
return res.json({
conversationId,
totalCost: '$0.00',
totalCostRaw: 0,
primaryModel: 'Unknown',
totalTokens: 0,
lastUpdated: new Date(),
error: 'No cost data available',
});
}

costDisplay.conversationId = conversationId;
res.json(costDisplay);
} catch (error) {
logger.error('Error getting conversation cost:', error);
res.status(500).json({ error: 'Failed to calculate conversation cost' });
}
});

/**
* POST /costs
* Get cost summaries for multiple conversations
* Body: { conversationIds: string[] }
*/
router.post('/costs', async (req, res) => {
try {
const { conversationIds } = req.body;
const userId = req.user.id;

if (!Array.isArray(conversationIds)) {
return res.status(400).json({ error: 'conversationIds must be an array' });
}

if (conversationIds.length > 50) {
return res.status(400).json({ error: 'Maximum 50 conversations allowed per request' });
}

const costs = await getMultipleConversationCosts(conversationIds, userId);
res.json(costs);
} catch (error) {
logger.error('Error getting multiple conversation costs:', error);
res.status(500).json({ error: 'Failed to calculate conversation costs' });
}
});
Loading
Loading