Skip to content

Commit 0749203

Browse files
Merge pull request #51 from hemanth5055/feat/anonymous-usage-analytics
Added language detection and KV-based usage analytics
2 parents efa406f + c8bd2f4 commit 0749203

File tree

2 files changed

+68
-16
lines changed

2 files changed

+68
-16
lines changed

backend/src/index.ts

Lines changed: 63 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { GoogleGenerativeAI } from '@google/generative-ai';
22

33
export interface Env {
44
RATE_LIMIT: KVNamespace;
5+
LANG_TRANSLATION_ANALYTICS: KVNamespace;
56
GEMINI_API_KEY: string;
67
}
78

@@ -15,7 +16,7 @@ const MAX_REQUESTS_ALLOWED = 10;
1516
const DURATION = 60_000;
1617

1718
async function checkRateLimit(ip: string, env: Env) {
18-
const key = `ip_key:${ip}`;
19+
const key = `ip_key:${ip}`.toLowerCase();
1920
const now = Date.now();
2021
let value = await env.RATE_LIMIT.get(key);
2122
let data = { count: 0, time: now };
@@ -38,7 +39,27 @@ async function checkRateLimit(ip: string, env: Env) {
3839

3940
return data.count <= MAX_REQUESTS_ALLOWED;
4041
}
41-
async function handleTranslate(request: Request, model: ReturnType<GoogleGenerativeAI['getGenerativeModel']>) {
42+
43+
async function updateAnalytics(source: string, dest: string, env: Env) {
44+
const normalizedSource = source.trim().toLowerCase();
45+
const normalizedDest = dest.trim().toLowerCase();
46+
const key = `${normalizedSource}-${normalizedDest}`;
47+
let value = await env.LANG_TRANSLATION_ANALYTICS.get(key);
48+
49+
let data = { count: 0 };
50+
if (value) {
51+
try {
52+
data = JSON.parse(value);
53+
} catch {
54+
data = { count: 0 };
55+
}
56+
}
57+
58+
data.count += 1; // increment usage count
59+
60+
await env.LANG_TRANSLATION_ANALYTICS.put(key, JSON.stringify(data));
61+
}
62+
async function handleTranslate(request: Request, model: ReturnType<GoogleGenerativeAI['getGenerativeModel']>, env: Env) {
4263
const { code, targetLanguage } = await request.json<{ code: string; targetLanguage: string }>();
4364

4465
if (!code || !targetLanguage) {
@@ -47,7 +68,7 @@ async function handleTranslate(request: Request, model: ReturnType<GoogleGenerat
4768
headers: { ...corsHeaders, 'Content-Type': 'application/json' },
4869
});
4970
}
50-
71+
const sourceLanguage = await detectLanguage(code, model);
5172
const prompt = `Translate the following code snippet to ${targetLanguage}.
5273
Do not add any explanation, commentary, or markdown formatting like \`\`\` around the code.
5374
**IMPORTANT: Preserve all original comments and their exact placement in the translated code. Do not add extra spaces in between.**
@@ -58,8 +79,8 @@ ${code}`;
5879

5980
const result = await model.generateContent(prompt);
6081
const translatedCode = result.response.text();
61-
62-
return new Response(JSON.stringify({ translation: translatedCode }), {
82+
await updateAnalytics(sourceLanguage, targetLanguage, env);
83+
return new Response(JSON.stringify({ translation: translatedCode}), {
6384
status: 200,
6485
headers: { ...corsHeaders, 'Content-Type': 'application/json' },
6586
});
@@ -102,23 +123,39 @@ export default {
102123

103124
try {
104125
const ip = request.headers.get('CF-Connecting-IP') || 'unknown';
105-
const allowed = await checkRateLimit(ip, env);
106-
if (!allowed) {
107-
return new Response(JSON.stringify({ error: "Too many requests. Try again later." }), {
108-
status: 429,
109-
headers: { ...corsHeaders, 'Content-Type': 'application/json' },
110-
});
111-
}
126+
const allowed = await checkRateLimit(ip, env);
127+
if (!allowed) {
128+
return new Response(JSON.stringify({ error: 'Too many requests. Try again later.' }), {
129+
status: 429,
130+
headers: { ...corsHeaders, 'Content-Type': 'application/json' },
131+
});
132+
}
112133
const url = new URL(request.url);
113134
const path = url.pathname;
114135
const genAI = new GoogleGenerativeAI(env.GEMINI_API_KEY);
115136
const model = genAI.getGenerativeModel({ model: 'gemini-2.0-flash' });
137+
if (path === '/v1/analytics') {
138+
const list = await env.LANG_TRANSLATION_ANALYTICS.list();
139+
const stats: Record<string, any> = {};
140+
for (const key of list.keys) {
141+
const val = await env.LANG_TRANSLATION_ANALYTICS.get(key.name);
142+
try {
143+
stats[key.name] = JSON.parse(val || '{}');
144+
} catch (e) {
145+
console.error(`Failed to parse analytics value for key "${key.name}":`, e);
146+
stats[key.name] = {};
147+
}
148+
}
149+
return new Response(JSON.stringify(stats, null, 2), {
150+
headers: { ...corsHeaders, 'Content-Type': 'application/json' },
151+
});
152+
}
116153

117-
if(path==="/test-rate-limit"){
118-
return new Response(JSON.stringify("Proceed !"))
154+
if (path === '/test-rate-limit') {
155+
return new Response(JSON.stringify('Proceed !'));
119156
}
120157
if (path === '/' || path === '/v1/translate') {
121-
return await handleTranslate(request, model);
158+
return await handleTranslate(request, model, env);
122159
}
123160

124161
if (path === '/v1/explain') {
@@ -138,3 +175,14 @@ export default {
138175
}
139176
},
140177
};
178+
179+
async function detectLanguage(code: string, model: ReturnType<GoogleGenerativeAI['getGenerativeModel']>) {
180+
const prompt = `Identify the programming language of the following code.
181+
Only respond with the exact language name (e.g., "python", "javascript", "c++", "java", etc.) without any extra text or punctuation.
182+
183+
Code:
184+
${code}`;
185+
186+
const result = await model.generateContent(prompt);
187+
return result.response.text().trim().toLowerCase();
188+
}

backend/wrangler.jsonc

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,11 @@
1313
"kv_namespaces": [
1414
{
1515
"binding": "RATE_LIMIT",
16-
"id": "<your_kv_id>"
16+
"id": "<your_kv_id>",
17+
}
18+
,{
19+
"binding": "LANG_TRANSLATION_ANALYTICS",
20+
"id":"<your_kv_id>"
1721
}
1822
]
1923

0 commit comments

Comments
 (0)