Wendgan commited on
Commit
95dc7c2
·
verified ·
1 Parent(s): 65146f2

Upload 3 files

Browse files
Files changed (3) hide show
  1. README.md +28 -20
  2. app.py.py +106 -0
  3. requirements.txt +4 -0
README.md CHANGED
@@ -1,5 +1,5 @@
1
  ---
2
- title: Gemini Distractor Generator
3
  emoji: 🧠
4
  colorFrom: indigo
5
  colorTo: pink
@@ -9,43 +9,51 @@ app_file: app.py
9
  pinned: false
10
  ---
11
 
 
12
 
13
- # Gemini Distractor Generator
14
-
15
- This is a simple web app that uses Gemini 1.5 Flash (via Gemini API) to generate multiple-choice distractors for a given question. It automatically identifies the correct answer and returns three plausible distractors.
16
 
17
  ## Features
18
- - Uses Gemini 1.5 Flash (Free Tier) via API
19
- - Auto-generates distractors based on question context
20
- - Also attempts to identify the correct answer
21
- - Includes minimal Gradio UI
 
22
 
23
  ## Files
24
- - `app.py`: Main Gradio application.
25
- - `requirements.txt`: Required Python libraries.
26
- - `README.md`: This file.
 
27
 
28
  ## How to Run
29
 
30
  ### Locally
31
- 1. Install the dependencies:
 
32
  ```bash
33
  pip install -r requirements.txt
34
  ```
35
 
36
- 2. Run the app:
 
 
 
 
37
  ```bash
38
  python app.py
39
  ```
40
 
41
- 3. Visit the local URL shown by Gradio.
42
 
43
  ### On Hugging Face Spaces
44
- 1. Upload all three files to your Hugging Face Space.
45
- 2. Choose `Gradio` as the SDK.
46
- 3. Make sure to set your Gemini API key in a secure way (e.g., using secrets or hardcoded for testing).
47
- 4. Set `app.py` as the entry point.
 
48
 
49
  ## Notes
50
- - Gemini 1.5 Flash is selected to stay within free-tier quotas.
51
- - You may need to set your API key inside `app.py` manually.
 
 
1
  ---
2
+ title: Gemini + Claude Distractor Generator
3
  emoji: 🧠
4
  colorFrom: indigo
5
  colorTo: pink
 
9
  pinned: false
10
  ---
11
 
12
+ # Gemini + Claude Distractor Generator
13
 
14
+ This is a web app that uses **Gemini 1.5 Flash (Free Tier)** to generate multiple-choice distractors and then uses **Claude 3.5 Sonnet (via OpenRouter)** to **rank the distractors** based on how confusing they are.
 
 
15
 
16
  ## Features
17
+
18
+ - Generates distractors using Gemini 1.5 Flash
19
+ - Uses Claude Sonnet 3.5 to evaluate and rank distractors (LLM-as-Judge)
20
+ - Displays the correct answer, distractors, and Claude's ranking
21
+ - Simple and clean Gradio UI
22
 
23
  ## Files
24
+
25
+ - `app.py`: Main Gradio application logic
26
+ - `requirements.txt`: Dependencies
27
+ - `README.md`: This file
28
 
29
  ## How to Run
30
 
31
  ### Locally
32
+
33
+ 1. Install dependencies:
34
  ```bash
35
  pip install -r requirements.txt
36
  ```
37
 
38
+ 2. Set your API keys inside `app.py`:
39
+ - Gemini API key (for generation)
40
+ - OpenRouter API key (for ranking via Claude)
41
+
42
+ 3. Run:
43
  ```bash
44
  python app.py
45
  ```
46
 
47
+ 4. Open the Gradio link shown in your terminal.
48
 
49
  ### On Hugging Face Spaces
50
+
51
+ 1. Upload `app.py`, `requirements.txt`, and `README.md`
52
+ 2. Choose `Gradio` as the SDK
53
+ 3. Make sure to set your **Gemini API key** and **OpenRouter API key** via HF secrets or hardcode for testing
54
+ 4. Set `app.py` as your main file (`app_file`)
55
 
56
  ## Notes
