feat: image generation overhaul (model-aware text, dim clamping, \.30 pricing), event-driven dashboard cache invalidation, SEO insights (AI visibility, GSC, keyword gap), YouTube OAuth/publish, blog writer & content planning improvements, scheduler monitoring updates
This commit is contained in:
@@ -1,68 +1,19 @@
|
||||
"""
|
||||
Cache management for subscription API endpoints.
|
||||
|
||||
Delegates to the canonical implementation in services/subscription/cache.py.
|
||||
All cache state lives there so service-layer code can invalidate without
|
||||
importing from the API layer.
|
||||
"""
|
||||
|
||||
from typing import Dict, Any
|
||||
import time
|
||||
import os
|
||||
from services.subscription.cache import (
|
||||
get_cached_dashboard,
|
||||
set_cached_dashboard,
|
||||
clear_dashboard_cache,
|
||||
)
|
||||
|
||||
|
||||
# Simple in-process cache for dashboard responses to smooth bursts
|
||||
# Cache key: (user_id). TTL-like behavior implemented via timestamp check
|
||||
_dashboard_cache: Dict[str, Dict[str, Any]] = {}
|
||||
_dashboard_cache_ts: Dict[str, float] = {}
|
||||
_DASHBOARD_CACHE_TTL_SEC = 600.0
|
||||
|
||||
|
||||
def get_cached_dashboard(user_id: str) -> Dict[str, Any] | None:
|
||||
"""
|
||||
Get cached dashboard data if available and not expired.
|
||||
|
||||
Args:
|
||||
user_id: User ID to get cached data for
|
||||
|
||||
Returns:
|
||||
Cached dashboard data or None if not cached/expired
|
||||
"""
|
||||
# Check if caching is disabled via environment variable
|
||||
nocache = False
|
||||
try:
|
||||
nocache = os.getenv('SUBSCRIPTION_DASHBOARD_NOCACHE', 'false').lower() in {'1', 'true', 'yes', 'on'}
|
||||
except Exception:
|
||||
nocache = False
|
||||
|
||||
if nocache:
|
||||
return None
|
||||
|
||||
now = time.time()
|
||||
if user_id in _dashboard_cache and (now - _dashboard_cache_ts.get(user_id, 0)) < _DASHBOARD_CACHE_TTL_SEC:
|
||||
return _dashboard_cache[user_id]
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def set_cached_dashboard(user_id: str, data: Dict[str, Any]) -> None:
|
||||
"""
|
||||
Cache dashboard data for a user.
|
||||
|
||||
Args:
|
||||
user_id: User ID to cache data for
|
||||
data: Dashboard data to cache
|
||||
"""
|
||||
_dashboard_cache[user_id] = data
|
||||
_dashboard_cache_ts[user_id] = time.time()
|
||||
|
||||
|
||||
def clear_dashboard_cache(user_id: str | None = None) -> None:
|
||||
"""
|
||||
Clear dashboard cache for a specific user or all users.
|
||||
|
||||
Args:
|
||||
user_id: User ID to clear cache for, or None to clear all
|
||||
"""
|
||||
if user_id:
|
||||
_dashboard_cache.pop(user_id, None)
|
||||
_dashboard_cache_ts.pop(user_id, None)
|
||||
else:
|
||||
_dashboard_cache.clear()
|
||||
_dashboard_cache_ts.clear()
|
||||
__all__ = [
|
||||
"get_cached_dashboard",
|
||||
"set_cached_dashboard",
|
||||
"clear_dashboard_cache",
|
||||
]
|
||||
|
||||
@@ -109,48 +109,49 @@ async def preflight_check(
|
||||
|
||||
# Get pricing for this operation
|
||||
model_name = op.get('model')
|
||||
pricing_info = None
|
||||
if model_name:
|
||||
pricing_info = pricing_service.get_pricing_for_provider_model(
|
||||
op['provider'],
|
||||
model_name
|
||||
)
|
||||
|
||||
if pricing_info:
|
||||
# Determine cost based on operation type
|
||||
if op['provider'] in [APIProvider.VIDEO, APIProvider.IMAGE_EDIT, APIProvider.STABILITY]:
|
||||
cost = pricing_info.get('cost_per_request', 0.0) or pricing_info.get('cost_per_image', 0.0) or 0.0
|
||||
elif op['provider'] == APIProvider.AUDIO:
|
||||
model_lower = (model_name or "").lower()
|
||||
if model_lower == "minimax/voice-clone":
|
||||
cost = pricing_info.get('cost_per_request', 0.5) or 0.5
|
||||
elif model_lower == "wavespeed-ai/qwen3-tts/voice-clone":
|
||||
chars = max(0, int(op.get('tokens_requested') or 0))
|
||||
cost = max(0.005, 0.005 * (chars / 100.0))
|
||||
else:
|
||||
cost = (pricing_info.get('cost_per_input_token', 0.0) or 0.0) * op['tokens_requested']
|
||||
elif op['tokens_requested'] > 0:
|
||||
cost = (pricing_info.get('cost_per_input_token', 0.0) or 0.0) * op['tokens_requested']
|
||||
|
||||
if pricing_info:
|
||||
# Determine cost based on operation type
|
||||
if op['provider'] in [APIProvider.VIDEO, APIProvider.IMAGE_EDIT, APIProvider.STABILITY]:
|
||||
cost = pricing_info.get('cost_per_request', 0.0) or pricing_info.get('cost_per_image', 0.0) or 0.0
|
||||
elif op['provider'] == APIProvider.AUDIO:
|
||||
model_lower = (model_name or "").lower()
|
||||
if model_lower == "minimax/voice-clone":
|
||||
cost = pricing_info.get('cost_per_request', 0.5) or 0.5
|
||||
elif model_lower == "wavespeed-ai/qwen3-tts/voice-clone":
|
||||
chars = max(0, int(op.get('tokens_requested') or 0))
|
||||
cost = max(0.005, 0.005 * (chars / 100.0))
|
||||
else:
|
||||
cost = pricing_info.get('cost_per_request', 0.0) or 0.0
|
||||
|
||||
cost = (pricing_info.get('cost_per_input_token', 0.0) or 0.0) * op['tokens_requested']
|
||||
elif op['tokens_requested'] > 0:
|
||||
cost = (pricing_info.get('cost_per_input_token', 0.0) or 0.0) * op['tokens_requested']
|
||||
else:
|
||||
cost = pricing_info.get('cost_per_request', 0.0) or 0.0
|
||||
|
||||
op_result['cost'] = round(cost, 4)
|
||||
total_cost += cost
|
||||
else:
|
||||
# Use default cost if pricing not found or no model specified
|
||||
if op['provider'] == APIProvider.VIDEO:
|
||||
op_result['cost'] = 0.10 # Default video cost
|
||||
total_cost += 0.10
|
||||
elif op['provider'] == APIProvider.IMAGE_EDIT:
|
||||
op_result['cost'] = 0.05 # Default image edit cost
|
||||
total_cost += 0.05
|
||||
elif op['provider'] == APIProvider.STABILITY:
|
||||
op_result['cost'] = 0.04 # Default image generation cost
|
||||
total_cost += 0.04
|
||||
elif op['provider'] == APIProvider.AUDIO:
|
||||
# Default audio cost: $0.05 per 1,000 characters
|
||||
cost = (op['tokens_requested'] / 1000.0) * 0.05
|
||||
op_result['cost'] = round(cost, 4)
|
||||
total_cost += cost
|
||||
else:
|
||||
# Use default cost if pricing not found
|
||||
if op['provider'] == APIProvider.VIDEO:
|
||||
op_result['cost'] = 0.10 # Default video cost
|
||||
total_cost += 0.10
|
||||
elif op['provider'] == APIProvider.IMAGE_EDIT:
|
||||
op_result['cost'] = 0.05 # Default image edit cost
|
||||
total_cost += 0.05
|
||||
elif op['provider'] == APIProvider.STABILITY:
|
||||
op_result['cost'] = 0.04 # Default image generation cost
|
||||
total_cost += 0.04
|
||||
elif op['provider'] == APIProvider.AUDIO:
|
||||
# Default audio cost: $0.05 per 1,000 characters
|
||||
cost = (op['tokens_requested'] / 1000.0) * 0.05
|
||||
op_result['cost'] = round(cost, 4)
|
||||
total_cost += cost
|
||||
|
||||
# Get limit information
|
||||
limit_info = None
|
||||
|
||||
Reference in New Issue
Block a user