Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 37 additions & 14 deletions backend/ai_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,9 +95,21 @@ def get_explain_prompt(code: str, lang: str) -> str:
[Identify the primary DSA patterns, algorithms, or data structures used in this code, e.g. Sliding Window, DFS, Hash Table, Stack, Binary Search. Provide 1-2 sentences explaining why this pattern fits the problem.]
DSA_PATTERN_END

LEETCODE_PROBLEMS_START
[Suggest 3 related LeetCode problems that practice this pattern. For each, output: Title | Link (use standard https://leetcode.com/problems/... urls). Use newlines to separate.]
LEETCODE_PROBLEMS_END
PLATFORM_PROBLEMS_START
[Suggest 4-5 related practice problems for this pattern. CRITICAL ACCURACY MANDATE: You MUST include problem links ONLY for exact problem matches on that platform. Do NOT output a link if the platform does not feature that exact problem. Do NOT output generic or mismatched problem links. Mix of platforms (GeeksforGeeks, HackerRank, Codeforces, Coding Ninjas, and LeetCode). For each problem, output strictly on a new line: Platform | Title | Link (use actual valid problem URLs like https://www.geeksforgeeks.org/..., https://www.hackerrank.com/..., https://codeforces.com/..., https://leetcode.com/...).]
PLATFORM_PROBLEMS_END

OPTIMIZED_CODE_START
[Provide the absolute most optimal version of the analyzed code in terms of time and space complexity. Do NOT use markdown code fences inside this tag. CRITICAL RULE: If the input code is ALREADY optimal in both time and space complexity, write strictly `ALREADY_OPTIMIZED` inside this block.]
OPTIMIZED_CODE_END

OPTIMIZED_TIME_COMPLEXITY_START
[State the exact Big-O time complexity of the optimized code, e.g. O(N) linear time.]
OPTIMIZED_TIME_COMPLEXITY_END

OPTIMIZED_SPACE_COMPLEXITY_START
[State the exact Big-O space complexity of the optimized code, e.g. O(1) constant space.]
OPTIMIZED_SPACE_COMPLEXITY_END

PRACTICE_EXERCISES_START
[Provide 3 practice questions (Beginner, Intermediate, Advanced) that build on this concept. For each, output the difficulty, aim, and a brief sample input/output. Use clean Markdown structure.]
Expand Down Expand Up @@ -150,15 +162,18 @@ def get_dry_run_prompt(code: str, lang: str, test_case: str = None) -> str:
CRITICAL VISUALIZATION RULES:
1. If the code uses, traverses, or modifies any array, vector, list, or string (like search, sort, two-sum, reverse, sub-array, etc.), you MUST set "ds_type": "array" and "ds_data" to the current state of that array (e.g. [2, 7, 11, 15] or [0, 1, 1, 0, 1]). You MUST track the pointer indices (like i, j, k, low, mid, high, left, right, slow, fast) inside the "variables" object (e.g. {{"i": 1, "target": 9}}) so the frontend can float pointer arrows above/below the array boxes.
2. If the code uses a map, dictionary, or set (like std::map, unordered_map, set, dict), set "ds_type": "map" or "ds_type": "set". Set "ds_data" to the key-value dictionary (e.g. {{ "2": 0, "7": 1 }}) or set element array (e.g. [2, 7]).
3. End the array with a step representing the final return/output value.]
3. Trace boundary cases and out-of-bound pointer index values (e.g. low > high, i = -1, i = N) inside the "variables" object when loop termination or out-of-bound checks occur so boundary conditions can be visually demonstrated.
4. End the array with a step representing the final return/output value.]
DRY_RUN_END

FLOWCHART_START
[Generate a beautiful, logical Mermaid.js control flow diagram (graph TD) showing the execution flow of the code.
Use clean shapes and semantic nodes:
- Start/End steps: Use rounded brackets with double-quoted labels like `A("Start"):::startEnd` or `Z("End"):::startEnd`.
- Conditionals/Decisions: Use brace nodes with double-quoted labels like `B{{"Is condition met?"}}:::decision` (write this with double curly braces) and label paths clearly using `-- Yes -->` or `-- No -->`.
- Standard statements/process: Use square brackets with double-quoted labels like `C["Process/Action"]:::default`.
[Generate a clean, valid Mermaid.js control flow diagram (graph TD) showing the execution flow of the code.
CRITICAL MERMAID SYNTAX RULES:
1. ALWAYS wrap ALL node label texts inside double quotes, e.g. `A["Start"]:::startEnd` or `B["Initialize maxi = 0"]:::default`.
2. NEVER put spaces before `:::` (write `:::default`, NOT ` ::: default`).
3. DO NOT use semicolons `;` at the end of lines.
4. If using subgraphs, ALWAYS wrap the subgraph name in double quotes, e.g. `subgraph "findi(root, &maxi)"`.
5. Label decision paths strictly using `-->|"Yes"|`.

Include these class definitions in the flowchart output to apply our custom color theme:
classDef default fill:#122858,stroke:#3ecfb2,stroke-width:1.5px,color:#f3f5ed;
Expand All @@ -168,7 +183,12 @@ def get_dry_run_prompt(code: str, lang: str, test_case: str = None) -> str:
FLOWCHART_END

RECURSION_TREE_START
[If the code uses recursion, generate a beautiful, logical Mermaid.js graph TD diagram representing the recursion tree of the execution with the actual arguments and values. Use custom color classes. If the code does not use recursion, write RECURSION_NONE.
[If the code uses recursion, generate a clean, valid Mermaid.js graph TD diagram representing the recursion tree of the execution with the actual arguments and values. Use custom color classes. If the code does not use recursion, write RECURSION_NONE.
CRITICAL MERMAID SYNTAX RULES:
1. ALWAYS wrap ALL node label texts in double quotes, e.g. `A["solve(5)"]:::default`.
2. NEVER put spaces before `:::`.
3. If using subgraphs, ALWAYS wrap the title in double quotes, e.g. `subgraph "solve(n)"`.

Include these class definitions in the flowchart output to apply our custom color theme:
classDef default fill:#122858,stroke:#3ecfb2,stroke-width:1.5px,color:#f3f5ed;
classDef decision fill:#0f2348,stroke:#c9a84c,stroke-width:1.5px,color:#c9a84c;
Expand Down Expand Up @@ -196,13 +216,13 @@ async def analyze_code(code: str, language: str, test_case: str = None, api_key:
prompt2 = get_dry_run_prompt(code, language, test_case)

async def call_gemini(prompt: str):
for attempt in range(2):
for attempt in range(3):
try:
response = await asyncio.to_thread(client.models.generate_content, model=MODEL_NAME, contents=prompt)
return response.text
except Exception as e:
if "429" in str(e) and attempt == 0:
await asyncio.sleep(5)
if ("429" in str(e) or "RESOURCE_EXHAUSTED" in str(e)) and attempt < 2:
await asyncio.sleep(4 * (attempt + 1))
else:
raise e

Expand All @@ -220,7 +240,10 @@ async def call_gemini(prompt: str):
"space_complexity": _extract(r1, "SPACE_COMPLEXITY_START", "SPACE_COMPLEXITY_END"),
"suggestions": _extract(r1, "SUGGESTIONS_START", "SUGGESTIONS_END"),
"dsa_pattern": _extract(r1, "DSA_PATTERN_START", "DSA_PATTERN_END"),
"leetcode_problems": _extract(r1, "LEETCODE_PROBLEMS_START", "LEETCODE_PROBLEMS_END"),
"platform_problems": _extract(r1, "PLATFORM_PROBLEMS_START", "PLATFORM_PROBLEMS_END"),
"optimized_code": _extract(r1, "OPTIMIZED_CODE_START", "OPTIMIZED_CODE_END"),
"optimized_time_complexity": _extract(r1, "OPTIMIZED_TIME_COMPLEXITY_START", "OPTIMIZED_TIME_COMPLEXITY_END"),
"optimized_space_complexity": _extract(r1, "OPTIMIZED_SPACE_COMPLEXITY_START", "OPTIMIZED_SPACE_COMPLEXITY_END"),
"practice_exercises":_extract(r1, "PRACTICE_EXERCISES_START", "PRACTICE_EXERCISES_END"),
"interview_questions":_extract(r1, "INTERVIEW_QUESTIONS_START", "INTERVIEW_QUESTIONS_END"),
"algorithm": _extract(r1, "ALGORITHM_START", "ALGORITHM_END"),
Expand Down
5 changes: 4 additions & 1 deletion backend/test_features.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,10 @@ async def test_all():
print(f" Time complexity: {res.get('time_complexity')}")
print("\n--- NEW LEARNING PLATFORM FIELDS ---")
print(f" DSA Pattern: {res.get('dsa_pattern')}")
print(f" LeetCode Problems: {res.get('leetcode_problems')}")
print(f" Platform Problems: {res.get('platform_problems')}")
print(f" Optimized Code: {res.get('optimized_code')}")
print(f" Optimized Time Complexity: {res.get('optimized_time_complexity')}")
print(f" Optimized Space Complexity: {res.get('optimized_space_complexity')}")
print(f" Practice Exercises: {res.get('practice_exercises')}")
print(f" Interview Questions: {res.get('interview_questions')}")
print(f" Algorithm: {res.get('algorithm')}")
Expand Down
26 changes: 22 additions & 4 deletions backend/test_mock_ai.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,23 @@
Simple Output Pattern
DSA_PATTERN_END

LEETCODE_PROBLEMS_START
Hello World | https://leetcode.com/problems/hello-world
LEETCODE_PROBLEMS_END
PLATFORM_PROBLEMS_START
LeetCode | Hello World | https://leetcode.com/problems/hello-world
GeeksforGeeks | Print Hello World | https://www.geeksforgeeks.org/print-hello-world
HackerRank | Say Hello World | https://www.hackerrank.com/challenges/say-hello-world
PLATFORM_PROBLEMS_END

OPTIMIZED_CODE_START
print('hello')
OPTIMIZED_CODE_END

OPTIMIZED_TIME_COMPLEXITY_START
O(1) constant time
OPTIMIZED_TIME_COMPLEXITY_END

OPTIMIZED_SPACE_COMPLEXITY_START
O(1) constant space
OPTIMIZED_SPACE_COMPLEXITY_END

PRACTICE_EXERCISES_START
Q1. Print something else.
Expand Down Expand Up @@ -106,7 +120,7 @@ async def run_mock_test():

# Side effect function to return different mock responses
call_count = 0
def mock_generate_content(model, contents):
def mock_generate_content(model, contents, **kwargs):
nonlocal call_count
call_count += 1
if call_count == 1:
Expand All @@ -124,6 +138,10 @@ def mock_generate_content(model, contents):
assert result["time_complexity"] == "O(1) constant time", f"Got {result['time_complexity']}"
assert result["recursion_tree"] == "RECURSION_NONE", f"Got {result['recursion_tree']}"
assert "Prints hello" in result["dry_run"], f"Got {result['dry_run']}"
assert "GeeksforGeeks" in result["platform_problems"], f"Got {result['platform_problems']}"
assert result["optimized_code"] == "print('hello')", f"Got {result['optimized_code']}"
assert result["optimized_time_complexity"] == "O(1) constant time", f"Got {result['optimized_time_complexity']}"
assert result["optimized_space_complexity"] == "O(1) constant space", f"Got {result['optimized_space_complexity']}"

print("[SUCCESS] Unit test passed! Parallel prompt queries executed and parsed correctly.")

Expand Down
Loading
Loading