Skip to content
9 changes: 9 additions & 0 deletions .claude/settings.local.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"permissions": {
"allow": [
"Bash(cat:*)"
],
"deny": [],
"ask": []
}
}
20 changes: 20 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
"setup-admin": "tsx scripts/setup-admin.ts"
},
"dependencies": {
"@google/generative-ai": "^0.24.1",
"@pinecone-database/pinecone": "^6.1.2",
"@react-three/drei": "^10.7.6",
"@react-three/fiber": "^9.3.0",
"@types/three": "^0.180.0",
Expand Down
52 changes: 52 additions & 0 deletions scripts/check-api-keys.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/**
* Check API Keys Configuration
* Verifies that Gemini and Pinecone API keys are properly loaded
*/

console.log('🔑 Checking API Keys Configuration\n');
console.log('='.repeat(60));

// Check Gemini API Key
const geminiKey = process.env.GOOGLE_GEMINI_API_KEY;
console.log('\n📊 Gemini API Key:');
if (geminiKey) {
console.log(`✅ Configured (${geminiKey.substring(0, 20)}...)`);
} else {
console.log('❌ NOT configured');
console.log('⚠️ Set GOOGLE_GEMINI_API_KEY in .env.local');
}

// Check Pinecone API Key
const pineconeKey = process.env.PINECONE_API_KEY;
console.log('\n📊 Pinecone API Key:');
if (pineconeKey) {
console.log(`✅ Configured (${pineconeKey.substring(0, 20)}...)`);
} else {
console.log('❌ NOT configured');
console.log('⚠️ Set PINECONE_API_KEY in .env.local');
}

// Check MongoDB URI
const mongoUri = process.env.MONGODB_URI;
console.log('\n📊 MongoDB URI:');
if (mongoUri) {
console.log('✅ Configured');
} else {
console.log('❌ NOT configured');
}

console.log('\n' + '='.repeat(60));

if (geminiKey && pineconeKey) {
console.log('\n✅ All API keys are configured!');
console.log('\n📝 Next steps:');
console.log('1. If you just added/changed keys, restart your Next.js dev server');
console.log('2. Run: npm run dev');
console.log('3. Test the chat assistant in your browser');
} else {
console.log('\n❌ Some API keys are missing!');
console.log('\n📝 Fix:');
console.log('1. Check that .env.local exists in project root');
console.log('2. Add missing keys to .env.local');
console.log('3. Restart Next.js dev server: npm run dev');
}
156 changes: 156 additions & 0 deletions scripts/check-api-rate-limit.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
/**
* Check Gemini API Rate Limit Status
* This script tests your API key and shows current limits
*/

import dotenv from 'dotenv';
import path from 'path';

// Load environment variables
dotenv.config({ path: path.join(process.cwd(), '.env.local') });
dotenv.config({ path: path.join(process.cwd(), '.env') });

import { GoogleGenerativeAI } from '@google/generative-ai';

async function checkRateLimit() {
console.log('🔍 Checking Gemini API Rate Limit Status...\n');
console.log('=' .repeat(70));

// Check if API key exists
const apiKey = process.env.GOOGLE_GEMINI_API_KEY;
if (!apiKey) {
console.log('❌ ERROR: GOOGLE_GEMINI_API_KEY not found in .env.local');
console.log(' Please add your API key to .env.local');
return;
}

console.log('✅ API Key found:', apiKey.substring(0, 10) + '...' + apiKey.substring(apiKey.length - 4));
console.log('');

// Test different models
const models = [
'gemini-2.0-flash-exp', // Current model (10 RPM)
'gemini-1.5-flash', // Alternative (15 RPM)
'gemini-1.5-pro' // Pro version (2 RPM)
];

const genAI = new GoogleGenerativeAI(apiKey);

console.log('📊 Testing API access with different models:\n');

for (const modelName of models) {
try {
console.log(`Testing: ${modelName}...`);

const model = genAI.getGenerativeModel({ model: modelName });
const startTime = Date.now();

const result = await model.generateContent('Say "Hello" in one word');
const response = result.response.text();

const endTime = Date.now();
const duration = endTime - startTime;

console.log(` ✅ SUCCESS`);
console.log(` Response: "${response.trim()}"`);
console.log(` Latency: ${duration}ms`);
console.log('');

} catch (error) {
const err = error as { status?: number; statusText?: string; message?: string; errorDetails?: Array<{ '@type': string; violations?: Array<{ quotaMetric: string; quotaValue: string }>; retryDelay?: string }> };

if (err.status === 429) {
console.log(` ❌ RATE LIMIT EXCEEDED`);
console.log(` Status: ${err.status} ${err.statusText}`);

// Extract rate limit info from error
if (err.errorDetails) {
const quotaFailure = err.errorDetails.find((d) =>
d['@type'] === 'type.googleapis.com/google.rpc.QuotaFailure'
);

if (quotaFailure?.violations) {
quotaFailure.violations.forEach((v) => {
console.log(` Quota Metric: ${v.quotaMetric}`);
console.log(` Quota Value: ${v.quotaValue} requests per minute`);
});
}

const retryInfo = err.errorDetails.find((d) =>
d['@type'] === 'type.googleapis.com/google.rpc.RetryInfo'
);

if (retryInfo?.retryDelay) {
console.log(` Retry After: ${retryInfo.retryDelay}`);
}
}
console.log('');
} else if (err.status === 404) {
console.log(` ⚠️ MODEL NOT AVAILABLE (may require different tier)`);
console.log('');
} else {
console.log(` ❌ ERROR: ${err.message}`);
console.log('');
}
}

// Wait between tests to avoid rate limit
await new Promise(resolve => setTimeout(resolve, 1000));
}

console.log('=' .repeat(70));
console.log('\n📋 Rate Limit Information:\n');

console.log('Free Tier Limits (per minute):');
console.log(' • gemini-2.0-flash-exp: 10 RPM (current model)');
console.log(' • gemini-1.5-flash: 15 RPM (recommended alternative)');
console.log(' • gemini-1.5-pro: 2 RPM (more powerful but slower)');
console.log('');

console.log('💡 Tips:');
console.log(' 1. For testing: Add 6-second delays between requests');
console.log(' 2. For production: Current limits are fine (users space out questions)');
console.log(' 3. If you hit limits: System falls back to keyword search automatically');
console.log('');

// Test rapid requests to see limit
console.log('🧪 Testing rapid requests to find your actual limit...\n');
console.log('Sending 5 quick requests in succession...');

const testModel = genAI.getGenerativeModel({ model: 'gemini-2.0-flash-exp' });
let successCount = 0;
let failCount = 0;

for (let i = 1; i <= 5; i++) {
try {
const result = await testModel.generateContent(`Count: ${i}`);
console.log(` ${i}. ✅ Success - ${result.response.text().trim()}`);
successCount++;
await new Promise(resolve => setTimeout(resolve, 500));
} catch (error) {
const err = error as { status?: number };
if (err.status === 429) {
console.log(` ${i}. ❌ Rate Limited - Hit quota at request #${i}`);
failCount++;
break;
}
}
}

console.log('');
console.log('📊 Rapid Test Results:');
console.log(` Successful: ${successCount}/5`);
console.log(` Rate Limited: ${failCount > 0 ? 'Yes' : 'No'}`);

if (failCount > 0) {
console.log(` \n ⚠️ You're currently at or near your rate limit.`);
console.log(` Wait ~60 seconds before running more tests.`);
} else {
console.log(` \n ✅ You have quota available for more requests.`);
}

console.log('\n' + '=' .repeat(70));
console.log('\n✅ Rate limit check complete!');
}

checkRateLimit().catch(console.error);
11 changes: 11 additions & 0 deletions scripts/debug-joke.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { isSpamOrOffTopic } from '../src/lib/geminiService';

const message = 'tell me a joke';
const result = isSpamOrOffTopic(message);

console.log(`Message: "${message}"`);
console.log(`Is spam/off-topic: ${result}`);
console.log(`Expected: true`);

// Check if 'joke' is in the message
console.log(`\nContains 'joke': ${message.toLowerCase().includes('joke')}`);
Loading