test: update tests
This commit is contained in:
+49
-38
@@ -1,3 +1,5 @@
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const { OpenAI } = require('openai');
|
||||
const { GoogleGenAI } = require('@google/genai');
|
||||
const { Progress } = require('../utils/progress');
|
||||
@@ -9,31 +11,17 @@ const { color } = require('../utils/colors');
|
||||
class AIProviderManager {
|
||||
constructor(config = {}) {
|
||||
this.config = config;
|
||||
this.cache = new Map();
|
||||
this.maxCacheSize = 100;
|
||||
}
|
||||
|
||||
resolveProvider(preference = 'auto') {
|
||||
if (preference === 'none') return 'none';
|
||||
|
||||
// Check if preferred provider's key is available
|
||||
if (preference === 'openai' && process.env.OPENAI_API_KEY) return 'openai';
|
||||
if (preference === 'gemini' && process.env.GEMINI_API_KEY) return 'gemini';
|
||||
if (preference === 'groq' && process.env.GROQ_API_KEY) return 'groq';
|
||||
|
||||
// Fallback: try any available provider (priority: Gemini → OpenAI → Groq)
|
||||
if (process.env.GEMINI_API_KEY) return 'gemini';
|
||||
if (process.env.OPENAI_API_KEY) return 'openai';
|
||||
if (process.env.GROQ_API_KEY) return 'groq';
|
||||
|
||||
return 'none';
|
||||
this.cacheFile = path.join(
|
||||
process.env.HOME || process.env.USERPROFILE || process.cwd(),
|
||||
'.gims', 'cache.json'
|
||||
);
|
||||
}
|
||||
|
||||
getDefaultModel(provider) {
|
||||
const defaults = {
|
||||
'gemini': 'gemini-3-flash-preview', // Latest Gemini model
|
||||
'openai': 'gpt-5.2-2025-12-11', // Latest GPT model
|
||||
'groq': 'groq/compound' // Latest Groq model
|
||||
'gemini': 'gemini-2.0-flash',
|
||||
'openai': 'gpt-4o-mini',
|
||||
'groq': 'llama-3.3-70b-versatile'
|
||||
};
|
||||
return defaults[provider] || '';
|
||||
}
|
||||
@@ -46,26 +34,36 @@ class AIProviderManager {
|
||||
|
||||
getFromCache(cacheKey) {
|
||||
if (!this.config.cacheEnabled) return null;
|
||||
return this.cache.get(cacheKey);
|
||||
try {
|
||||
if (!fs.existsSync(this.cacheFile)) return null;
|
||||
const data = JSON.parse(fs.readFileSync(this.cacheFile, 'utf8'));
|
||||
return data[cacheKey] || null;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
setCache(cacheKey, result, usedLocal = false) {
|
||||
if (!this.config.cacheEnabled) return;
|
||||
|
||||
if (this.cache.size >= this.maxCacheSize) {
|
||||
const firstKey = this.cache.keys().next().value;
|
||||
this.cache.delete(firstKey);
|
||||
}
|
||||
|
||||
this.cache.set(cacheKey, {
|
||||
result,
|
||||
usedLocal,
|
||||
timestamp: Date.now()
|
||||
});
|
||||
try {
|
||||
const dir = path.dirname(this.cacheFile);
|
||||
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
||||
let data = {};
|
||||
if (fs.existsSync(this.cacheFile)) {
|
||||
try { data = JSON.parse(fs.readFileSync(this.cacheFile, 'utf8')); } catch {}
|
||||
}
|
||||
const keys = Object.keys(data);
|
||||
if (keys.length >= 100) {
|
||||
const oldest = keys.sort((a, b) => (data[a].timestamp || 0) - (data[b].timestamp || 0));
|
||||
oldest.slice(0, keys.length - 99).forEach(k => delete data[k]);
|
||||
}
|
||||
data[cacheKey] = { result, usedLocal, timestamp: Date.now() };
|
||||
fs.writeFileSync(this.cacheFile, JSON.stringify(data));
|
||||
} catch {}
|
||||
}
|
||||
|
||||
async generateWithProvider(provider, prompt, options = {}) {
|
||||
const { model = '', temperature = 0.3, maxTokens = 200 } = options;
|
||||
const { model = '' } = options;
|
||||
|
||||
try {
|
||||
switch (provider) {
|
||||
@@ -79,7 +77,14 @@ class AIProviderManager {
|
||||
throw new Error(`Unknown provider: ${provider}`);
|
||||
}
|
||||
} catch (error) {
|
||||
throw new Error(`${provider} generation failed: ${error.message}`);
|
||||
const msg = error.message || '';
|
||||
if (msg.includes('429') || /rate.?limit/i.test(msg)) {
|
||||
throw new Error(`${provider} rate limit hit — try again in a moment`);
|
||||
}
|
||||
if (msg.includes('401') || /invalid.api.key|api_key/i.test(msg)) {
|
||||
throw new Error(`${provider} API key invalid — run: g setup --api-key ${provider}`);
|
||||
}
|
||||
throw new Error(`${provider} generation failed: ${msg}`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -135,8 +140,14 @@ class AIProviderManager {
|
||||
verbose = false
|
||||
} = options;
|
||||
|
||||
// Truncate oversized diffs before sending to AI
|
||||
const maxDiffSize = this.config.maxDiffSize || 100000;
|
||||
const truncatedDiff = diff.length > maxDiffSize
|
||||
? diff.substring(0, maxDiffSize) + '\n... (diff truncated for AI)'
|
||||
: diff;
|
||||
|
||||
// Check cache first
|
||||
const cacheKey = this.getCacheKey(diff, { conventional, body });
|
||||
const cacheKey = this.getCacheKey(truncatedDiff, { conventional, body });
|
||||
const cached = this.getFromCache(cacheKey);
|
||||
if (cached && Date.now() - cached.timestamp < 3600000) { // 1 hour cache
|
||||
if (verbose) Progress.info('Using cached result');
|
||||
@@ -148,12 +159,12 @@ class AIProviderManager {
|
||||
for (const provider of providerChain) {
|
||||
try {
|
||||
if (provider === 'local') {
|
||||
const result = await this.generateLocalHeuristic(diff, options);
|
||||
const result = await this.generateLocalHeuristic(truncatedDiff, options);
|
||||
this.setCache(cacheKey, result, true);
|
||||
return { message: result, usedLocal: true };
|
||||
}
|
||||
|
||||
const prompt = this.buildPrompt(diff, { conventional, body });
|
||||
const prompt = this.buildPrompt(truncatedDiff, { conventional, body });
|
||||
const result = await this.generateWithProvider(provider, prompt, options);
|
||||
const cleaned = this.cleanCommitMessage(result, { body });
|
||||
|
||||
|
||||
Reference in New Issue
Block a user