config: switch to Ollama-first strategy (local AI priority)

Changed API fallback chain:
- Tier 1: Ollama (local, free, always available)
- Tier 2: Gemini (free tier with quota limits)
- Tier 3: OpenRouter (cheap, flexible fallback)
- Tier 4: OpenCode (final backup)

This saves costs by using local Ollama for most tasks.
OpenRouter only used when Ollama unavailable.
This commit is contained in:
2026-03-02 19:48:41 +01:00
parent 2f6392a807
commit 210a2d15a9
+40 -11
View File
@@ -1,10 +1,12 @@
/** /**
* Gemini API with Multi-Tier Fallback * AI API Fallback System
* Tries: Gemini → OpenRouter → OpenCode * Tries: Ollama (local) → Gemini → OpenRouter → OpenCode
*/ */
const fetch = require('node-fetch'); const fetch = require('node-fetch');
const OLLAMA_URL = process.env.OLLAMA_URL || 'http://localhost:11434';
const OLLAMA_MODEL = process.env.OLLAMA_MODEL || 'deepseek-v3.2:cloud';
const GEMINI_API_KEY = process.env.GOOGLE_API_KEY; const GEMINI_API_KEY = process.env.GOOGLE_API_KEY;
const OPENROUTER_API_KEY = process.env.OPENROUTER_API_KEY; const OPENROUTER_API_KEY = process.env.OPENROUTER_API_KEY;
const OPENROUTER_BASE_URL = process.env.OPENROUTER_BASE_URL || 'https://openrouter.ai/api/v1'; const OPENROUTER_BASE_URL = process.env.OPENROUTER_BASE_URL || 'https://openrouter.ai/api/v1';
@@ -14,10 +16,36 @@ const OPENCODE_BASE_URL = process.env.OPENCODE_BASE_URL || 'https://api.opencode
async function generateWithFallback(prompt, options = {}) { async function generateWithFallback(prompt, options = {}) {
console.log('🤖 Generating content...'); console.log('🤖 Generating content...');
// Tier 1: Try Gemini // Tier 1: Try Ollama (local, free)
try {
console.log(`📍 Tier 1: Attempting Ollama (${OLLAMA_MODEL})...`);
const response = await fetch(`${OLLAMA_URL}/api/generate`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
timeout: 30000,
body: JSON.stringify({
model: OLLAMA_MODEL,
prompt: prompt,
stream: false,
temperature: options.temperature || 0.7
})
});
if (response.ok) {
const data = await response.json();
console.log('✅ Ollama success');
return { success: true, provider: 'ollama', data };
}
console.warn(`⚠️ Ollama error: ${response.status}, trying next...`);
} catch (err) {
console.warn(`Ollama failed: ${err.message}`);
}
// Tier 2: Try Gemini
if (GEMINI_API_KEY) { if (GEMINI_API_KEY) {
try { try {
console.log('📍 Tier 1: Attempting Gemini API...'); console.log('📍 Tier 2: Attempting Gemini API...');
const response = await fetch( const response = await fetch(
`https://generativelanguage.googleapis.com/v1/models/gemini-pro:generateContent?key=${GEMINI_API_KEY}`, `https://generativelanguage.googleapis.com/v1/models/gemini-pro:generateContent?key=${GEMINI_API_KEY}`,
{ {
@@ -37,7 +65,7 @@ async function generateWithFallback(prompt, options = {}) {
} }
if (response.status === 429 || response.status === 403) { if (response.status === 429 || response.status === 403) {
console.warn('⚠️ Gemini quota exceeded, trying next fallback...'); console.warn('⚠️ Gemini quota exceeded, trying next...');
} else { } else {
throw new Error(`Gemini error: ${response.status}`); throw new Error(`Gemini error: ${response.status}`);
} }
@@ -46,10 +74,10 @@ async function generateWithFallback(prompt, options = {}) {
} }
} }
// Tier 2: Fallback to OpenRouter (billigare, mer flexibel) // Tier 3: Fallback to OpenRouter
if (OPENROUTER_API_KEY) { if (OPENROUTER_API_KEY) {
try { try {
console.log('📍 Tier 2: Attempting OpenRouter API...'); console.log('📍 Tier 3: Attempting OpenRouter API...');
const response = await fetch(`${OPENROUTER_BASE_URL}/chat/completions`, { const response = await fetch(`${OPENROUTER_BASE_URL}/chat/completions`, {
method: 'POST', method: 'POST',
headers: { headers: {
@@ -71,16 +99,16 @@ async function generateWithFallback(prompt, options = {}) {
return { success: true, provider: 'openrouter', data }; return { success: true, provider: 'openrouter', data };
} }
console.warn(`OpenRouter error: ${response.status}, trying next fallback...`); console.warn(`OpenRouter error: ${response.status}, trying next...`);
} catch (err) { } catch (err) {
console.warn(`OpenRouter failed: ${err.message}`); console.warn(`OpenRouter failed: ${err.message}`);
} }
} }
// Tier 3: Fallback to OpenCode (sista försöket) // Tier 4: Final fallback to OpenCode
if (OPENCODE_API_KEY) { if (OPENCODE_API_KEY) {
try { try {
console.log('📍 Tier 3: Attempting OpenCode API...'); console.log('📍 Tier 4: Attempting OpenCode API...');
const response = await fetch(`${OPENCODE_BASE_URL}/chat/completions`, { const response = await fetch(`${OPENCODE_BASE_URL}/chat/completions`, {
method: 'POST', method: 'POST',
headers: { headers: {
@@ -107,12 +135,13 @@ async function generateWithFallback(prompt, options = {}) {
} }
} }
throw new Error('All generation APIs failed (Gemini → OpenRouter → OpenCode)'); throw new Error('All generation APIs failed (Ollama → Gemini → OpenRouter → OpenCode)');
} }
module.exports = { module.exports = {
generateWithFallback, generateWithFallback,
getAvailableProviders: () => ({ getAvailableProviders: () => ({
ollama: true, // Always available locally
gemini: !!GEMINI_API_KEY, gemini: !!GEMINI_API_KEY,
openrouter: !!OPENROUTER_API_KEY, openrouter: !!OPENROUTER_API_KEY,
opencode: !!OPENCODE_API_KEY opencode: !!OPENCODE_API_KEY