# 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)