57
+
58
+ - Claude is used only to rank distractors; Gemini handles generation.
59
+ - Free-tier Gemini is slower and limited — ranking may take longer due to OpenRouter limits.
app.py.py ADDED
@@ -0,0 +1,106 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ import gradio as gr
3
+ import google.generativeai as genai
4
+ import requests
5
+ import json
6
+ import time
7
+
8
+ genai.configure(api_key="GEMINI_API_KEY")
9
+ gemini_model = genai.GenerativeModel("gemini-2.0-flash")
10
+
11
+ class OpenRouter:
12
+ def __init__(self, model_name, key, role="user"):
13
+ self.model_name = model_name
14
+ self.key = key
15
+ self.api_url = "https://openrouter.ai/api/v1/chat/completions"
16
+ self.role = role
17
+
18
+ def get_response(self, prompt):
19
+ headers = {
20
+ "Authorization": f"Bearer {self.key}",
21
+ "Content-Type": "application/json"
22
+ }
23
+ payload = {
24
+ "model": self.model_name,
25
+ "messages": [{"role": self.role, "content": prompt}]
26
+ }
27
+ response = requests.post(self.api_url, headers=headers, data=json.dumps(payload))
28
+ if response.status_code == 200:
29
+ return response.json()["choices"][0]["message"]["content"]
30
+ else:
31
+ raise Exception(f"Request failed: {response.status_code} - {response.text}")
32
+
33
+ def call_openrouter(llm, prompt, retries=5, delay=5):
34
+ for attempt in range(retries):
35
+ try:
36
+ return llm.get_response(prompt)
37
+ except Exception as e:
38
+ print(f"Retry {attempt+1}: {e}")
39
+ time.sleep(delay)
40
+ return "ERROR"
41
+
42
+ def prepare_claude_ranking_prompt(question, correct, distractors):
43
+ distractor_text = "\n".join([f"Choice {i+1}: {d}" for i, d in enumerate(distractors)])
44
+ return f"""You are a helpful assistant evaluating multiple-choice distractors.
45
+
46
+ Question: {question}
47
+ Correct Answer: {correct}
48
+
49
+ Here are the distractors:
50
+ {distractor_text}
51
+
52
+ Your task: Rank the distractors from most to least confusing.
53
+
54
+ Only respond with one line in this format: 2 > 1 > 3
55
+ """
56
+
57
+
58
+ def generate_and_rank(question):
59
+ gemini_prompt = f"""You are an assistant generating multiple-choice questions.
60
+
61
+ Given a question, generate:
62
+ - One correct answer
63
+ - Three plausible but incorrect distractors
64
+
65
+ Format:
66
+ Correct Answer: ...
67
+ Distractor 1: ...
68
+ Distractor 2: ...
69
+ Distractor 3: ...
70
+
71
+ Question: {question}
72
+ """
73
+ gemini_response = gemini_model.generate_content(gemini_prompt).text.strip()
74
+
75
+ lines = [line.strip() for line in gemini_response.splitlines() if line.strip()]
76
+ correct = next((line.split(":", 1)[1].strip() for line in lines if line.startswith("Correct Answer")), "N/A")
77
+ distractors = [line.split(":", 1)[1].strip() for line in lines if line.startswith("Distractor")]
78
+
79
+ if len(distractors) != 3:
80
+ return "Error: Could not extract exactly 3 distractors.", gemini_response, ""
81
+
82
+ claude_llm = OpenRouter("anthropic/claude-3.5-sonnet-20240612", "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")
83
+ claude_prompt = prepare_claude_ranking_prompt(question, correct, distractors)
84
+ ranking = call_openrouter(claude_llm, claude_prompt)
85
+
86
+ output = f"**Question:** {question}\n\n"
87
+ output += f"**Correct Answer:** {correct}\n"
88
+ for i, d in enumerate(distractors, 1):
89
+ output += f"**Distractor {i}:** {d}\n"
90
+ output += f"\n**Claude Ranking:** {ranking.strip()}"
91
+
92
+ return output, gemini_response, claude_prompt
93
+
94
+
95
+ iface = gr.Interface(
96
+ fn=generate_and_rank,
97
+ inputs=gr.Textbox(label="Enter your MCQ Question"),
98
+ outputs=[
99
+ gr.Textbox(label="Final Output"),
100
+ gr.Textbox(label="Raw Gemini Output"),
101
+ gr.Textbox(label="Claude Prompt")
102
+ ],
103
+ title="Confusing Distractor Generator + Claude Ranking"
104
+ )
105
+
106
+ iface.launch()
requirements.txt ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ google-generativeai>=0.3.2
2
+ gradio>=4.28.3
3
+ python-dotenv
4
+ requests