Subscription dashboard improvements, AI text generation limit, and other fixes.

This commit is contained in:
ajaysi
2025-11-01 18:01:14 +05:30
parent cdb41aec1b
commit de4328175d
64 changed files with 5809 additions and 444 deletions

View File

@@ -0,0 +1,146 @@
"""
Migration Script: Add ai_text_generation_calls_limit column to subscription_plans table.
This adds the unified AI text generation limit column that applies to all LLM providers
(gemini, openai, anthropic, mistral) instead of per-provider limits.
"""
import sys
import os
from pathlib import Path
from datetime import datetime, timezone
# Add the backend directory to Python path
backend_dir = Path(__file__).parent.parent
sys.path.insert(0, str(backend_dir))
from sqlalchemy import create_engine, text, inspect
from sqlalchemy.orm import sessionmaker
from loguru import logger
from models.subscription_models import SubscriptionPlan, SubscriptionTier
from services.database import DATABASE_URL
def add_ai_text_generation_limit_column():
"""Add ai_text_generation_calls_limit column to subscription_plans table."""
try:
engine = create_engine(DATABASE_URL, echo=False)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
db = SessionLocal()
try:
# Check if column already exists
inspector = inspect(engine)
columns = [col['name'] for col in inspector.get_columns('subscription_plans')]
if 'ai_text_generation_calls_limit' in columns:
logger.info("✅ Column 'ai_text_generation_calls_limit' already exists. Skipping migration.")
return True
logger.info("📋 Adding 'ai_text_generation_calls_limit' column to subscription_plans table...")
# Add the column (SQLite compatible)
alter_query = text("""
ALTER TABLE subscription_plans
ADD COLUMN ai_text_generation_calls_limit INTEGER DEFAULT 0
""")
db.execute(alter_query)
db.commit()
logger.info("✅ Column added successfully!")
# Update existing plans with unified limits based on their current limits
logger.info("\n🔄 Updating existing subscription plans with unified limits...")
plans = db.query(SubscriptionPlan).all()
updated_count = 0
for plan in plans:
# Use the first non-zero LLM provider limit as the unified limit
# Or use gemini_calls_limit as default
unified_limit = (
plan.ai_text_generation_calls_limit or
plan.gemini_calls_limit or
plan.openai_calls_limit or
plan.anthropic_calls_limit or
plan.mistral_calls_limit or
0
)
# For Basic plan, ensure it's set to 10 (from our recent update)
if plan.tier == SubscriptionTier.BASIC:
unified_limit = 10
if plan.ai_text_generation_calls_limit != unified_limit:
plan.ai_text_generation_calls_limit = unified_limit
plan.updated_at = datetime.now(timezone.utc)
updated_count += 1
logger.info(f" ✅ Updated {plan.name} ({plan.tier.value}): ai_text_generation_calls_limit = {unified_limit}")
else:
logger.info(f" {plan.name} ({plan.tier.value}): already set to {unified_limit}")
if updated_count > 0:
db.commit()
logger.info(f"\n✅ Updated {updated_count} subscription plan(s)")
else:
logger.info("\n No plans needed updating")
# Display summary
logger.info("\n" + "="*60)
logger.info("MIGRATION SUMMARY")
logger.info("="*60)
all_plans = db.query(SubscriptionPlan).all()
for plan in all_plans:
logger.info(f"\n{plan.name} ({plan.tier.value}):")
logger.info(f" Unified AI Text Gen Limit: {plan.ai_text_generation_calls_limit if plan.ai_text_generation_calls_limit else 'Not set'}")
logger.info(f" Legacy Limits: gemini={plan.gemini_calls_limit}, mistral={plan.mistral_calls_limit}")
logger.info("\n" + "="*60)
logger.info("✅ Migration completed successfully!")
return True
except Exception as e:
db.rollback()
logger.error(f"❌ Error during migration: {e}")
import traceback
logger.error(traceback.format_exc())
raise
finally:
db.close()
except Exception as e:
logger.error(f"❌ Failed to connect to database: {e}")
import traceback
logger.error(traceback.format_exc())
return False
if __name__ == "__main__":
logger.info("🚀 Starting ai_text_generation_calls_limit column migration...")
logger.info("="*60)
logger.info("This will add the unified AI text generation limit column")
logger.info("and update existing plans with appropriate values.")
logger.info("="*60)
try:
success = add_ai_text_generation_limit_column()
if success:
logger.info("\n✅ Script completed successfully!")
sys.exit(0)
else:
logger.error("\n❌ Script failed!")
sys.exit(1)
except KeyboardInterrupt:
logger.info("\n⚠️ Script cancelled by user")
sys.exit(1)
except Exception as e:
logger.error(f"\n❌ Unexpected error: {e}")
sys.exit(1)

