Oviya commited on
Commit
4cc2cda
·
1 Parent(s): 936df9d

add vocabularybuilder

Browse files
Files changed (2) hide show
  1. verification.py +2 -0
  2. vocabularyBuilder.py +329 -0
verification.py CHANGED
@@ -308,10 +308,12 @@ from chat import movie_bp # ensure testmovie.py defines movie_bp = Blueprint(..
308
  from generateQuestion import questions_bp
309
  from reading import reading_bp
310
  from writting import writting_bp # match the exact file name on Linux
 
311
  app.register_blueprint(movie_bp, url_prefix="/media")
312
  app.register_blueprint(questions_bp, url_prefix="/media")
313
  app.register_blueprint(reading_bp, url_prefix="/media")
314
  app.register_blueprint(writting_bp, url_prefix="/media")
 
315
  # app.register_blueprint(questions_bp, url_prefix="/media") # <-- add this
316
  # ------------------------------------------------------------------------------
317
  # Local run (Gunicorn will import `verification:app` on Spaces)
 
308
  from generateQuestion import questions_bp
309
  from reading import reading_bp
310
  from writting import writting_bp # match the exact file name on Linux
311
+ from vocabularyBuilder import vocab_bp
312
  app.register_blueprint(movie_bp, url_prefix="/media")
313
  app.register_blueprint(questions_bp, url_prefix="/media")
314
  app.register_blueprint(reading_bp, url_prefix="/media")
315
  app.register_blueprint(writting_bp, url_prefix="/media")
316
+ app.register_blueprint(vocab_bp, url_prefix="/media")
317
  # app.register_blueprint(questions_bp, url_prefix="/media") # <-- add this
318
  # ------------------------------------------------------------------------------
319
  # Local run (Gunicorn will import `verification:app` on Spaces)
