diff --git a/backend/ai.py b/backend/ai.py index 797d9e8..3d850ab 100644 --- a/backend/ai.py +++ b/backend/ai.py @@ -381,3 +381,47 @@ def rate_code_efficiency(title: str, code: str, language: str = "python") -> dic raise last_error or Exception( "All Gemini models are currently quota-limited. Please wait a minute and try again." ) + +def generate_hint( + title: str, + description: str, + difficulty: str, + hint_level: int +) -> str: + + hint_prompts = { + 1: "Give only a direction hint. Do not reveal algorithm or code.", + 2: "Suggest useful data structures. Do not reveal solution.", + 3: "Explain the intuition. No code.", + 4: "Provide pseudocode only.", + 5: "Provide the complete optimized solution." + } + + prompt = f""" + You are a LeetCode mentor. + + Problem Title: + {title} + + Problem Description: + {description} + + Difficulty: + {difficulty} + + Task: + {hint_prompts.get(hint_level, hint_prompts[1])} + + Keep response concise. + """ + + api_key = os.getenv("GEMINI_API_KEY") + + client = genai.Client(api_key=api_key) + + response = client.models.generate_content( + model="models/gemini-2.5-flash", + contents=prompt + ) + + return response.text \ No newline at end of file diff --git a/backend/main.py b/backend/main.py index b237e41..b634b40 100644 --- a/backend/main.py +++ b/backend/main.py @@ -20,6 +20,7 @@ # --- UPDATED AI PATH --- from ai_core.blog_generator import generate_blog +from ai import generate_hint from devto import publish_to_platforms from models.reminder import PublishRecord from services.reminder_scheduler import start_scheduler @@ -369,6 +370,12 @@ def health_check(): # ----------------------------- # Blog Generator Endpoint # ----------------------------- +class HintRequest(BaseModel): + title: str + description: str + difficulty: str = "Unknown" + hint_level: int + @app.post("/generate-blog") async def create_blog( problem: Problem, @@ -481,6 +488,29 @@ async def create_blog( "social": social_results, }, } +@app.post("/generate-hint") +async def generate_hint_api(request: HintRequest): + + try: + + hint = generate_hint( + request.title, + request.description, + request.difficulty, + request.hint_level + ) + + return { + "status": "success", + "hint": hint + } + + except Exception as e: + + return { + "status": "error", + "message": str(e) + } # ----------------------------- diff --git a/backend/tests/test_devto.py b/backend/tests/test_devto.py index 55ccfb1..2901a76 100644 --- a/backend/tests/test_devto.py +++ b/backend/tests/test_devto.py @@ -183,4 +183,6 @@ async def test_empty_errors_list_does_not_raise(self, mock_hashnode_request): "Two Sum", "# content", tags=["leetcode"], published=True ) assert result.status == "success" + + diff --git a/extension/background.js b/extension/background.js index 642ee24..fa917b4 100644 --- a/extension/background.js +++ b/extension/background.js @@ -50,6 +50,31 @@ chrome.runtime.onMessage.addListener((request, sender, sendResponse) => { }); } + chrome.runtime.onMessage.addListener((request) => { + + if (request.type === "GENERATE_HINT") { + + fetch(`${API_BASE_URL}/generate-hint`, { + method: "POST", + headers: { + "Content-Type": "application/json" + }, + body: JSON.stringify(request.payload) + }) + .then(res => res.json()) + .then(data => { + + chrome.runtime.sendMessage({ + type: "HINT_READY", + hint: data.hint + }); + + }); + + } + + }); + if (data.status === 'success' || data.status === 'partial_success') { const platforms = data.data?.platforms || []; const postedPlatforms = platforms diff --git a/extension/popup.html b/extension/popup.html index c03bdf0..0bb7369 100644 --- a/extension/popup.html +++ b/extension/popup.html @@ -572,6 +572,42 @@

Preview & Edit Blog

+
+ +

Hint Progression Mode

+ +
+ + +
+ +
+ + +
+ + + + + +
+
Ready to blog! 🚀
📋 Recent Posts diff --git a/extension/popup.js b/extension/popup.js index 78d5131..1420b5f 100644 --- a/extension/popup.js +++ b/extension/popup.js @@ -533,4 +533,74 @@ const copyBtn = document.getElementById('copyBtn'); if (copyBtn) { copyBtn.addEventListener('click', copyBlogToClipboard); } + +async function requestHint(level) { + + const tabs = await chrome.tabs.query({ + active: true, + currentWindow: true + }); + + const tab = tabs[0]; + + chrome.scripting.executeScript( + { + target: { tabId: tab.id }, + func: () => { + + const titleElement = + document.querySelector('div[data-cy="question-title"]') || + document.querySelector('.text-title-large'); + + const descriptionElement = + document.querySelector('.elfjS') || + document.querySelector('[data-track-load="description_content"]'); + + return { + title: titleElement ? titleElement.innerText : "", + description: descriptionElement ? descriptionElement.innerText : "", + difficulty: "Unknown" + }; + } + }, + (results) => { + + const problem = results[0].result; + + chrome.runtime.sendMessage({ + type: "GENERATE_HINT", + payload: { + ...problem, + hint_level: level + } + }); + + } + ); +} + +document.getElementById("hint1Btn")?.addEventListener("click", () => requestHint(1)); + +document.getElementById("hint2Btn")?.addEventListener("click", () => requestHint(2)); + +document.getElementById("hint3Btn")?.addEventListener("click", () => requestHint(3)); + +document.getElementById("hint4Btn")?.addEventListener("click", () => requestHint(4)); + +document.getElementById("solutionBtn")?.addEventListener("click", () => requestHint(5)); + +chrome.runtime.onMessage.addListener((request) => { + + if (request.type === "HINT_READY") { + + const hintBox = document.getElementById("hintOutput"); + + if (hintBox) { + hintBox.value = request.hint; + } + + } + +}); + // ================================================= \ No newline at end of file