View File

@@ -0,0 +1,210 @@
"""
Standalone script to cap usage counters at new Basic plan limits.
This preserves historical usage data but caps it at the new limits so users
can continue making new calls within their limits.
"""
import sys
import os
from pathlib import Path
from datetime import datetime, timezone
# Add the backend directory to Python path
backend_dir = Path(__file__).parent.parent
sys.path.insert(0, str(backend_dir))
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from loguru import logger
from models.subscription_models import SubscriptionPlan, SubscriptionTier, UserSubscription, UsageSummary, UsageStatus
from services.database import DATABASE_URL
from services.subscription import PricingService
def cap_basic_plan_usage():
"""Cap usage counters at new Basic plan limits."""
try:
engine = create_engine(DATABASE_URL, echo=False)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
db = SessionLocal()
try:
# Find Basic plan
basic_plan = db.query(SubscriptionPlan).filter(
SubscriptionPlan.tier == SubscriptionTier.BASIC
).first()
if not basic_plan:
logger.error("❌ Basic plan not found in database!")
return False
# New limits
new_call_limit = basic_plan.gemini_calls_limit # Should be 10
new_token_limit = basic_plan.gemini_tokens_limit # Should be 2000
new_image_limit = basic_plan.stability_calls_limit # Should be 5
logger.info(f"📋 Basic Plan Limits:")
logger.info(f" Calls: {new_call_limit}")
logger.info(f" Tokens: {new_token_limit}")
logger.info(f" Images: {new_image_limit}")
# Get all Basic plan users
user_subscriptions = db.query(UserSubscription).filter(
UserSubscription.plan_id == basic_plan.id,
UserSubscription.is_active == True
).all()
logger.info(f"\n👥 Found {len(user_subscriptions)} Basic plan user(s)")
pricing_service = PricingService(db)
capped_count = 0
for sub in user_subscriptions:
try:
# Get current billing period for this user
current_period = pricing_service.get_current_billing_period(sub.user_id) or datetime.now(timezone.utc).strftime("%Y-%m")
# Find usage summary for current period
usage_summary = db.query(UsageSummary).filter(
UsageSummary.user_id == sub.user_id,
UsageSummary.billing_period == current_period
).first()
if usage_summary:
# Store old values for logging
old_gemini = usage_summary.gemini_calls or 0
old_mistral = usage_summary.mistral_calls or 0
old_openai = usage_summary.openai_calls or 0
old_anthropic = usage_summary.anthropic_calls or 0
old_tokens = max(
usage_summary.gemini_tokens or 0,
usage_summary.openai_tokens or 0,
usage_summary.anthropic_tokens or 0,
usage_summary.mistral_tokens or 0
)
old_images = usage_summary.stability_calls or 0
# Check if capping is needed
needs_cap = (
old_gemini > new_call_limit or
old_mistral > new_call_limit or
old_openai > new_call_limit or
old_anthropic > new_call_limit or
old_images > new_image_limit or
old_tokens > new_token_limit
)
if needs_cap:
# Cap LLM provider counters at new limits
usage_summary.gemini_calls = min(old_gemini, new_call_limit)
usage_summary.mistral_calls = min(old_mistral, new_call_limit)
usage_summary.openai_calls = min(old_openai, new_call_limit)
usage_summary.anthropic_calls = min(old_anthropic, new_call_limit)
# Cap token counters at new limits
usage_summary.gemini_tokens = min(usage_summary.gemini_tokens or 0, new_token_limit)
usage_summary.openai_tokens = min(usage_summary.openai_tokens or 0, new_token_limit)
usage_summary.anthropic_tokens = min(usage_summary.anthropic_tokens or 0, new_token_limit)
usage_summary.mistral_tokens = min(usage_summary.mistral_tokens or 0, new_token_limit)
# Cap image counter at new limit
usage_summary.stability_calls = min(old_images, new_image_limit)
# Recalculate totals based on capped values
total_capped_calls = (
usage_summary.gemini_calls +
usage_summary.mistral_calls +
usage_summary.openai_calls +
usage_summary.anthropic_calls +
usage_summary.stability_calls
)
total_capped_tokens = (
usage_summary.gemini_tokens +
usage_summary.mistral_tokens +
usage_summary.openai_tokens +
usage_summary.anthropic_tokens
)
usage_summary.total_calls = total_capped_calls
usage_summary.total_tokens = total_capped_tokens
# Reset status to active to allow new calls
usage_summary.usage_status = UsageStatus.ACTIVE
usage_summary.updated_at = datetime.now(timezone.utc)
db.commit()
capped_count += 1
logger.info(f"\n✅ Capped usage for user {sub.user_id} (period {current_period}):")
logger.info(f" Gemini Calls: {old_gemini}{usage_summary.gemini_calls} (limit: {new_call_limit})")
logger.info(f" Mistral Calls: {old_mistral}{usage_summary.mistral_calls} (limit: {new_call_limit})")
logger.info(f" OpenAI Calls: {old_openai}{usage_summary.openai_calls} (limit: {new_call_limit})")
logger.info(f" Anthropic Calls: {old_anthropic}{usage_summary.anthropic_calls} (limit: {new_call_limit})")
logger.info(f" Tokens: {old_tokens}{max(usage_summary.gemini_tokens, usage_summary.mistral_tokens)} (limit: {new_token_limit})")
logger.info(f" Images: {old_images}{usage_summary.stability_calls} (limit: {new_image_limit})")
else:
logger.info(f" User {sub.user_id} usage is within limits - no capping needed")
else:
logger.info(f" No usage summary found for user {sub.user_id} (period {current_period})")
except Exception as cap_error:
logger.error(f" ❌ Error capping usage for user {sub.user_id}: {cap_error}")
import traceback
logger.error(traceback.format_exc())
db.rollback()
if capped_count > 0:
logger.info(f"\n✅ Successfully capped usage for {capped_count} user(s)")
logger.info(" Historical usage preserved, but capped at new limits")
logger.info(" Users can now make new calls within their limits")
else:
logger.info("\n No usage counters needed capping")
logger.info("\n" + "="*60)
logger.info("CAPPING COMPLETE")
logger.info("="*60)
return True
except Exception as e:
db.rollback()
logger.error(f"❌ Error capping usage: {e}")
import traceback
logger.error(traceback.format_exc())
raise
finally:
db.close()
except Exception as e:
logger.error(f"❌ Failed to connect to database: {e}")
import traceback
logger.error(traceback.format_exc())
return False
if __name__ == "__main__":
logger.info("🚀 Starting Basic plan usage capping...")
logger.info("="*60)
logger.info("This will cap usage counters at new Basic plan limits")
logger.info("while preserving historical usage data.")
logger.info("="*60)
try:
success = cap_basic_plan_usage()
if success:
logger.info("\n✅ Script completed successfully!")
sys.exit(0)
else:
logger.error("\n❌ Script failed!")
sys.exit(1)
except KeyboardInterrupt:
logger.info("\n⚠️ Script cancelled by user")
sys.exit(1)
except Exception as e:
logger.error(f"\n❌ Unexpected error: {e}")
sys.exit(1)

