config: add OpenCode API fallback for Gemini quota
- Configured OpenCode as fallback when Gemini quota exceeded - Created gemini-fallback.js utility (tries Gemini → OpenCode) - API keys stored in .env (excluded from git) - PM unblocked: can resume 05-03 with fallback system Flow: Gemini (primary) → OpenCode (fallback) → fail gracefully
This commit is contained in:
+24
-5
@@ -1,7 +1,26 @@
|
||||
{
|
||||
"lastRun": "2026-03-02T18:20:00Z",
|
||||
"status": "completed",
|
||||
"result": "Task 05-03 completed: Frontend integration for exercise research display. Created ExerciseResearchPanel.jsx (107 lines) and ExerciseEncyclopediaPage.jsx (128 lines). Wired into App.jsx with nav button and CSS.",
|
||||
"nextTask": "05-04: Testing and polish (if any remaining tasks in phase 05)",
|
||||
"commits": ["83ccd6c feat(05-03): Exercise research frontend integration"]
|
||||
"lastRun": "2026-03-02T19:37:00Z",
|
||||
"status": "unblocked",
|
||||
"unblockedReason": "OpenCode API configured as fallback for Gemini quota",
|
||||
"currentPhase": "05",
|
||||
"currentTask": "05-03",
|
||||
"result": "Fallback system implemented: Gemini (primary) → OpenCode (fallback)",
|
||||
"nextTask": "05-03: Frontend integration for research display (can now proceed with OpenCode fallback)",
|
||||
|
||||
"apiConfiguration": {
|
||||
"primary": {
|
||||
"provider": "Gemini",
|
||||
"status": "quota-limited",
|
||||
"notes": "Free tier has daily limits"
|
||||
},
|
||||
"fallback": {
|
||||
"provider": "OpenCode",
|
||||
"baseUrl": "https://api.opencode.com/v1",
|
||||
"model": "gpt-4",
|
||||
"status": "configured"
|
||||
},
|
||||
"implementation": "backend/src/utils/gemini-fallback.js"
|
||||
},
|
||||
|
||||
"action": "READY TO RESUME: PM can continue with 05-03 using fallback"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
/**
|
||||
* Gemini API with OpenCode Fallback
|
||||
* Tries Gemini first, falls back to OpenCode if quota exceeded
|
||||
*/
|
||||
|
||||
const fetch = require('node-fetch');
|
||||
|
||||
const GEMINI_API_KEY = process.env.GOOGLE_API_KEY;
|
||||
const OPENCODE_API_KEY = process.env.OPENCODE_API_KEY;
|
||||
const OPENCODE_BASE_URL = process.env.OPENCODE_BASE_URL || 'https://api.opencode.com/v1';
|
||||
|
||||
async function generateWithFallback(prompt, options = {}) {
|
||||
console.log('🤖 Generating content...');
|
||||
|
||||
// Try Gemini first
|
||||
if (GEMINI_API_KEY) {
|
||||
try {
|
||||
console.log('Attempting Gemini API...');
|
||||
const response = await fetch(
|
||||
`https://generativelanguage.googleapis.com/v1/models/gemini-pro:generateContent?key=${GEMINI_API_KEY}`,
|
||||
{
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
contents: [{ parts: [{ text: prompt }] }],
|
||||
generationConfig: options.config || {}
|
||||
})
|
||||
}
|
||||
);
|
||||
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
console.log('✅ Gemini API success');
|
||||
return { success: true, provider: 'gemini', data };
|
||||
}
|
||||
|
||||
if (response.status === 429 || response.status === 403) {
|
||||
console.warn('⚠️ Gemini quota exceeded, falling back to OpenCode...');
|
||||
throw new Error('QUOTA_EXCEEDED');
|
||||
}
|
||||
|
||||
throw new Error(`Gemini API error: ${response.status}`);
|
||||
} catch (err) {
|
||||
console.warn(`Gemini failed: ${err.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback to OpenCode
|
||||
if (OPENCODE_API_KEY) {
|
||||
try {
|
||||
console.log('Attempting OpenCode API...');
|
||||
const response = await fetch(`${OPENCODE_BASE_URL}/chat/completions`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Authorization': `Bearer ${OPENCODE_API_KEY}`,
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
model: options.model || 'gpt-4',
|
||||
messages: [{ role: 'user', content: prompt }],
|
||||
temperature: options.temperature || 0.7,
|
||||
max_tokens: options.maxTokens || 2048
|
||||
})
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
console.log('✅ OpenCode API success');
|
||||
return { success: true, provider: 'opencode', data };
|
||||
}
|
||||
|
||||
throw new Error(`OpenCode API error: ${response.status}`);
|
||||
} catch (err) {
|
||||
console.error(`OpenCode failed: ${err.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
throw new Error('All generation APIs failed');
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
generateWithFallback,
|
||||
getAvailableProviders: () => ({
|
||||
gemini: !!GEMINI_API_KEY,
|
||||
opencode: !!OPENCODE_API_KEY
|
||||
})
|
||||
};
|
||||
Reference in New Issue
Block a user