From 40df266ceb432ea40a04387096f3d2f67e6f1a2c Mon Sep 17 00:00:00 2001 From: Deepak-Gnanasekar Date: Sat, 21 Mar 2026 18:08:26 +0530 Subject: [PATCH 1/5] Fix token-usage endpoint returning 500 in OSS mode --- .../chat_message/views/message_views.py | 28 +++++++++++++------ 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/backend/backend/core/routers/chat_message/views/message_views.py b/backend/backend/core/routers/chat_message/views/message_views.py index 2e9149b..190a550 100644 --- a/backend/backend/core/routers/chat_message/views/message_views.py +++ b/backend/backend/core/routers/chat_message/views/message_views.py @@ -52,12 +52,15 @@ def persist_prompt(self, request: Request, *args, **kwargs) -> Response: def get_token_usage(self, request, project_id=None, chat_id=None, chat_message_id=None, *args, **kwargs) -> Response: """ Get token usage data for a specific chat message. - + Returns: - remaining_balance: Current token balance - total_consumed: Total tokens consumed - message_tokens_consumed: Tokens consumed for this specific message - token_usage_found: Whether token usage was found for this message + + In OSS mode (no subscriptions module), returns a response indicating + token tracking is not available rather than a 500 error. """ try: # Get organization ID from header @@ -67,18 +70,25 @@ def get_token_usage(self, request, project_id=None, chat_id=None, chat_message_i {"error": "Organization header is required"}, status=status.HTTP_400_BAD_REQUEST ) - + # Get token usage data using the existing function token_data = get_token_usage_data(org_id, str(chat_message_id), str(chat_id)) - + if token_data is None: - return Response( - {"error": "Failed to retrieve token usage data"}, - status=status.HTTP_500_INTERNAL_SERVER_ERROR - ) - + # Token tracking not available (OSS mode without subscriptions module) + return Response({ + "organization_id": org_id, + "remaining_balance": 0, + "total_consumed": 0, + "total_purchased": 0, + "utilization_percentage": 0, + "message_tokens_consumed": 0, + "token_usage_found": False, + "token_tracking_available": False, + }, status=status.HTTP_200_OK) + return Response(token_data, status=status.HTTP_200_OK) - + except Exception as e: return Response( {"error": f"Error retrieving token usage: {str(e)}"}, From 69cca9f4ab899164f5f1dad7cbc157d84095b23e Mon Sep 17 00:00:00 2001 From: Deepak-Gnanasekar Date: Tue, 24 Mar 2026 11:10:05 +0530 Subject: [PATCH 2/5] optional handeling of token API call based on simple session check --- .../chat_message/views/message_views.py | 18 ++++-------------- frontend/src/ide/chat-ai/ExistingChat.jsx | 7 +++++-- 2 files changed, 9 insertions(+), 16 deletions(-) diff --git a/backend/backend/core/routers/chat_message/views/message_views.py b/backend/backend/core/routers/chat_message/views/message_views.py index 190a550..b519b9c 100644 --- a/backend/backend/core/routers/chat_message/views/message_views.py +++ b/backend/backend/core/routers/chat_message/views/message_views.py @@ -58,9 +58,6 @@ def get_token_usage(self, request, project_id=None, chat_id=None, chat_message_i - total_consumed: Total tokens consumed - message_tokens_consumed: Tokens consumed for this specific message - token_usage_found: Whether token usage was found for this message - - In OSS mode (no subscriptions module), returns a response indicating - token tracking is not available rather than a 500 error. """ try: # Get organization ID from header @@ -75,17 +72,10 @@ def get_token_usage(self, request, project_id=None, chat_id=None, chat_message_i token_data = get_token_usage_data(org_id, str(chat_message_id), str(chat_id)) if token_data is None: - # Token tracking not available (OSS mode without subscriptions module) - return Response({ - "organization_id": org_id, - "remaining_balance": 0, - "total_consumed": 0, - "total_purchased": 0, - "utilization_percentage": 0, - "message_tokens_consumed": 0, - "token_usage_found": False, - "token_tracking_available": False, - }, status=status.HTTP_200_OK) + return Response( + {"error": "Failed to retrieve token usage data"}, + status=status.HTTP_500_INTERNAL_SERVER_ERROR + ) return Response(token_data, status=status.HTTP_200_OK) diff --git a/frontend/src/ide/chat-ai/ExistingChat.jsx b/frontend/src/ide/chat-ai/ExistingChat.jsx index 89c2810..2bb8915 100644 --- a/frontend/src/ide/chat-ai/ExistingChat.jsx +++ b/frontend/src/ide/chat-ai/ExistingChat.jsx @@ -17,6 +17,7 @@ import { OnboardingGuide } from "./OnboardingGuide"; import { OnboardingCompletionPopup } from "./OnboardingCompletionPopup"; import { useNotificationService } from "../../service/notification-service"; import { SpinnerLoader } from "../../widgets/spinner_loader"; +import { useSessionStore } from "../../store/session-store"; const ExistingChat = memo(function ExistingChat({ selectedChatId, @@ -63,6 +64,7 @@ const ExistingChat = memo(function ExistingChat({ }) { const { getChatMessagesByChatId, getTokenUsage, updateChatName } = useChatAIService(); + const isCloud = useSessionStore((state) => state.sessionDetails?.is_cloud); const chatContainerRef = useRef(null); const [isLoadingChats, setIsLoadingChats] = useState(false); @@ -262,8 +264,9 @@ const ExistingChat = memo(function ExistingChat({ : [msg.response].filter(Boolean), })); - // Fetch token usage for all messages to display in historical conversations - if (updatedData.length > 0) { + // Fetch token usage for all messages to display in historical conversations. + // In OSS mode, token usage data comes through WebSocket — skip the API call. + if (isCloud && updatedData.length > 0) { const tokenUsagePromises = updatedData.map((msg) => getTokenUsage(selectedChatId, msg.chat_message_id).catch(() => null) ); From 019624b8e2cfd49745f4f1dba349d16cc57bb01a Mon Sep 17 00:00:00 2001 From: Deepak-Gnanasekar Date: Tue, 24 Mar 2026 13:50:03 +0530 Subject: [PATCH 3/5] optional import for the service API instead checks --- frontend/src/ide/chat-ai/ExistingChat.jsx | 15 +++++++++++---- frontend/src/ide/chat-ai/services.js | 17 ----------------- 2 files changed, 11 insertions(+), 21 deletions(-) diff --git a/frontend/src/ide/chat-ai/ExistingChat.jsx b/frontend/src/ide/chat-ai/ExistingChat.jsx index 2bb8915..29a27c5 100644 --- a/frontend/src/ide/chat-ai/ExistingChat.jsx +++ b/frontend/src/ide/chat-ai/ExistingChat.jsx @@ -19,6 +19,14 @@ import { useNotificationService } from "../../service/notification-service"; import { SpinnerLoader } from "../../widgets/spinner_loader"; import { useSessionStore } from "../../store/session-store"; +// Cloud-only: fetch per-message token usage (unavailable in OSS — import fails gracefully) +let getTokenUsage = null; +try { + ({ getTokenUsage } = require("../../plugins/token-management/token-usage")); +} catch { + // OSS: token usage API not available +} + const ExistingChat = memo(function ExistingChat({ selectedChatId, chatName, @@ -62,8 +70,7 @@ const ExistingChat = memo(function ExistingChat({ onSkipCurrentTask, onSendButtonClick, }) { - const { getChatMessagesByChatId, getTokenUsage, updateChatName } = - useChatAIService(); + const { getChatMessagesByChatId, updateChatName } = useChatAIService(); const isCloud = useSessionStore((state) => state.sessionDetails?.is_cloud); const chatContainerRef = useRef(null); @@ -265,8 +272,8 @@ const ExistingChat = memo(function ExistingChat({ })); // Fetch token usage for all messages to display in historical conversations. - // In OSS mode, token usage data comes through WebSocket — skip the API call. - if (isCloud && updatedData.length > 0) { + // Only available in cloud mode via the token-usage plugin. + if (getTokenUsage && isCloud && updatedData.length > 0) { const tokenUsagePromises = updatedData.map((msg) => getTokenUsage(selectedChatId, msg.chat_message_id).catch(() => null) ); diff --git a/frontend/src/ide/chat-ai/services.js b/frontend/src/ide/chat-ai/services.js index c54e66f..1fb5f7a 100644 --- a/frontend/src/ide/chat-ai/services.js +++ b/frontend/src/ide/chat-ai/services.js @@ -134,22 +134,6 @@ export function useChatAIService() { return response.data; }; - const getTokenUsage = async (chatId, chatMessageId) => { - const url = `/api/v1/visitran/${orgId}/project/${projectId}/chat/${chatId}/chat-message/${chatMessageId}/token-usage/`; - try { - const response = await axiosPrivate.get(url, { - headers: { - "Content-Type": "application/json", - "X-Organization": orgId, - }, - }); - return response.data || null; - } catch (error) { - console.warn("Failed to fetch token usage:", error); - return null; - } - }; - return { getAllChats, deleteChatById, @@ -161,6 +145,5 @@ export function useChatAIService() { getChatLlmModels, completeOnboardingTask, getOnboardingStatus, - getTokenUsage, }; } From e6773c6ff8dca53fb9a13708bd111cd44e321b25 Mon Sep 17 00:00:00 2001 From: Deepak-Gnanasekar Date: Tue, 24 Mar 2026 15:19:42 +0530 Subject: [PATCH 4/5] using axios private instead axios --- frontend/src/ide/chat-ai/ExistingChat.jsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/frontend/src/ide/chat-ai/ExistingChat.jsx b/frontend/src/ide/chat-ai/ExistingChat.jsx index 29a27c5..bf2172b 100644 --- a/frontend/src/ide/chat-ai/ExistingChat.jsx +++ b/frontend/src/ide/chat-ai/ExistingChat.jsx @@ -16,6 +16,7 @@ import { TodoGuide } from "./TodoGuide"; import { OnboardingGuide } from "./OnboardingGuide"; import { OnboardingCompletionPopup } from "./OnboardingCompletionPopup"; import { useNotificationService } from "../../service/notification-service"; +import { useAxiosPrivate } from "../../service/axios-service"; import { SpinnerLoader } from "../../widgets/spinner_loader"; import { useSessionStore } from "../../store/session-store"; @@ -71,6 +72,7 @@ const ExistingChat = memo(function ExistingChat({ onSendButtonClick, }) { const { getChatMessagesByChatId, updateChatName } = useChatAIService(); + const axiosPrivate = useAxiosPrivate(); const isCloud = useSessionStore((state) => state.sessionDetails?.is_cloud); const chatContainerRef = useRef(null); @@ -275,7 +277,7 @@ const ExistingChat = memo(function ExistingChat({ // Only available in cloud mode via the token-usage plugin. if (getTokenUsage && isCloud && updatedData.length > 0) { const tokenUsagePromises = updatedData.map((msg) => - getTokenUsage(selectedChatId, msg.chat_message_id).catch(() => null) + getTokenUsage(axiosPrivate, selectedChatId, msg.chat_message_id).catch(() => null) ); const tokenUsageResults = await Promise.all(tokenUsagePromises); From 217595c65d843b539d84c503784b2a885b2d690d Mon Sep 17 00:00:00 2001 From: Deepak-Gnanasekar Date: Tue, 24 Mar 2026 15:30:27 +0530 Subject: [PATCH 5/5] lint fix --- frontend/src/ide/chat-ai/ExistingChat.jsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/frontend/src/ide/chat-ai/ExistingChat.jsx b/frontend/src/ide/chat-ai/ExistingChat.jsx index bf2172b..a63f3ca 100644 --- a/frontend/src/ide/chat-ai/ExistingChat.jsx +++ b/frontend/src/ide/chat-ai/ExistingChat.jsx @@ -277,7 +277,11 @@ const ExistingChat = memo(function ExistingChat({ // Only available in cloud mode via the token-usage plugin. if (getTokenUsage && isCloud && updatedData.length > 0) { const tokenUsagePromises = updatedData.map((msg) => - getTokenUsage(axiosPrivate, selectedChatId, msg.chat_message_id).catch(() => null) + getTokenUsage( + axiosPrivate, + selectedChatId, + msg.chat_message_id + ).catch(() => null) ); const tokenUsageResults = await Promise.all(tokenUsagePromises);