View File

@@ -0,0 +1,168 @@
"""
Quick script to reset usage counters for Basic plan users.
This fixes the issue where plan limits were updated but old usage data remained.
Resets all usage counters (calls, tokens, images) to 0 for the current billing period.
"""
import sys
import os
from pathlib import Path
from datetime import datetime, timezone
# Add the backend directory to Python path
backend_dir = Path(__file__).parent.parent
sys.path.insert(0, str(backend_dir))
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from loguru import logger
from models.subscription_models import SubscriptionPlan, SubscriptionTier, UserSubscription, UsageSummary, UsageStatus
from services.database import DATABASE_URL
from services.subscription import PricingService
def reset_basic_plan_usage():
"""Reset usage counters for all Basic plan users."""
try:
engine = create_engine(DATABASE_URL, echo=False)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
db = SessionLocal()
try:
# Find Basic plan
basic_plan = db.query(SubscriptionPlan).filter(
SubscriptionPlan.tier == SubscriptionTier.BASIC
).first()
if not basic_plan:
logger.error("❌ Basic plan not found in database!")
return False
# Get all Basic plan users
user_subscriptions = db.query(UserSubscription).filter(
UserSubscription.plan_id == basic_plan.id,
UserSubscription.is_active == True
).all()
logger.info(f"Found {len(user_subscriptions)} Basic plan user(s)")
pricing_service = PricingService(db)
reset_count = 0
for sub in user_subscriptions:
try:
# Get current billing period for this user
current_period = pricing_service.get_current_billing_period(sub.user_id) or datetime.now(timezone.utc).strftime("%Y-%m")
# Find usage summary for current period
usage_summary = db.query(UsageSummary).filter(
UsageSummary.user_id == sub.user_id,
UsageSummary.billing_period == current_period
).first()
if usage_summary:
# Store old values for logging
old_gemini = usage_summary.gemini_calls or 0
old_mistral = usage_summary.mistral_calls or 0
old_tokens = (usage_summary.mistral_tokens or 0) + (usage_summary.gemini_tokens or 0)
old_images = usage_summary.stability_calls or 0
old_total_calls = usage_summary.total_calls or 0
old_total_tokens = usage_summary.total_tokens or 0
# Reset all LLM provider counters
usage_summary.gemini_calls = 0
usage_summary.openai_calls = 0
usage_summary.anthropic_calls = 0
usage_summary.mistral_calls = 0
# Reset all token counters
usage_summary.gemini_tokens = 0
usage_summary.openai_tokens = 0
usage_summary.anthropic_tokens = 0
usage_summary.mistral_tokens = 0
# Reset image counter
usage_summary.stability_calls = 0
# Reset totals
usage_summary.total_calls = 0
usage_summary.total_tokens = 0
usage_summary.total_cost = 0.0
# Reset status to active
usage_summary.usage_status = UsageStatus.ACTIVE
usage_summary.updated_at = datetime.now(timezone.utc)
db.commit()
reset_count += 1
logger.info(f"\n✅ Reset usage for user {sub.user_id} (period {current_period}):")
logger.info(f" Calls: {old_gemini + old_mistral} (gemini: {old_gemini}, mistral: {old_mistral}) → 0")
logger.info(f" Tokens: {old_tokens} → 0")
logger.info(f" Images: {old_images} → 0")
logger.info(f" Total Calls: {old_total_calls} → 0")
logger.info(f" Total Tokens: {old_total_tokens} → 0")
else:
logger.info(f" No usage summary found for user {sub.user_id} (period {current_period}) - nothing to reset")
except Exception as reset_error:
logger.error(f" ❌ Error resetting usage for user {sub.user_id}: {reset_error}")
import traceback
logger.error(traceback.format_exc())
db.rollback()
if reset_count > 0:
logger.info(f"\n✅ Successfully reset usage counters for {reset_count} user(s)")
else:
logger.info("\n No usage counters to reset")
logger.info("\n" + "="*60)
logger.info("RESET COMPLETE")
logger.info("="*60)
logger.info("\n💡 Usage counters have been reset. Users can now use their new limits.")
logger.info(" Next API call will start counting from 0.")
return True
except Exception as e:
db.rollback()
logger.error(f"❌ Error resetting usage: {e}")
import traceback
logger.error(traceback.format_exc())
raise
finally:
db.close()
except Exception as e:
logger.error(f"❌ Failed to connect to database: {e}")
import traceback
logger.error(traceback.format_exc())
return False
if __name__ == "__main__":
logger.info("🚀 Starting Basic plan usage counter reset...")
logger.info("="*60)
logger.info("This will reset all usage counters (calls, tokens, images) to 0")
logger.info("for all Basic plan users in their current billing period.")
logger.info("="*60)
try:
success = reset_basic_plan_usage()
if success:
logger.info("\n✅ Script completed successfully!")
sys.exit(0)
else:
logger.error("\n❌ Script failed!")
sys.exit(1)
except KeyboardInterrupt:
logger.info("\n⚠️ Script cancelled by user")
sys.exit(1)
except Exception as e:
logger.error(f"\n❌ Unexpected error: {e}")
sys.exit(1)

