feat(chat): Dwa klucze API - Free tier dla Flash, Paid dla Pro
- GOOGLE_GEMINI_API_KEY_FREE: klucz Free tier dla Flash (darmowy) - GOOGLE_GEMINI_API_KEY: klucz Paid tier dla Pro (płatny) - GeminiService automatycznie wybiera klucz na podstawie modelu - Flash pricing ustawiony na $0.00 (Free tier) - UI pokazuje Flash jako "Darmowy" Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
26db9a7cc9
commit
1b2ba66ead
46
app.py
46
app.py
@ -4623,6 +4623,52 @@ def chat_send_message(conversation_id):
|
|||||||
# Get model from request or session (flash = default, pro = premium)
|
# Get model from request or session (flash = default, pro = premium)
|
||||||
model_choice = data.get('model') or session.get('chat_model', 'flash')
|
model_choice = data.get('model') or session.get('chat_model', 'flash')
|
||||||
|
|
||||||
|
# Check Pro model limits (Flash is free - no limits)
|
||||||
|
if model_choice == 'pro':
|
||||||
|
# Users without limits (admins)
|
||||||
|
UNLIMITED_USERS = ['maciej.pienczyn@inpi.pl', 'artur.wiertel@waterm.pl']
|
||||||
|
|
||||||
|
if current_user.email not in UNLIMITED_USERS:
|
||||||
|
# Check daily and monthly limits for Pro
|
||||||
|
from database import AIApiCost
|
||||||
|
db_check = SessionLocal()
|
||||||
|
try:
|
||||||
|
# Daily limit: $2.00
|
||||||
|
today_start = datetime.now().replace(hour=0, minute=0, second=0, microsecond=0)
|
||||||
|
daily_costs = db_check.query(AIApiCost).filter(
|
||||||
|
AIApiCost.user_id == current_user.id,
|
||||||
|
AIApiCost.timestamp >= today_start,
|
||||||
|
AIApiCost.model_name.like('%pro%')
|
||||||
|
).all()
|
||||||
|
daily_total = sum(c.total_cost_usd or 0 for c in daily_costs)
|
||||||
|
|
||||||
|
if daily_total >= 2.0:
|
||||||
|
return jsonify({
|
||||||
|
'success': False,
|
||||||
|
'error': 'Osiągnięto dzienny limit Pro ($2.00). Spróbuj jutro lub użyj darmowego modelu Flash.',
|
||||||
|
'limit_exceeded': 'daily',
|
||||||
|
'daily_used': round(daily_total, 2)
|
||||||
|
}), 429
|
||||||
|
|
||||||
|
# Monthly limit: $20.00
|
||||||
|
month_start = datetime.now().replace(day=1, hour=0, minute=0, second=0, microsecond=0)
|
||||||
|
monthly_costs = db_check.query(AIApiCost).filter(
|
||||||
|
AIApiCost.user_id == current_user.id,
|
||||||
|
AIApiCost.timestamp >= month_start,
|
||||||
|
AIApiCost.model_name.like('%pro%')
|
||||||
|
).all()
|
||||||
|
monthly_total = sum(c.total_cost_usd or 0 for c in monthly_costs)
|
||||||
|
|
||||||
|
if monthly_total >= 20.0:
|
||||||
|
return jsonify({
|
||||||
|
'success': False,
|
||||||
|
'error': 'Osiągnięto miesięczny limit Pro ($20.00). Użyj darmowego modelu Flash.',
|
||||||
|
'limit_exceeded': 'monthly',
|
||||||
|
'monthly_used': round(monthly_total, 2)
|
||||||
|
}), 429
|
||||||
|
finally:
|
||||||
|
db_check.close()
|
||||||
|
|
||||||
# Map model choice to actual model name
|
# Map model choice to actual model name
|
||||||
model_map = {
|
model_map = {
|
||||||
'flash': '3-flash',
|
'flash': '3-flash',
|
||||||
|
|||||||
@ -58,13 +58,14 @@ THINKING_LEVELS = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
# Pricing per 1M tokens (USD) - updated 2026-01-29
|
# Pricing per 1M tokens (USD) - updated 2026-01-29
|
||||||
|
# Note: Flash on Free Tier = $0.00, Pro on Paid Tier = paid pricing
|
||||||
GEMINI_PRICING = {
|
GEMINI_PRICING = {
|
||||||
'gemini-2.5-flash': {'input': 0.30, 'output': 2.50, 'thinking': 0},
|
'gemini-2.5-flash': {'input': 0.30, 'output': 2.50, 'thinking': 0},
|
||||||
'gemini-2.5-flash-lite': {'input': 0.10, 'output': 0.40, 'thinking': 0},
|
'gemini-2.5-flash-lite': {'input': 0.10, 'output': 0.40, 'thinking': 0},
|
||||||
'gemini-2.5-pro': {'input': 1.25, 'output': 10.00, 'thinking': 0},
|
'gemini-2.5-pro': {'input': 1.25, 'output': 10.00, 'thinking': 0},
|
||||||
'gemini-2.0-flash': {'input': 0.10, 'output': 0.40, 'thinking': 0},
|
'gemini-2.0-flash': {'input': 0.10, 'output': 0.40, 'thinking': 0},
|
||||||
'gemini-3-flash-preview': {'input': 0.50, 'output': 3.00, 'thinking': 1.00},
|
'gemini-3-flash-preview': {'input': 0.00, 'output': 0.00, 'thinking': 0.00}, # Free tier!
|
||||||
'gemini-3-pro-preview': {'input': 2.00, 'output': 12.00, 'thinking': 4.00},
|
'gemini-3-pro-preview': {'input': 2.00, 'output': 12.00, 'thinking': 4.00}, # Paid tier
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -82,12 +83,24 @@ class GeminiService:
|
|||||||
Initialize Gemini service.
|
Initialize Gemini service.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
api_key: Google AI API key (reads from GOOGLE_GEMINI_API_KEY env if not provided)
|
api_key: Google AI API key (reads from env if not provided)
|
||||||
model: Model to use ('flash', 'flash-lite', 'pro', '3-flash', '3-pro')
|
model: Model to use ('flash', 'flash-lite', 'pro', '3-flash', '3-pro')
|
||||||
thinking_level: Reasoning depth ('minimal', 'low', 'medium', 'high')
|
thinking_level: Reasoning depth ('minimal', 'low', 'medium', 'high')
|
||||||
include_thoughts: Whether to include thinking process in response (for debugging)
|
include_thoughts: Whether to include thinking process in response (for debugging)
|
||||||
|
|
||||||
|
API Keys (auto-selected by model):
|
||||||
|
- GOOGLE_GEMINI_API_KEY_FREE: Free tier for Flash models (no cost)
|
||||||
|
- GOOGLE_GEMINI_API_KEY: Paid tier for Pro models
|
||||||
"""
|
"""
|
||||||
self.api_key = api_key or os.getenv('GOOGLE_GEMINI_API_KEY')
|
# Auto-select API key based on model (Free tier for Flash, Paid for Pro)
|
||||||
|
if api_key:
|
||||||
|
self.api_key = api_key
|
||||||
|
elif model in ('3-pro', 'pro'):
|
||||||
|
# Pro models use paid tier
|
||||||
|
self.api_key = os.getenv('GOOGLE_GEMINI_API_KEY')
|
||||||
|
else:
|
||||||
|
# Flash models prefer free tier, fallback to paid
|
||||||
|
self.api_key = os.getenv('GOOGLE_GEMINI_API_KEY_FREE') or os.getenv('GOOGLE_GEMINI_API_KEY')
|
||||||
|
|
||||||
# Debug: Log API key (masked)
|
# Debug: Log API key (masked)
|
||||||
if self.api_key:
|
if self.api_key:
|
||||||
|
|||||||
@ -733,6 +733,16 @@
|
|||||||
background: linear-gradient(135deg, #f59e0b 0%, #d97706 100%);
|
background: linear-gradient(135deg, #f59e0b 0%, #d97706 100%);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.thinking-option-badge.free {
|
||||||
|
background: linear-gradient(135deg, #10b981 0%, #059669 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.thinking-option-price.free {
|
||||||
|
color: #10b981;
|
||||||
|
font-weight: 500;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
|
||||||
.thinking-option-price {
|
.thinking-option-price {
|
||||||
display: block;
|
display: block;
|
||||||
font-size: 11px;
|
font-size: 11px;
|
||||||
@ -1247,10 +1257,10 @@
|
|||||||
<div class="thinking-option-header">
|
<div class="thinking-option-header">
|
||||||
<span class="thinking-option-icon">⚡</span>
|
<span class="thinking-option-icon">⚡</span>
|
||||||
<span class="thinking-option-name">Gemini Flash</span>
|
<span class="thinking-option-name">Gemini Flash</span>
|
||||||
<span class="thinking-option-badge">Domyślny</span>
|
<span class="thinking-option-badge free">Darmowy</span>
|
||||||
</div>
|
</div>
|
||||||
<p class="thinking-option-desc">Szybki i ekonomiczny. Dla prostych pytań o firmy, kontakty, wydarzenia.</p>
|
<p class="thinking-option-desc">Szybki i skuteczny. Dla większości pytań o firmy, kontakty, wydarzenia.</p>
|
||||||
<span class="thinking-option-price">~$0.05/pytanie</span>
|
<span class="thinking-option-price free">Bez opłat</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="thinking-option" data-model="pro" onclick="setModel('pro')">
|
<div class="thinking-option" data-model="pro" onclick="setModel('pro')">
|
||||||
<div class="thinking-option-header">
|
<div class="thinking-option-header">
|
||||||
@ -1258,8 +1268,8 @@
|
|||||||
<span class="thinking-option-name">Gemini Pro</span>
|
<span class="thinking-option-name">Gemini Pro</span>
|
||||||
<span class="thinking-option-badge premium">Premium</span>
|
<span class="thinking-option-badge premium">Premium</span>
|
||||||
</div>
|
</div>
|
||||||
<p class="thinking-option-desc">Głęboka analiza. Dla złożonych pytań, strategii, rekomendacji.</p>
|
<p class="thinking-option-desc">Głęboka analiza i rozumowanie. Dla złożonych pytań, strategii, rekomendacji.</p>
|
||||||
<span class="thinking-option-price">~$0.20/pytanie</span>
|
<span class="thinking-option-price">~$0.20/pytanie · limit: $2/dzień</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user