Refactor Gemini rate limiting configuration
- Removed billingEnabled setting and replaced it with rateLimitingEnabled in GeminiAgent. - Updated enforceRateLimitForModel function to skip rate limiting based on rateLimitingEnabled. - Adjusted getGeminiConfig to retrieve rateLimitingEnabled from settings. - Changed settings management to reflect the new rate limiting logic in SettingsDefaultsManager and UI components. - Updated ContextSettingsModal to toggle rate limiting instead of billing. - Ensured default settings reflect the new rate limiting behavior for free tier users.
This commit is contained in:
@@ -18,8 +18,7 @@ import { logger } from '../../utils/logger.js';
|
||||
import { parseObservations, parseSummary } from '../../sdk/parser.js';
|
||||
import { buildInitPrompt, buildObservationPrompt, buildSummaryPrompt, buildContinuationPrompt } from '../../sdk/prompts.js';
|
||||
import { SettingsDefaultsManager } from '../../shared/SettingsDefaultsManager.js';
|
||||
import { USER_SETTINGS_PATH } from '../../shared/paths.js';
|
||||
import type { ActiveSession, PendingMessage, ConversationMessage } from '../worker-types.js';
|
||||
import type { ActiveSession, ConversationMessage } from '../worker-types.js';
|
||||
import { ModeManager } from '../domain/ModeManager.js';
|
||||
|
||||
// Gemini API endpoint
|
||||
@@ -46,13 +45,13 @@ const GEMINI_RPM_LIMITS: Record<GeminiModel, number> = {
|
||||
let lastRequestTime = 0;
|
||||
|
||||
/**
|
||||
* Enforce RPM rate limit for Gemini free tier (no billing).
|
||||
* Enforce RPM rate limit for Gemini free tier.
|
||||
* Waits the required time between requests based on model's RPM limit + 100ms safety buffer.
|
||||
* Skipped entirely if billing is enabled (1000+ RPM available).
|
||||
* Skipped entirely if rate limiting is disabled (billing users with 1000+ RPM available).
|
||||
*/
|
||||
async function enforceRateLimitForModel(model: GeminiModel, billingEnabled: boolean): Promise<void> {
|
||||
// Skip rate limiting if billing is enabled (1000+ RPM available)
|
||||
if (billingEnabled) {
|
||||
async function enforceRateLimitForModel(model: GeminiModel, rateLimitingEnabled: boolean): Promise<void> {
|
||||
// Skip rate limiting if disabled (billing users with 1000+ RPM)
|
||||
if (!rateLimitingEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -142,7 +141,7 @@ export class GeminiAgent {
|
||||
async startSession(session: ActiveSession, worker?: any): Promise<void> {
|
||||
try {
|
||||
// Get Gemini configuration
|
||||
const { apiKey, model, billingEnabled } = this.getGeminiConfig();
|
||||
const { apiKey, model, rateLimitingEnabled } = this.getGeminiConfig();
|
||||
|
||||
if (!apiKey) {
|
||||
throw new Error('Gemini API key not configured. Set CLAUDE_MEM_GEMINI_API_KEY in settings or GEMINI_API_KEY environment variable.');
|
||||
@@ -158,7 +157,7 @@ export class GeminiAgent {
|
||||
|
||||
// Add to conversation history and query Gemini with full context
|
||||
session.conversationHistory.push({ role: 'user', content: initPrompt });
|
||||
const initResponse = await this.queryGeminiMultiTurn(session.conversationHistory, apiKey, model, billingEnabled);
|
||||
const initResponse = await this.queryGeminiMultiTurn(session.conversationHistory, apiKey, model, rateLimitingEnabled);
|
||||
|
||||
if (initResponse.content) {
|
||||
// Add response to conversation history
|
||||
@@ -193,7 +192,7 @@ export class GeminiAgent {
|
||||
|
||||
// Add to conversation history and query Gemini with full context
|
||||
session.conversationHistory.push({ role: 'user', content: obsPrompt });
|
||||
const obsResponse = await this.queryGeminiMultiTurn(session.conversationHistory, apiKey, model, billingEnabled);
|
||||
const obsResponse = await this.queryGeminiMultiTurn(session.conversationHistory, apiKey, model, rateLimitingEnabled);
|
||||
|
||||
if (obsResponse.content) {
|
||||
// Add response to conversation history
|
||||
@@ -225,7 +224,7 @@ export class GeminiAgent {
|
||||
|
||||
// Add to conversation history and query Gemini with full context
|
||||
session.conversationHistory.push({ role: 'user', content: summaryPrompt });
|
||||
const summaryResponse = await this.queryGeminiMultiTurn(session.conversationHistory, apiKey, model, billingEnabled);
|
||||
const summaryResponse = await this.queryGeminiMultiTurn(session.conversationHistory, apiKey, model, rateLimitingEnabled);
|
||||
|
||||
if (summaryResponse.content) {
|
||||
// Add response to conversation history
|
||||
@@ -307,7 +306,7 @@ export class GeminiAgent {
|
||||
history: ConversationMessage[],
|
||||
apiKey: string,
|
||||
model: GeminiModel,
|
||||
billingEnabled: boolean
|
||||
rateLimitingEnabled: boolean
|
||||
): Promise<{ content: string; tokensUsed?: number }> {
|
||||
const contents = this.conversationToGeminiContents(history);
|
||||
const totalChars = history.reduce((sum, m) => sum + m.content.length, 0);
|
||||
@@ -319,8 +318,8 @@ export class GeminiAgent {
|
||||
|
||||
const url = `${GEMINI_API_URL}/${model}:generateContent?key=${apiKey}`;
|
||||
|
||||
// Enforce RPM rate limit for free tier (skipped if billing enabled)
|
||||
await enforceRateLimitForModel(model, billingEnabled);
|
||||
// Enforce RPM rate limit for free tier (skipped if rate limiting disabled)
|
||||
await enforceRateLimitForModel(model, rateLimitingEnabled);
|
||||
|
||||
const response = await fetch(url, {
|
||||
method: 'POST',
|
||||
@@ -516,7 +515,7 @@ export class GeminiAgent {
|
||||
/**
|
||||
* Get Gemini configuration from settings or environment
|
||||
*/
|
||||
private getGeminiConfig(): { apiKey: string; model: GeminiModel; billingEnabled: boolean } {
|
||||
private getGeminiConfig(): { apiKey: string; model: GeminiModel; rateLimitingEnabled: boolean } {
|
||||
const settingsPath = path.join(homedir(), '.claude-mem', 'settings.json');
|
||||
const settings = SettingsDefaultsManager.loadFromFile(settingsPath);
|
||||
|
||||
@@ -545,10 +544,10 @@ export class GeminiAgent {
|
||||
model = defaultModel;
|
||||
}
|
||||
|
||||
// Billing: if enabled, skip rate limiting (1000+ RPM available)
|
||||
const billingEnabled = settings.CLAUDE_MEM_GEMINI_BILLING_ENABLED === 'true';
|
||||
// Rate limiting: enabled by default for free tier users
|
||||
const rateLimitingEnabled = settings.CLAUDE_MEM_GEMINI_RATE_LIMITING_ENABLED !== 'false';
|
||||
|
||||
return { apiKey, model, billingEnabled };
|
||||
return { apiKey, model, rateLimitingEnabled };
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ export interface SettingsDefaults {
|
||||
CLAUDE_MEM_PROVIDER: string; // 'claude' | 'gemini'
|
||||
CLAUDE_MEM_GEMINI_API_KEY: string;
|
||||
CLAUDE_MEM_GEMINI_MODEL: string; // 'gemini-2.5-flash-lite' | 'gemini-2.5-flash' | 'gemini-3-flash'
|
||||
CLAUDE_MEM_GEMINI_BILLING_ENABLED: string; // 'true' | 'false' - skip rate limiting if billing enabled
|
||||
CLAUDE_MEM_GEMINI_RATE_LIMITING_ENABLED: string; // 'true' | 'false' - enable rate limiting for free tier
|
||||
// System Configuration
|
||||
CLAUDE_MEM_DATA_DIR: string;
|
||||
CLAUDE_MEM_LOG_LEVEL: string;
|
||||
@@ -59,7 +59,7 @@ export class SettingsDefaultsManager {
|
||||
CLAUDE_MEM_PROVIDER: 'claude', // Default to Claude
|
||||
CLAUDE_MEM_GEMINI_API_KEY: '', // Empty by default, can be set via UI or env
|
||||
CLAUDE_MEM_GEMINI_MODEL: 'gemini-2.5-flash-lite', // Default Gemini model (highest free tier RPM)
|
||||
CLAUDE_MEM_GEMINI_BILLING_ENABLED: 'false', // Rate limiting enabled by default for no-billing users
|
||||
CLAUDE_MEM_GEMINI_RATE_LIMITING_ENABLED: 'true', // Rate limiting ON by default for free tier users
|
||||
// System Configuration
|
||||
CLAUDE_MEM_DATA_DIR: join(homedir(), '.claude-mem'),
|
||||
CLAUDE_MEM_LOG_LEVEL: 'INFO',
|
||||
|
||||
@@ -481,11 +481,11 @@ export function ContextSettingsModal({
|
||||
</FormField>
|
||||
<div className="toggle-group" style={{ marginTop: '8px' }}>
|
||||
<ToggleSwitch
|
||||
id="gemini-billing-enabled"
|
||||
label="Billing Enabled"
|
||||
description="Enable if you have billing set up on Google Cloud. Skips rate limiting (1000+ RPM available)."
|
||||
checked={formState.CLAUDE_MEM_GEMINI_BILLING_ENABLED === 'true'}
|
||||
onChange={(checked) => updateSetting('CLAUDE_MEM_GEMINI_BILLING_ENABLED', checked ? 'true' : 'false')}
|
||||
id="gemini-rate-limiting"
|
||||
label="Rate Limiting"
|
||||
description="Enable for free tier (10-30 RPM). Disable if you have billing set up (1000+ RPM)."
|
||||
checked={formState.CLAUDE_MEM_GEMINI_RATE_LIMITING_ENABLED === 'true'}
|
||||
onChange={(checked) => updateSetting('CLAUDE_MEM_GEMINI_RATE_LIMITING_ENABLED', checked ? 'true' : 'false')}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
|
||||
@@ -12,7 +12,7 @@ export const DEFAULT_SETTINGS = {
|
||||
CLAUDE_MEM_PROVIDER: 'claude',
|
||||
CLAUDE_MEM_GEMINI_API_KEY: '',
|
||||
CLAUDE_MEM_GEMINI_MODEL: 'gemini-2.5-flash-lite',
|
||||
CLAUDE_MEM_GEMINI_BILLING_ENABLED: 'false',
|
||||
CLAUDE_MEM_GEMINI_RATE_LIMITING_ENABLED: 'true',
|
||||
|
||||
// Token Economics (all true for backwards compatibility)
|
||||
CLAUDE_MEM_CONTEXT_SHOW_READ_TOKENS: 'true',
|
||||
|
||||
@@ -64,6 +64,7 @@ export interface Settings {
|
||||
CLAUDE_MEM_PROVIDER?: string; // 'claude' | 'gemini'
|
||||
CLAUDE_MEM_GEMINI_API_KEY?: string;
|
||||
CLAUDE_MEM_GEMINI_MODEL?: string; // 'gemini-2.5-flash-lite' | 'gemini-2.5-flash' | 'gemini-3-flash'
|
||||
CLAUDE_MEM_GEMINI_RATE_LIMITING_ENABLED?: string; // 'true' | 'false'
|
||||
|
||||
// Token Economics Display
|
||||
CLAUDE_MEM_CONTEXT_SHOW_READ_TOKENS?: string;
|
||||
|
||||
Reference in New Issue
Block a user