Auto-deploy: 2026-05-17 01:38:08
This commit is contained in:
@@ -17,59 +17,66 @@ chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
|
||||
|
||||
// ─── Core API call ───────────────────────────────────────────────────────────
|
||||
|
||||
async function handleGeminiRequest({ apiKey, prompt, tab }) {
|
||||
async function handleGeminiRequest({ apiKey, prompt, tab, action = 'generateText', jobDescription = '' }) {
|
||||
// Rate limit check
|
||||
const canProceed = await checkRateLimit();
|
||||
if (!canProceed) {
|
||||
throw new Error('Daily limit reached (1,000 requests). Resets at midnight PT.');
|
||||
}
|
||||
|
||||
// Check cache (keyed by tab + prompt hash — different jobs produce different hashes)
|
||||
const cacheKey = `cache_${tab}_${hashString(prompt)}`;
|
||||
// Check cache (keyed by tab + prompt hash)
|
||||
const cacheStr = prompt || jobDescription;
|
||||
const cacheKey = `cache_${tab}_${action}_${hashString(cacheStr)}`;
|
||||
const cached = await getCached(cacheKey);
|
||||
if (cached) {
|
||||
return { text: cached, fromCache: true };
|
||||
return cached; // returns either text object or pdf object
|
||||
}
|
||||
|
||||
// Truncate prompt if too long (free tier has strict TPM limits)
|
||||
const maxPromptChars = 6000;
|
||||
const trimmedPrompt = prompt.length > maxPromptChars
|
||||
? prompt.substring(0, maxPromptChars) + '\n\n[Description truncated for length]'
|
||||
// Truncate text
|
||||
const maxChars = 6000;
|
||||
const trimmedPrompt = prompt && prompt.length > maxChars
|
||||
? prompt.substring(0, maxChars) + '\n\n[Truncated]'
|
||||
: prompt;
|
||||
|
||||
// Retry logic (up to 3 attempts with LONG backoff for free tier)
|
||||
const MAX_RETRIES = 3;
|
||||
const RETRY_DELAYS = [2000, 20000, 30000]; // 2s, 20s, 30s
|
||||
const RETRY_DELAYS = [2000, 20000, 30000];
|
||||
let lastError = '';
|
||||
|
||||
for (let attempt = 0; attempt < MAX_RETRIES; attempt++) {
|
||||
await delay(RETRY_DELAYS[attempt]);
|
||||
|
||||
try {
|
||||
const response = await fetch(`${GEMINI_URL}?key=${apiKey}`, {
|
||||
const response = await fetch('https://cv.intaleqapp.com/cv/server/generate_cv.php', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
contents: [{ parts: [{ text: trimmedPrompt }] }],
|
||||
generationConfig: {
|
||||
temperature: 0.7,
|
||||
maxOutputTokens: 2048,
|
||||
topP: 0.9
|
||||
}
|
||||
action: action,
|
||||
apiKey: apiKey,
|
||||
prompt: trimmedPrompt,
|
||||
jobDescription: jobDescription
|
||||
})
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
const text = data.candidates?.[0]?.content?.parts?.[0]?.text;
|
||||
if (!text) {
|
||||
lastError = 'Empty response from Gemini.';
|
||||
continue;
|
||||
|
||||
if (action === 'generatePdf') {
|
||||
if (!data.pdf) throw new Error('Empty PDF response from server.');
|
||||
await incrementUsage();
|
||||
const result = { pdf: data.pdf, filename: data.filename, fromCache: false };
|
||||
await setCached(cacheKey, result);
|
||||
return result;
|
||||
} else {
|
||||
const text = data.candidates?.[0]?.content?.parts?.[0]?.text;
|
||||
if (!text) {
|
||||
lastError = 'Empty response from API.';
|
||||
continue;
|
||||
}
|
||||
await incrementUsage();
|
||||
const result = { text, fromCache: false };
|
||||
await setCached(cacheKey, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
await incrementUsage();
|
||||
await setCached(cacheKey, text);
|
||||
return { text, fromCache: false };
|
||||
}
|
||||
|
||||
const status = response.status;
|
||||
|
||||
Reference in New Issue
Block a user