mj-learn-backend / generateQuestion.py
pykara's picture
Update generateQuestion.py
fc01712 verified
raw
history blame
14.5 kB
# from flask import Flask, Blueprint, jsonify, send_file, abort, make_response, request
from flask import Flask, Blueprint, jsonify, send_file, abort, make_response, request, current_app
from flask_cors import CORS
import os
print(f"GOOGLE_APPLICATION_CREDENTIALS: {os.getenv('GOOGLE_APPLICATION_CREDENTIALS')}")
import io
import uuid
import requests
import re
questions_bp = Blueprint("questions", __name__)
app = Flask(__name__)
CORS(app)
@app.route('/')
def home():
return "Welcome to the Flask app! The server is running."
# read from env as a fallback (works for local .env and HF Secrets)
COHERE_API_KEY = os.getenv("COHERE_API_KEY", "")
def _cohere_headers():
"""
Prefer a key set on the Flask app (e.g., in verification.py: app.config["COHERE_API_KEY"]),
otherwise fall back to the environment variable COHERE_API_KEY.
"""
api_key = current_app.config.get("COHERE_API_KEY") or COHERE_API_KEY
if not api_key:
return None
return {
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json",
}
# (1) UPDATED: Cohere v2 Chat endpoint
COHERE_API_URL = 'https://api.cohere.com/v2/chat'
def _extract_text_v2(resp_json: dict) -> str:
"""
v2 /chat returns:
{ "message": { "content": [ { "type": "text", "text": "..." } ] } }
"""
msg = resp_json.get("message", {})
content = msg.get("content", [])
if isinstance(content, list) and content:
block = content[0]
if isinstance(block, dict):
return (block.get("text") or "").strip()
return ""
def validate_topic(topic):
validation_prompt = f"""
ou are a highly knowledgeable AI grammar expert. Your task is to evaluate whether the given topic relates to **English grammar** or not.
**Input Topic:** "{topic}"
### **Instructions:**
- If the input **exactly refers to** grammar concepts (such as **parts of speech**, **verb tenses**, **sentence structure**, **grammar rules**, etc.), respond with `"Grammar"`.
- If the input **seems to be a general question or concept** that is **not directly related to grammar**, such as general knowledge, science, history, or unrelated fields, respond with `"Not Grammar"`.
- If the input is in the form of a **question** (e.g., "What is subject-verb agreement?"), respond with `"ask grammar topics"`.
- If the topic refers to a **specific grammar concept** (e.g., **noun**, **verb**, **preposition**, **past tense**, etc.), always classify it as `"Grammar"`.
- **Do not include any explanations or examples**. Your answer must only be `"Grammar"`, `"Not Grammar"`, or `"ask grammar topics"`, depending on whether the topic is relevant to grammar.
- If the input is **unclear**, err on the side of classifying it as `"Not Grammar"` rather than `"Grammar"`.
Your response must only be one of these three options:
- `"Grammar"`
- `"Not Grammar"`
- `"ask grammar topics"`
No extra text or explanation.
"""
headers = _cohere_headers()
if not headers:
return "Error: COHERE_API_KEY not set"
# (2) UPDATED: messages payload
payload = {
'model': 'command-r-08-2024',
'messages': [
{'role': 'user', 'content': validation_prompt}
],
'max_tokens': 5
}
try:
response = requests.post(COHERE_API_URL, json=payload, headers=headers)
# (3) UPDATED: v2 parsing
validation_result = _extract_text_v2(response.json())
if validation_result not in ["Grammar", "Not Grammar", "ask grammar topics"]:
return "Not Grammar"
return validation_result
except Exception as e:
return f"Error: {str(e)}"
@questions_bp.post('/generate-questions')
def generate_questions_test():
try:
data = request.get_json()
topic = data.get('topic', '').strip()
validation_result = validate_topic(topic)
if validation_result != "Grammar":
return jsonify({"message": "Please enter a valid **grammar topic**, not a general word or unrelated question."}), 400
difficulty = data.get('difficulty', 'basic')
print(f"Generating {difficulty} questions for topic: {topic}")
if difficulty == 'basic':
prompt = f"""
Generate five **completely new and unique** very basic-level fill-in-the-blank grammar questions **every time** on the topic '{topic}'.
### Rules:
- Generate five unique fill-in-the-blank grammar questions based on the topic '{topic}'.
- Each question must have exactly one blank represented by '_______' (not two blanks or underscores inside the sentence).
- Each question must have a different theme for variety.
- Use different sentence structures; avoid predictable patterns.
- Avoid long words or abstract concepts.
- Focus on the topic '{topic}', and ensure the blank is the key part of speech.
- Each question must include the correct answer in parentheses at the end.
- Do not include any explanations or instructions—only the five questions.
"""
elif difficulty == 'intermediate':
prompt = f"""
Generate five **completely new and unique** intermediate-level fill-in-the-blank grammar questions **every time** on the topic '{topic}'.
### Rules:
- Generate five unique fill-in-the-blank grammar questions based on the topic '{topic}'.
- Each question must have exactly one blank represented by '_______'.
- Slightly more challenging than basic-level; use a wider range of sentence structures and vocabulary.
- Each question must have a different theme.
- Sentences should be longer and include more detail.
- Focus on the topic '{topic}', and ensure the blank is the key part of speech.
- Each question must include the correct answer in parentheses at the end.
- Do not include any explanations or instructions—only the five questions.
"""
elif difficulty == 'expert':
prompt = f"""
Generate five **completely new and unique** advanced-level (C1) fill-in-the-blank grammar questions **every time** on the topic '{topic}'.
### Rules:
- Generate five unique fill-in-the-blank grammar questions based on the topic '{topic}'.
- Each question must have exactly one blank represented by '_______'.
- More challenging than intermediate (C1); require expert-level mastery of grammar and context.
- Ensure varied and sophisticated vocabulary; avoid basic words.
- Each question should require nuanced comprehension; test advanced grammar patterns.
- The blank must be the key part of the sentence (not an obvious answer).
- Each question must include the correct answer in parentheses at the end.
- Do not include any explanations or instructions—only the five questions.
"""
else:
return jsonify({"error": "Invalid difficulty level"}), 400
headers = _cohere_headers()
if not headers:
return jsonify({"error": "COHERE_API_KEY not set on the server"}), 500
# (2) UPDATED: messages payload
payload = {
'model': 'command-r-08-2024',
'messages': [
{'role': 'user', 'content': prompt}
],
'max_tokens': 1000
}
response = requests.post(COHERE_API_URL, json=payload, headers=headers)
print("Response status code:", response.status_code)
print("Response content:", response.text)
if response.status_code == 200:
# (3) UPDATED: v2 parsing
text = _extract_text_v2(response.json())
# Return same style as before but adapted (text content)
return jsonify({"text": text})
else:
return jsonify({"error": "Failed to fetch questions", "details": response.text}), 500
except Exception as e:
return jsonify({"error": str(e)}), 500
@questions_bp.post('/validate-answer')
def validate_answer():
try:
data = request.get_json()
topic = data.get('topic', '')
question = data.get('question', '')
user_answer = data.get('user_answer', '')
if not question or not user_answer or not topic:
return jsonify({'error': 'Topic, question, and user answer are required'}), 400
prompt = f"""
You are a highly knowledgeable grammar assistant. Validate whether the user's answer to the following question is correct or not based on {topic}. If the answer is incorrect, provide a helpful hint.
Topic: {topic}
Question: "{question}"
User's Answer: "{user_answer}"
Is the answer correct? If not, please explain why and give a hint.
"""
headers = _cohere_headers()
if not headers:
return jsonify({"error": "COHERE_API_KEY not set on the server"}), 500
# (2) UPDATED: messages payload
payload = {
'model': 'command-r-08-2024',
'messages': [
{'role': 'user', 'content': prompt}
],
'max_tokens': 100,
'temperature': 0.7
}
response = requests.post(COHERE_API_URL, headers=headers, json=payload)
print(f"Status Code: {response.status_code}")
print(f"Response Body: {response.text}")
if response.status_code == 200:
# (3) UPDATED: v2 parsing
text = _extract_text_v2(response.json())
return jsonify({"text": text})
else:
return jsonify({'error': 'Failed to fetch data from Cohere API'}), 500
except Exception as e:
return jsonify({'error': str(e)}), 500
# Function to validate an individual answer using Cohere API
def validate_answer_with_ai(topic, question, user_answer):
try:
prompt = f"""
You are a highly knowledgeable grammar assistant. Validate whether the user's answer to the following question is correct or not based on {topic}. If the answer is incorrect, provide a helpful hint.
Topic: {topic}
Question: "{question}"
User's Answer: "{user_answer}"
Is the answer correct? If not, please explain why and give a hint.
"""
headers = _cohere_headers()
if not headers:
return "Error: COHERE_API_KEY not set"
# (2) UPDATED: messages payload
payload = {
'model': 'command-r-08-2024',
'messages': [
{'role': 'user', 'content': prompt}
],
'max_tokens': 200,
'temperature': 0.7
}
# (1) UPDATED URL used here too
response = requests.post(COHERE_API_URL, headers=headers, json=payload)
if response.status_code == 200:
# (3) UPDATED: v2 parsing
return _extract_text_v2(response.json())
else:
return f"Error: {response.status_code} - {response.text}"
except Exception as e:
return f"An error occurred: {str(e)}"
@questions_bp.post('/validate-all-answers')
def validate_all_answers():
try:
data = request.get_json()
questions = data.get('questions', [])
if not questions:
return jsonify({'error': 'No questions provided'}), 400
validation_results = []
for item in questions:
topic = item.get('topic', '')
question = item.get('question', '')
user_answer = item.get('user_answer', '')
if not topic or not question or not user_answer:
validation_results.append({
'question': question,
'error': 'Missing required fields (topic, question, or answer).'
})
continue
validation_response = validate_answer_with_ai(topic, question, user_answer)
hint = None
if isinstance(validation_response, str) and (
"incorrect" in validation_response.lower() or "not correct" in validation_response.lower()
):
hint = generate_hint(topic, question, user_answer)
validation_results.append({
'question': question,
'user_answer': user_answer,
'validation_response': validation_response,
'hint': hint
})
return jsonify({'results': validation_results})
except Exception as e:
return jsonify({'error': str(e)}), 500
def generate_hint(topic, question, user_answer):
try:
prompt = f"""
You are a highly skilled grammar assistant. Your task is to generate a helpful hint for the user to improve their answer based on the following question.
Topic: {topic}
Question: "{question}"
User's Answer: "{user_answer}"
If the user's answer is incorrect, provide a specific, actionable hint to help the user correct their answer.
The hint should include:
- Explanation of the error made by the user.
- A hint on the correct grammatical structure or word form.
- A hint on how to structure the sentence correctly **without revealing the exact answer**.
Please make sure the hint is **clear** and **helpful** for the user, **without revealing the correct answer**.
"""
headers = _cohere_headers()
if not headers:
return "Error: COHERE_API_KEY not set"
# (2) UPDATED: messages payload
payload = {
'model': 'command-r-08-2024',
'messages': [
{'role': 'user', 'content': prompt}
],
'max_tokens': 250,
'temperature': 0.7
}
response = requests.post(COHERE_API_URL, headers=headers, json=payload)
if response.status_code == 200:
# (3) UPDATED: v2 parsing
return _extract_text_v2(response.json())
else:
return f"Error: {response.status_code} - {response.text}"
except Exception as e:
return f"An error occurred: {str(e)}"
if __name__ == '__main__':
app.register_blueprint(questions_bp, url_prefix='') # local exposure
app.run(host='0.0.0.0', port=5012, debug=True)