vocabularyBuilder.py ADDED
@@ -0,0 +1,329 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from flask import Flask, Blueprint, jsonify, request, current_app
2
+ from flask_cors import CORS
3
+ import openai
4
+ import os
5
+ import random
6
+ import requests
7
+ import json # Add this at the top
8
+ import logging
9
+
10
+ # ---------- Blueprint ----------
11
+ vocab_bp = Blueprint("vocab", __name__)
12
+
13
+ logging.basicConfig(
14
+ filename='app.log',
15
+ level=logging.DEBUG, # Use DEBUG for detailed logs during development
16
+ format='%(asctime)s - %(levelname)s - %(message)s'
17
+ )
18
+
19
+
20
+
21
+ app = Flask(__name__)
22
+ CORS(app)
23
+
24
+ # OpenAI API Key for AI-based validation
25
+ # openai.api_key = "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"
26
+
27
+ # Cohere API Configuration (for generating vocabulary words)
28
+ # COHERE_API_KEY = "WgEAxQyfUXoC3mTTywigL8kNmBH0CmCFtvhdpnC0"
29
+ COHERE_API_URL = "https://api.cohere.ai/v1/generate"
30
+ _OPENAI_API_KEY_FALLBACK = os.getenv("OPENAI_API_KEY", "")
31
+ _COHERE_API_KEY_FALLBACK = os.getenv("COHERE_API_KEY", "")
32
+
33
+ def _ensure_openai_key():
34
+ """Set openai.api_key from Flask config or env before each API call."""
35
+ api_key = (current_app.config.get("OPENAI_API_KEY") if current_app else None) or _OPENAI_API_KEY_FALLBACK
36
+ if api_key:
37
+ openai.api_key = api_key
38
+
39
+ def _cohere_headers():
40
+ """Headers for Cohere API, reading key from Flask config or env."""
41
+ api_key = (current_app.config.get("COHERE_API_KEY") if current_app else None) or _COHERE_API_KEY_FALLBACK
42
+ if not api_key:
43
+ return None
44
+ return {
45
+ "Authorization": f"Bearer {api_key}",
46
+ "Content-Type": "application/json"
47
+ }
48
+
49
+ @vocab_bp.route('/') # redirect to /vocapp (same behaviour)
50
+ # @app.route('/') #Add this route
51
+ def root():
52
+ return redirect(url_for('home'))
53
+
54
+ @vocab_bp.route('/vocapp')
55
+ # @app.route('/vocapp')
56
+ def home():
57
+ return "Welcome to the Flask app! The server is running."
58
+
59
+ @vocab_bp.route('/generate-word-association', methods=['GET'])
60
+ # @app.route('/generate-word-association', methods=['GET'])
61
+ def generate_word_association():
62
+ try:
63
+ import re
64
+
65
+ # -----------------------------
66
+ # 1️⃣ Generate Vocabulary Word + 3 Related Words
67
+ # -----------------------------
68
+ prompt_related = """
69
+ Generate a simple vocabulary word and three related words.
70
+ Return only the JSON output. Do not include any explanation.
71
+
72
+ Format:
73
+ {
74
+ "word": "<main_word>",
75
+ "options": ["word1", "word2", "word3"]
76
+ }
77
+ """
78
+ headers = _cohere_headers()
79
+ if not headers:
80
+ return jsonify({"error": "COHERE_API_KEY not set"}), 500
81
+
82
+ # headers = {
83
+ # "Authorization": f"Bearer {COHERE_API_KEY}",
84
+ # "Content-Type": "application/json"
85
+ # }
86
+
87
+ data_related = {
88
+ "model": "command-r-08-2024",
89
+ "prompt": prompt_related,
90
+ "max_tokens": 100,
91
+ "temperature": 1.0,
92
+ "stop_sequences": ["}"]
93
+ }
94
+
95
+ response_related = requests.post(COHERE_API_URL, json=data_related, headers=headers)
96
+
97
+ if response_related.status_code != 200 or not response_related.text.strip():
98
+ return jsonify({"error": "Failed to generate vocabulary."}), 500
99
+
100
+ raw_text = response_related.json().get("generations", [{}])[0].get("text", "").strip()
101
+ match = re.search(r'\{.*\}', raw_text, re.DOTALL)
102
+ if not match:
103
+ return jsonify({"error": "Invalid JSON from related words API"}), 500
104
+
105
+ json_str = match.group(0)
106
+ related_data = json.loads(json_str)
107
+
108
+ word = related_data.get("word", "").strip()
109
+ related_options = related_data.get("options", [])
110
+
111
+ if len(related_options) < 3:
112
+ return jsonify({"error": "Not enough related words"}), 500
113
+
114
+ # -----------------------------
115
+ # 2️⃣ Generate 2 Unrelated Words
116
+ # -----------------------------
117
+ prompt_unrelated = f"""
118
+ Generate two random words that are NOT related to '{word}' in meaning.
119
+ The words should belong to a different category.
120
+ Return only the JSON output:
121
+ {{
122
+ "options": ["word1", "word2"]
123
+ }}
124
+ """
125
+
126
+ data_unrelated = {
127
+ "model": "command-r-08-2024",
128
+ "prompt": prompt_unrelated,
129
+ "max_tokens": 50,
130
+ "temperature": 1.0,
131
+ "stop_sequences": ["}"]
132
+ }
133
+
134
+ response_unrelated = requests.post(COHERE_API_URL, json=data_unrelated, headers=headers)
135
+ raw_unrelated_text = response_unrelated.json().get("generations", [{}])[0].get("text", "").strip()
136
+ match_unrelated = re.search(r'\{.*\}', raw_unrelated_text, re.DOTALL)
137
+ json_str_unrelated = match_unrelated.group(0)
138
+ unrelated_options = json.loads(json_str_unrelated).get("options", [])
139
+
140
+ if len(unrelated_options) < 2:
141
+ return jsonify({"error": "Not enough unrelated words"}), 500
142
+
143
+ # -----------------------------
144
+ # 3️⃣ Combine & Shuffle Options
145
+ # -----------------------------
146
+ all_options = related_options + unrelated_options
147
+ random.shuffle(all_options)
148
+
149
+ # -----------------------------
150
+ # 4️⃣ Generate Image for the Word
151
+ # -----------------------------
152
+ prompt_image = (
153
+ f"A conceptual, symbolic, high-quality illustration representing the meaning of the word '{word}'. "
154
+ "Do not use text. The image should reflect the emotional or logical meaning of the word."
155
+ )
156
+
157
+ try:
158
+ image_response = openai.images.generate(
159
+ model="dall-e-3",
160
+ prompt=prompt_image,
161
+ n=1,
162
+ size="1024x1024"
163
+ )
164
+ image_url = image_response.data[0].url
165
+ except Exception as img_err:
166
+ logging.error("Image generation failed: %s", str(img_err))
167
+ image_url = ""
168
+
169
+ # -----------------------------
170
+ # ✅ Return All Data
171
+ # -----------------------------
172
+ return jsonify({
173
+ "word": word,
174
+ "options": all_options,
175
+ "correctOptions": related_options,
176
+ "image_url": image_url
177
+ }), 200
178
+
179
+ except Exception as e:
180
+ logging.error("Error in generate_word_association: %s", str(e))
181
+ return jsonify({"error": f"Internal Server Error: {str(e)}"}), 500
182
+
183
+ # ----------------------------
184
+ # 2️⃣ Validate User's Selected Words (Check Correct Answers)
185
+ # ----------------------------
186
+
187
+ @vocab_bp.route('/validate-selection', methods=['POST'])
188
+ # @app.route('/validate-selection', methods=['POST'])
189
+ def validate_selection():
190
+ try:
191
+ data = request.json
192
+ question_word = data.get("word")
193
+ selected_words = data.get("selected_words")
194
+ all_options = data.get("all_options")
195
+
196
+ if not question_word or not selected_words or not all_options:
197
+ return jsonify({"error": "Missing word, selections, or full option list"}), 400
198
+
199
+ validation_prompt = f"""
200
+ The main word is '{question_word}'.
201
+
202
+ For each of the following words, evaluate whether it is logically associated with the main word.
203
+ Provide:
204
+ - A boolean value: true if it is associated, false otherwise.
205
+ - A brief explanation of why it is or isn't associated.
206
+
207
+ Words to evaluate: {all_options}
208
+
209
+ Return the response as JSON in this format:
210
+ {{
211
+ "feedback": [
212
+ {{ "word": "word1", "is_correct": true/false, "reason": "explanation" }},
213
+ ...
214
+ ]
215
+ }}
216
+ """
217
+
218
+ _ensure_openai_key()
219
+ response = openai.chat.completions.create(
220
+ model="gpt-4-turbo",
221
+ messages=[{"role": "user", "content": validation_prompt}],
222
+ max_tokens=500,
223
+ temperature=0.5
224
+ )
225
+
226
+ feedback_text = response.choices[0].message.content.strip()
227
+
228
+ # Try parsing directly as JSON
229
+ try:
230
+ feedback_json = json.loads(feedback_text)
231
+ structured_feedback = feedback_json.get("feedback", [])
232
+ except json.JSONDecodeError:
233
+ # If markdown or formatting exists, clean it and retry
234
+ feedback_text = feedback_text.replace("```json", "").replace("```", "").strip()
235
+ feedback_json = json.loads(feedback_text)
236
+ structured_feedback = feedback_json.get("feedback", [])
237
+
238
+ # Sanity check
239
+ if not isinstance(structured_feedback, list):
240
+ return jsonify({"error": "Unexpected feedback format from AI."}), 500
241
+
242
+ correct_answers = [entry.get("word") for entry in structured_feedback if entry.get("is_correct")]
243
+
244
+ return jsonify({
245
+ "feedback": structured_feedback,
246
+ "correctAnswers": correct_answers
247
+ }), 200
248
+
249
+ except Exception as e:
250
+ print(f"Error validating selection: {e}")
251
+ return jsonify({"error": "An error occurred while validating the selection."}), 500
252
+
253
+ # ----------------------------
254
+ # 3️⃣ Validate User's Sentence Using AI
255
+ # ----------------------------
256
+ @vocab_bp.route('/validate-sentence', methods=['POST'])
257
+ # @app.route('/validate-sentence', methods=['POST'])
258
+ def validate_sentence():
259
+ try:
260
+ data = request.json
261
+ sentence = data.get("sentence")
262
+ selected_words = data.get("selected_words")
263
+
264
+ if not sentence or not selected_words:
265
+ return jsonify({"error": "Sentence and selected words are required"}), 400
266
+
267
+ validation_prompt = f"""
268
+ Evaluate the following sentence for grammar, clarity, and correctness.
269
+ Ensure that the selected words {selected_words} are used correctly.
270
+ If the sentence is incorrect, suggest improvements.
271
+ Sentence: '{sentence}'
272
+ """
273
+ _ensure_openai_key()
274
+ response = openai.chat.completions.create(
275
+ model="gpt-4-turbo",
276
+ messages=[{"role": "user", "content": validation_prompt}],
277
+ max_tokens=500,
278
+ temperature=0.7
279
+ )
280
+
281
+ feedback = response.choices[0].message.content.strip()
282
+ return jsonify({"feedback": feedback}), 200
283
+
284
+ except Exception as e:
285
+ print(f"Error validating sentence: {e}")
286
+ return jsonify({"error": "An error occurred while validating the sentence."}), 500
287
+
288
+ @vocab_bp.route('/generate-image', methods=['POST'])
289
+ # @app.route('/generate-image', methods=['POST'])
290
+ def generate_image():
291
+ try:
292
+ data = request.json
293
+ word = data.get("word")
294
+
295
+ if not word:
296
+ return jsonify({"error": "Word is required to generate image"}), 400
297
+
298
+ # Adjust prompt to create meaningful, visual examples of the word
299
+ prompt = (
300
+ f"A conceptual, high-quality illustration that visually explains the word '{word}'. "
301
+ "Use realistic or symbolic elements to represent its meaning clearly. No text in the image."
302
+ )
303
+
304
+ _ensure_openai_key()
305
+ response = openai.images.generate(
306
+ model="dall-e-3", # or "dall-e-2" if you don’t have access
307
+ prompt=prompt,
308
+ n=1,
309
+ size="1024x1024"
310
+ )
311
+
312
+ image_url = response.data[0].url
313
+ return jsonify({"image_url": image_url}), 200
314
+
315
+ except Exception as e:
316
+ return jsonify({"error": str(e)}), 500
317
+
318
+
319
+ # if __name__ == '__main__':
320
+ # app.run(host='0.0.0.0', port=5002, debug=True)
321
+
322
+ # ---------- Standalone (local testing) ----------
323
+ if __name__ == '__main__':
324
+ app = Flask(__name__)
325
+ CORS(app)
326
+ app.config["OPENAI_API_KEY"] = os.getenv("OPENAI_API_KEY", "")
327
+ app.config["COHERE_API_KEY"] = os.getenv("COHERE_API_KEY", "")
328
+ app.register_blueprint(vocab_bp, url_prefix='')
329
+ app.run(host='0.0.0.0', port=5002, debug=True)