From 21dc10800b697a11889c682de7d78dafd7aa03ea Mon Sep 17 00:00:00 2001 From: Arghya721 Date: Mon, 8 Jul 2024 01:13:56 +0530 Subject: [PATCH] refactor: Implement lazy loading for images in DashboardV2 and update chat route --- app.py | 281 +++++++++++++++++++++-------- web/src/components/PlansModal.js | 177 ++++++++++++------ web/src/images/congratulations.gif | Bin 0 -> 971349 bytes 3 files changed, 327 insertions(+), 131 deletions(-) create mode 100644 web/src/images/congratulations.gif diff --git a/app.py b/app.py index 561daa7..5edf530 100644 --- a/app.py +++ b/app.py @@ -139,33 +139,114 @@ class SubscriptionRequest(BaseModel): redirect_url: str model_company_mapping = { - "gpt-3.5-turbo": ChatOpenAI, - "gpt-4-turbo-preview": ChatOpenAI, - "gpt-4": ChatOpenAI, - "gpt-4o": ChatOpenAI, - "claude-3-opus-20240229" : ChatAnthropic, - "claude-3-sonnet-20240229" : ChatAnthropic, - "claude-3-haiku-20240307" : ChatAnthropic, - "mistral-tiny-2312": ChatMistralAI, - "mistral-small-2312": ChatMistralAI, - "mistral-small-2402": ChatMistralAI, - "mistral-medium-2312": ChatMistralAI, - "mistral-large-2402": ChatMistralAI, - "gemini-1.0-pro": ChatGoogleGenerativeAI, - "gemini-1.5-flash-latest": ChatGoogleGenerativeAI, - "gemini-1.5-pro-latest": ChatGoogleGenerativeAI, - "sonar-small-chat": ChatPerplexity, - "sonar-small-online": ChatPerplexity, - "sonar-medium-chat" : ChatPerplexity, - "sonar-medium-online" : ChatPerplexity, - "codellama/CodeLlama-34b-Instruct-hf": ChatTogether, - "codellama/CodeLlama-70b-Instruct-hf": ChatTogether, - "meta-llama/Llama-2-13b-chat-hf": ChatTogether, - "meta-llama/Llama-2-70b-chat-hf": ChatTogether, - "meta-llama/Llama-3-8b-chat-hf" : ChatTogether, - "meta-llama/Llama-3-70b-chat-hf": ChatTogether, - "google/gemma-2b-it": ChatTogether, - "google/gemma-7b-it": ChatTogether, + "gpt-3.5-turbo": { + "model" : ChatOpenAI, + "premium" : False, + }, + "gpt-4-turbo-preview": { + "model" : ChatOpenAI, + "premium" : True, + }, + "gpt-4": { + "model" : ChatOpenAI, + "premium" : True, + }, + "gpt-4o": { + "model" : ChatOpenAI, + "premium" : True, + }, + "claude-3-opus-20240229" : { + "model" : ChatAnthropic, + "premium" : True, + }, + "claude-3-sonnet-20240229" : { + "model" : ChatAnthropic, + "premium" : True, + }, + "claude-3-haiku-20240307" : { + "model" : ChatAnthropic, + "premium" : False, + }, + "mistral-tiny-2312": { + "model" : ChatMistralAI, + "premium" : False, + }, + "mistral-small-2312": { + "model" : ChatMistralAI, + "premium" : False, + }, + "mistral-small-2402": { + "model" : ChatMistralAI, + "premium" : False, + }, + "mistral-medium-2312": { + "model" : ChatMistralAI, + "premium" : True, + }, + "mistral-large-2402": { + "model" : ChatMistralAI, + "premium" : True, + }, + "gemini-1.0-pro": { + "model" : ChatGoogleGenerativeAI, + "premium" : False, + }, + "gemini-1.5-flash-latest": { + "model" : ChatGoogleGenerativeAI, + "premium" : False, + }, + "gemini-1.5-pro-latest": { + "model" : ChatGoogleGenerativeAI, + "premium" : True, + }, + "sonar-small-chat": { + "model" : ChatPerplexity, + "premium" : False, + }, + "sonar-small-online": { + "model" : ChatPerplexity, + "premium" : True, + }, + "sonar-medium-chat" : { + "model" : ChatPerplexity, + "premium" : False, + }, + "sonar-medium-online" : { + "model" : ChatPerplexity, + "premium" : True, + }, + "codellama/CodeLlama-34b-Instruct-hf": { + "model" : ChatTogether, + "premium" : False, + }, + "codellama/CodeLlama-70b-Instruct-hf": { + "model" : ChatTogether, + "premium" : True, + }, + "meta-llama/Llama-2-13b-chat-hf": { + "model" : ChatTogether, + "premium" : False, + }, + "meta-llama/Llama-2-70b-chat-hf": { + "model" : ChatTogether, + "premium" : True, + }, + "meta-llama/Llama-3-8b-chat-hf" : { + "model" : ChatTogether, + "premium" : False, + }, + "meta-llama/Llama-3-70b-chat-hf": { + "model" : ChatTogether, + "premium" : True, + }, + "google/gemma-2b-it": { + "model" : ChatTogether, + "premium" : False, + }, + "google/gemma-7b-it": { + "model" : ChatTogether, + "premium" : False, + }, } @@ -317,6 +398,24 @@ async def verify_google_token(background_tasks: BackgroundTasks, credentials: HT ) +def verify_active_subscription(token_info: dict = Depends(verify_google_token)): + """Verify the active subscription for the user.""" + + # check if the customer has a subscription + subscription_ref = db.collection('subscriptions').where('customer_id', '==', token_info['sub']).stream() + + # check if the customer has an active subscription + for doc in subscription_ref: + subscription_data = doc.to_dict() + subscription_status = subscription.fetch(subscription_data['subscription_id'])['status'] + if subscription_status == 'active': + return True + + raise HTTPException( + status_code=status.HTTP_403_FORBIDDEN, + detail="Forbidden", + ) + @app.get("/auth/google", response_model=dict, tags=["Authentication Endpoints"]) async def google_auth(idinfo: dict = Depends(verify_google_token)): """Google authentication endpoint to verify the Google ID token.""" @@ -349,54 +448,56 @@ async def verify_token_info(token_info: dict = Depends(verify_token)): return {"token_info": token_info} -@app.post("/v1/chat", response_model=ChatResponse, tags=["AI Endpoints"]) -async def chat_conversation(request: ChatRequest, token_info: dict = Depends(verify_token)): - """Chat endpoint for the OpenAI chatbot.""" - try: - # Get the chat model from the request and create the corresponding chat instance - chat_model = request.chat_model - chat = model_company_mapping.get(chat_model) - if chat is None: - raise ValueError(f"Invalid chat model: {chat_model}") - - print("Chat model: ", chat_model) - - - # Create the chat prompt and memory for the conversation - chat = chat( - model_name=chat_model, - model=chat_model, - temperature=request.temperature, - ) - - - prompt = ChatPromptTemplate( - messages=[ - # SystemMessagePromptTemplate.from_template(""), - MessagesPlaceholder(variable_name="chat_history"), - HumanMessagePromptTemplate.from_template("{user_input}"), - ] - ) - memory = ConversationBufferMemory(memory_key="chat_history", return_messages=True) - conversation = LLMChain(llm=chat, memory=memory, prompt=prompt, verbose=False) - # Seed the chat history with the user's input from the request - for chat_history in request.chat_history: - memory.chat_memory.add_user_message(chat_history.user_message) - memory.chat_memory.add_ai_message(chat_history.ai_message) - # Run the conversation.invoke method in a separate thread - response = conversation.invoke(input=request.user_input) +# @app.post("/v1/chat", response_model=ChatResponse, tags=["AI Endpoints"]) +# async def chat_conversation(request: ChatRequest, token_info: dict = Depends(verify_token)): +# """Chat endpoint for the OpenAI chatbot.""" +# try: +# # Get the chat model from the request and create the corresponding chat instance +# chat_model = request.chat_model +# chat = model_company_mapping.get(chat_model) +# if chat is None: +# raise ValueError(f"Invalid chat model: {chat_model}") + +# print("Chat model: ", chat_model) - return ChatResponse(response=response["text"]) - except ValidationError as ve: - # Handle validation errors specifically for better user feedback - logging.error("Validation error: %s", ve) - raise HTTPException(status_code=400, detail="Invalid request data") from ve - except Exception as e: - # Log and handle generic exceptions gracefully - logging.error("Error processing chat request: %s", e) - raise HTTPException(status_code=500, detail="Internal server error") from e + +# # Create the chat prompt and memory for the conversation +# chat = chat( +# model_name=chat_model, +# model=chat_model, +# temperature=request.temperature, +# ) + + +# prompt = ChatPromptTemplate( +# messages=[ +# # SystemMessagePromptTemplate.from_template(""), +# MessagesPlaceholder(variable_name="chat_history"), +# HumanMessagePromptTemplate.from_template("{user_input}"), +# ] +# ) +# memory = ConversationBufferMemory(memory_key="chat_history", return_messages=True) +# conversation = LLMChain(llm=chat, memory=memory, prompt=prompt, verbose=False) + +# # Seed the chat history with the user's input from the request +# for chat_history in request.chat_history: +# memory.chat_memory.add_user_message(chat_history.user_message) +# memory.chat_memory.add_ai_message(chat_history.ai_message) + +# # Run the conversation.invoke method in a separate thread +# response = conversation.invoke(input=request.user_input) + +# return ChatResponse(response=response["text"]) +# except ValidationError as ve: +# # Handle validation errors specifically for better user feedback +# logging.error("Validation error: %s", ve) +# raise HTTPException(status_code=400, detail="Invalid request data") from ve +# except Exception as e: +# # Log and handle generic exceptions gracefully +# logging.error("Error processing chat request: %s", e) +# raise HTTPException(status_code=500, detail="Internal server error") from e @app.post("/v1/chat_event_streaming", tags=["AI Endpoints"]) async def chat_event_streaming(request: ChatRequest, token_info: dict = Depends(verify_token)): @@ -404,13 +505,19 @@ async def chat_event_streaming(request: ChatRequest, token_info: dict = Depends( try: # Get the chat model from the request and create the corresponding chat instance chat_model = request.chat_model - chat = model_company_mapping.get(chat_model) - if chat is None: + chat_config = model_company_mapping.get(chat_model) + + if not chat_config: raise ValueError(f"Invalid chat model: {chat_model}") - + + if chat_config['premium'] == True: + if not verify_active_subscription(token_info): + raise HTTPException( + status_code=status.HTTP_403_FORBIDDEN, + detail="Forbidden", + ) - # Create the chat prompt and memory for the conversation - chat = chat( + chat = chat_config['model']( model_name=chat_model, model=chat_model, temperature=request.temperature, @@ -603,7 +710,21 @@ async def get_subscriptions(request: SubscriptionRequest, token_info: dict = Dep # check if customer id exists in the database customer_ref = db.collection('users').document(token_info['sub']) customer_data = customer_ref.get() - + + # check if the customer has a subscription + subscription_ref = db.collection('subscriptions').where('customer_id', '==', token_info['sub']).stream() + + # check if the customer has an active subscription + for doc in subscription_ref: + subscription_data = doc.to_dict() + print(subscription_data['subscription_id']) + subscription_status = subscription.fetch(subscription_data['subscription_id'])['status'] + if subscription_status == 'active': + # return the 400 status code with message + raise HTTPException( + status_code=status.HTTP_400_BAD_REQUEST, + detail="Subscription already exists", + ) subscription_data = { "plan_id": PLAN_ID, @@ -631,6 +752,10 @@ async def get_subscriptions(request: SubscriptionRequest, token_info: dict = Dep }) return subscription_response + + + except HTTPException as e: + raise e except Exception as e: logging.error("Error getting subscriptions: %s", e) diff --git a/web/src/components/PlansModal.js b/web/src/components/PlansModal.js index 50ae9e4..1585908 100644 --- a/web/src/components/PlansModal.js +++ b/web/src/components/PlansModal.js @@ -1,6 +1,7 @@ import React, { useEffect, useState } from "react"; import { Modal, ModalContent, ModalHeader, ModalBody, ModalFooter, Button, Spinner } from "@nextui-org/react"; import axios from "axios"; +import congratulations from "../images/congratulations.gif"; export const PlansModal = ({ isOpen, onClose }) => { const API_HOST = process.env.REACT_APP_API_HOST || "http://localhost:5000"; @@ -8,6 +9,7 @@ export const PlansModal = ({ isOpen, onClose }) => { const [plans, setPlans] = useState(); const [loading, setLoading] = useState(false); + const [paymentStatus, setPaymentStatus] = useState(null); const createSubscription = async () => { setLoading(true); @@ -25,51 +27,92 @@ export const PlansModal = ({ isOpen, onClose }) => { }, } ); - + if (response.data && response.data.short_url) { - // Open the payment URL in a popup const paymentPopup = window.open( response.data.short_url, 'RazorpayPayment', 'width=600,height=600,resizable=yes,scrollbars=yes,status=yes' ); - // Check if popup was blocked by the browser if (!paymentPopup || paymentPopup.closed || typeof paymentPopup.closed == 'undefined') { alert("Popup blocked. Please allow popups for this site to proceed with the payment."); + setLoading(false); + } else { + // Check if the popup is closed every second + const checkPopupClosed = setInterval(() => { + if (paymentPopup.closed) { + clearInterval(checkPopupClosed); + handlePopupClosed(); + } + }, 1000); } - - // Listen for messages from the popup - window.addEventListener('message', handlePaymentMessage, false); } else { console.error('No payment URL received'); + setLoading(false); } } catch (error) { console.error('Error creating subscription:', error); - } finally { setLoading(false); } }; - const handlePaymentMessage = (event) => { - // Verify the origin of the message - if (event.origin !== window.location.origin) return; - - if (event.data.paymentStatus === 'success') { - // Handle successful payment - console.log('Payment successful'); - onClose(); // Close the modal - // You might want to update the user's subscription status here - } else if (event.data.paymentStatus === 'failure') { - // Handle payment failure - console.log('Payment failed'); + const checkSubscriptionStatus = async () => { + try { + // fetch the latest subscription + const response = await axios.get( + `${API_HOST}/v1/fetch_subscription`, + { + headers: { + Authorization: `Bearer ${accessToken}`, + }, + } + ); + // if response is 200, then get the top level key of the response + if (response.status === 200 && response.data.length > 0) { + const subscriptionId = response.data[0].subscription_id; + // check the subscription status + const subscriptionStatus = await axios.get( + `${API_HOST}/v1/subscriptions/${subscriptionId}/status`, + { + headers: { + Authorization: `Bearer ${accessToken}`, + }, + } + ); + console.log('Subscription status:', subscriptionStatus.data); + // if the subscription status is active, then set the payment status to success + if (subscriptionStatus?.data?.status === 'active') { + return true; + } else { + return false; + } + } } + catch (error) { + console.error('Error fetching subscription:', error); + } + } - // Remove the event listener - window.removeEventListener('message', handlePaymentMessage); + const handlePopupClosed = async () => { + try { + const subscriptionStatus = await checkSubscriptionStatus(); + if (subscriptionStatus) { + setPaymentStatus('success'); + } else { + setPaymentStatus('failed'); + } + setLoading(false); + } catch (error) { + console.error('Error handling popup closed:', error); + setLoading(false); + } finally { + setLoading(false); + } }; useEffect(() => { + const fetchPlans = async () => { try { const response = await axios.get(`${API_HOST}/v1/plans`, { @@ -83,7 +126,14 @@ export const PlansModal = ({ isOpen, onClose }) => { } }; - fetchPlans(); + checkSubscriptionStatus().then((status) => { + if (status) { + setPaymentStatus('success'); + } else { + fetchPlans(); + } + }); + }, [API_HOST, accessToken]); return ( @@ -92,41 +142,62 @@ export const PlansModal = ({ isOpen, onClose }) => {

Plus Subscription

- -
-
- Price: - ₹ {plans?.amount} -
-
- - - - Early access to new features + + {paymentStatus === 'success' ? ( +
+ Congratulations +

+ Congratulations! You can now enjoy the Plus subscription and its benefits. +

+

+ Explore advanced features and powerful AI models at your fingertips. +

-
- - - - Access to powerful models like GPT-4, Claude-Opus, Gemini-1.5-Pro, and many more + ) : ( +
+
+ Price: + ₹ {plans?.amount} +
+
+ + + + Early access to new features +
+
+ + + + Access to powerful models like GPT-4, Claude-Opus, Gemini-1.5-Pro, and many more +
-
+ )}
- + {paymentStatus === 'success' ? ( + + ) : ( + + )} diff --git a/web/src/images/congratulations.gif b/web/src/images/congratulations.gif new file mode 100644 index 0000000000000000000000000000000000000000..95a644d228463a86cf39edd71ef3c46c0d319345 GIT binary patch literal 971349 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 zΖ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