View File

@@ -0,0 +1,279 @@
"""
Script to update Basic plan subscription limits for testing rate limits and renewal flows.
Updates:
- LLM Calls (all providers): 10 calls (was 500-1000)
- LLM Tokens (all providers): 2000 tokens (was 200k-1M)
- Images: 5 images (was 50)
This script updates the SubscriptionPlan table, which automatically applies to all users
who have a Basic plan subscription via the plan_id foreign key.
"""
import sys
import os
from pathlib import Path
from datetime import datetime, timezone
# Add the backend directory to Python path
backend_dir = Path(__file__).parent.parent
sys.path.insert(0, str(backend_dir))
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from loguru import logger
from models.subscription_models import SubscriptionPlan, SubscriptionTier, UserSubscription, UsageStatus
from services.database import DATABASE_URL
def update_basic_plan_limits():
"""Update Basic plan limits for testing rate limits and renewal."""
try:
engine = create_engine(DATABASE_URL, echo=False)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
db = SessionLocal()
try:
# Find Basic plan
basic_plan = db.query(SubscriptionPlan).filter(
SubscriptionPlan.tier == SubscriptionTier.BASIC
).first()
if not basic_plan:
logger.error("❌ Basic plan not found in database!")
return False
# Store old values for logging
old_limits = {
'gemini_calls': basic_plan.gemini_calls_limit,
'mistral_calls': basic_plan.mistral_calls_limit,
'gemini_tokens': basic_plan.gemini_tokens_limit,
'mistral_tokens': basic_plan.mistral_tokens_limit,
'stability_calls': basic_plan.stability_calls_limit,
}
logger.info(f"📋 Current Basic plan limits:")
logger.info(f" Gemini Calls: {old_limits['gemini_calls']}")
logger.info(f" Mistral Calls: {old_limits['mistral_calls']}")
logger.info(f" Gemini Tokens: {old_limits['gemini_tokens']}")
logger.info(f" Mistral Tokens: {old_limits['mistral_tokens']}")
logger.info(f" Images (Stability): {old_limits['stability_calls']}")
# Update unified AI text generation limit to 10
basic_plan.ai_text_generation_calls_limit = 10
# Legacy per-provider limits (kept for backwards compatibility, but not used for enforcement)
basic_plan.gemini_calls_limit = 1000
basic_plan.openai_calls_limit = 500
basic_plan.anthropic_calls_limit = 200
basic_plan.mistral_calls_limit = 500
# Update all LLM provider token limits to 2000
basic_plan.gemini_tokens_limit = 2000
basic_plan.openai_tokens_limit = 2000
basic_plan.anthropic_tokens_limit = 2000
basic_plan.mistral_tokens_limit = 2000
# Update image generation limit to 5
basic_plan.stability_calls_limit = 5
# Update timestamp
basic_plan.updated_at = datetime.now(timezone.utc)
logger.info("\n📝 New Basic plan limits:")
logger.info(f" LLM Calls (all providers): 10")
logger.info(f" LLM Tokens (all providers): 2000")
logger.info(f" Images: 5")
# Count and get affected users
user_subscriptions = db.query(UserSubscription).filter(
UserSubscription.plan_id == basic_plan.id,
UserSubscription.is_active == True
).all()
affected_users = len(user_subscriptions)
logger.info(f"\n👥 Users affected: {affected_users}")
if affected_users > 0:
logger.info("\n📋 Affected user IDs:")
for sub in user_subscriptions:
logger.info(f" - {sub.user_id}")
else:
logger.info(" (No active Basic plan subscriptions found)")
# Commit plan limit changes first
db.commit()
logger.info("\n✅ Basic plan limits updated successfully!")
# Cap usage at new limits for all affected users (preserve historical data, but cap enforcement)
logger.info("\n🔄 Capping usage counters at new limits for Basic plan users...")
logger.info(" (Historical usage preserved, but capped to allow new calls within limits)")
from models.subscription_models import UsageSummary
from services.subscription import PricingService
pricing_service = PricingService(db)
capped_count = 0
# New limits - use unified AI text generation limit if available
new_call_limit = getattr(basic_plan, 'ai_text_generation_calls_limit', None) or basic_plan.gemini_calls_limit
new_token_limit = basic_plan.gemini_tokens_limit # 2000
new_image_limit = basic_plan.stability_calls_limit # 5
for sub in user_subscriptions:
try:
# Get current billing period for this user
current_period = pricing_service.get_current_billing_period(sub.user_id) or datetime.now(timezone.utc).strftime("%Y-%m")
# Find usage summary for current period
usage_summary = db.query(UsageSummary).filter(
UsageSummary.user_id == sub.user_id,
UsageSummary.billing_period == current_period
).first()
if usage_summary:
# Store old values for logging
old_gemini = usage_summary.gemini_calls or 0
old_mistral = usage_summary.mistral_calls or 0
old_openai = usage_summary.openai_calls or 0
old_anthropic = usage_summary.anthropic_calls or 0
old_tokens = max(
usage_summary.gemini_tokens or 0,
usage_summary.openai_tokens or 0,
usage_summary.anthropic_tokens or 0,
usage_summary.mistral_tokens or 0
)
old_images = usage_summary.stability_calls or 0
# Cap LLM provider counters at new limits (don't reset, just cap)
# This allows historical data to remain but prevents blocking from old usage
usage_summary.gemini_calls = min(old_gemini, new_call_limit)
usage_summary.mistral_calls = min(old_mistral, new_call_limit)
usage_summary.openai_calls = min(old_openai, new_call_limit)
usage_summary.anthropic_calls = min(old_anthropic, new_call_limit)
# Cap token counters at new limits
usage_summary.gemini_tokens = min(usage_summary.gemini_tokens or 0, new_token_limit)
usage_summary.openai_tokens = min(usage_summary.openai_tokens or 0, new_token_limit)
usage_summary.anthropic_tokens = min(usage_summary.anthropic_tokens or 0, new_token_limit)
usage_summary.mistral_tokens = min(usage_summary.mistral_tokens or 0, new_token_limit)
# Cap image counter at new limit
usage_summary.stability_calls = min(old_images, new_image_limit)
# Update totals based on capped values (approximate)
# Recalculate total_calls and total_tokens based on capped provider values
total_capped_calls = (
usage_summary.gemini_calls +
usage_summary.mistral_calls +
usage_summary.openai_calls +
usage_summary.anthropic_calls +
usage_summary.stability_calls
)
total_capped_tokens = (
usage_summary.gemini_tokens +
usage_summary.mistral_tokens +
usage_summary.openai_tokens +
usage_summary.anthropic_tokens
)
usage_summary.total_calls = total_capped_calls
usage_summary.total_tokens = total_capped_tokens
# Reset status to active to allow new calls
usage_summary.usage_status = UsageStatus.ACTIVE
usage_summary.updated_at = datetime.now(timezone.utc)
db.commit()
capped_count += 1
logger.info(f" ✅ Capped usage for user {sub.user_id}:")
logger.info(f" Gemini Calls: {old_gemini}{usage_summary.gemini_calls} (limit: {new_call_limit})")
logger.info(f" Mistral Calls: {old_mistral}{usage_summary.mistral_calls} (limit: {new_call_limit})")
logger.info(f" Tokens: {old_tokens}{max(usage_summary.gemini_tokens, usage_summary.mistral_tokens)} (limit: {new_token_limit})")
logger.info(f" Images: {old_images}{usage_summary.stability_calls} (limit: {new_image_limit})")
else:
logger.info(f" No usage summary found for user {sub.user_id} (period {current_period})")
except Exception as cap_error:
logger.error(f" ❌ Error capping usage for user {sub.user_id}: {cap_error}")
import traceback
logger.error(traceback.format_exc())
db.rollback()
if capped_count > 0:
logger.info(f"\n✅ Capped usage counters for {capped_count} user(s)")
logger.info(" Historical usage preserved, but capped at new limits to allow new calls")
else:
logger.info("\n No usage counters to cap")
# Note about cache clearing
logger.info("\n🔄 Cache Information:")
logger.info(" The subscription limits cache is per-instance and will refresh on next request.")
logger.info(" No manual cache clearing needed - limits will be read from database on next check.")
# Display final summary
logger.info("\n" + "="*60)
logger.info("BASIC PLAN UPDATE SUMMARY")
logger.info("="*60)
logger.info(f"\nPlan: {basic_plan.name} ({basic_plan.tier.value})")
logger.info(f"Price: ${basic_plan.price_monthly}/mo, ${basic_plan.price_yearly}/yr")
logger.info(f"\nUpdated Limits:")
logger.info(f" LLM Calls (gemini/openai/anthropic/mistral): {basic_plan.gemini_calls_limit}")
logger.info(f" LLM Tokens (gemini/openai/anthropic/mistral): {basic_plan.gemini_tokens_limit}")
logger.info(f" Images (stability): {basic_plan.stability_calls_limit}")
logger.info(f"\nUsers Affected: {affected_users}")
logger.info("\n" + "="*60)
logger.info("\n💡 Note: These limits apply immediately to all Basic plan users.")
logger.info(" Historical usage has been preserved but capped at new limits.")
logger.info(" Users can continue making new calls up to the new limits.")
logger.info(" Users will hit rate limits faster for testing purposes.")
return True
except Exception as e:
db.rollback()
logger.error(f"❌ Error updating Basic plan: {e}")
import traceback
logger.error(traceback.format_exc())
raise
finally:
db.close()
except Exception as e:
logger.error(f"❌ Failed to connect to database: {e}")
import traceback
logger.error(traceback.format_exc())
return False
if __name__ == "__main__":
logger.info("🚀 Starting Basic plan limits update...")
logger.info("="*60)
logger.info("This will update Basic plan limits for testing rate limits:")
logger.info(" - LLM Calls: 10 (all providers)")
logger.info(" - LLM Tokens: 2000 (all providers)")
logger.info(" - Images: 5")
logger.info("="*60)
# Ask for confirmation in non-interactive mode, proceed directly
# In interactive mode, you can add: input("\nPress Enter to continue or Ctrl+C to cancel...")
try:
success = update_basic_plan_limits()
if success:
logger.info("\n✅ Script completed successfully!")
sys.exit(0)
else:
logger.error("\n❌ Script failed!")
sys.exit(1)
except KeyboardInterrupt:
logger.info("\n⚠️ Script cancelled by user")
sys.exit(1)
except Exception as e:
logger.error(f"\n❌ Unexpected error: {e}")
sys.exit(1)