Spaces:
Sleeping
Sleeping
| #app.py | |
| import os | |
| import io | |
| import json | |
| import base64 | |
| from PIL import Image | |
| from flask import Flask, request, jsonify, send_file | |
| from gtts import gTTS | |
| from groq import Groq | |
| import google.generativeai as genai | |
| # --- CONFIGURAÇÃO INICIAL --- | |
| app = Flask(__name__) | |
| # --- CONFIGURAÇÃO DAS APIS LLM --- | |
| genai_client = None | |
| groq_client = None | |
| # 1. Configuração Gemini | |
| try: | |
| GEMINI_API_KEY = os.environ.get("GEMINI_API_KEY") | |
| if GEMINI_API_KEY: | |
| genai.configure(api_key=GEMINI_API_KEY) | |
| genai_client = genai | |
| else: | |
| print("AVISO: GEMINI_API_KEY não configurada.") | |
| except Exception as e: | |
| genai_client = None | |
| print(f"ERRO ao inicializar o cliente Gemini: {e}.") | |
| # 2. Configuração Groq | |
| try: | |
| GROQ_API_KEY = os.environ.get("GROQ_API_KEY") | |
| if GROQ_API_KEY: | |
| groq_client = Groq(api_key=GROQ_API_KEY) | |
| else: | |
| print("AVISO: GROQ_API_KEY não configurada.") | |
| except Exception as e: | |
| groq_client = None | |
| print(f"ERRO ao inicializar o cliente Groq: {e}.") | |
| # --- ROTAS PRINCIPAIS --- | |
| def tts_proxy(): | |
| data = request.get_json() | |
| text = data.get('text', '') | |
| if not text: | |
| return jsonify({"error": "No text provided"}), 400 | |
| try: | |
| tts = gTTS(text=text, lang='en', tld='co.uk') | |
| mp3_fp = io.BytesIO() | |
| tts.write_to_fp(mp3_fp) | |
| mp3_fp.seek(0) | |
| return send_file(mp3_fp, mimetype='audio/mpeg') | |
| except Exception as e: | |
| return jsonify({"error": f"Failed to generate audio via gTTS: {e}"}), 500 | |
| def explain_proxy(): | |
| data = request.get_json() | |
| model_provider, model_name = data.get('model', 'gemini:gemini-2.5-flash-latest').split(':', 1) | |
| context_focus = data.get('context_focus', 'General/Social') | |
| custom_prompt = data.get('custom_prompt', None) | |
| word = data.get('word', '').strip() | |
| context = data.get('context', '') | |
| for_flashcard = data.get('for_flashcard', False) | |
| if (model_provider == 'gemini' and not genai_client) or (model_provider == 'groq' and not groq_client): | |
| return jsonify({"error": f"{model_provider.upper()}_API_KEY not configured."}), 503 | |
| system_instruction_base = f"You are a professional English tutor. The user's study focus is '{context_focus}'. All your responses must be in ENGLISH." | |
| try: | |
| if custom_prompt: | |
| activity_text = get_ai_text_response(model_provider, model_name, system_instruction_base, custom_prompt) | |
| return jsonify({"explanation": activity_text}) | |
| if not word: return jsonify({"error": "No word selected."}), 400 | |
| if for_flashcard: | |
| schema = {"type": "object", "properties": {"term": {"type": "string"}, "translation": {"type": "string"}, "context_sentence": {"type": "string"}, "gapped_sentence": {"type": "string"}, "definition": {"type": "string"}}, "required": ["term", "translation", "context_sentence", "gapped_sentence", "definition"]} | |
| prompt = f"Analyze '{word}' in context: '{context}'. Generate a JSON for a flashcard. The 'gapped_sentence' must replace '{word}' with '______________'. You must strictly follow the JSON schema and provide valid, non-empty values for all fields." | |
| return jsonify(get_ai_text_response(model_provider, model_name, system_instruction_base, prompt, json_schema=schema)) | |
| else: # Quick translation logic (not currently used in UI, but kept for potential future use) | |
| prompt = f"Analyze '{word}' in context: '{context}'. Provide a one-sentence English explanation, then '---', then the Portuguese translation." | |
| parts = get_ai_text_response(model_provider, model_name, system_instruction_base, prompt).split('---', 1) | |
| return jsonify({"explanation": parts[0].strip(), "translation": parts[1].strip() if len(parts) > 1 else 'N/A'}) | |
| except Exception as e: | |
| return jsonify({"error": f"AI analysis failed: {e}"}), 500 | |
| # --- NOVA ROTA PARA FEEDBACK DE ATIVIDADES --- | |
| def activity_feedback(): | |
| data = request.get_json() | |
| model_provider, model_name = data.get('model', 'gemini:gemini-2.5-flash-latest').split(':', 1) | |
| context_focus = data.get('context_focus', 'General/Social') | |
| original_prompt = data.get('original_prompt', '') | |
| user_response = data.get('user_response', '') | |
| if not original_prompt or not user_response: | |
| return jsonify({"error": "Original prompt and user response are required."}), 400 | |
| if (model_provider == 'gemini' and not genai_client) or (model_provider == 'groq' and not groq_client): | |
| return jsonify({"error": f"{model_provider.upper()}_API_KEY not configured."}), 503 | |
| system_instruction = ( | |
| "You are an expert English teacher providing feedback. " | |
| f"The user's study focus is '{context_focus}'. " | |
| "Your entire response MUST be in English. " | |
| "Provide clear, constructive feedback on the user's writing. " | |
| "Point out grammar, spelling, or style errors. " | |
| "Offer a corrected or improved version of their text. " | |
| "Structure your feedback with markdown for clarity (e.g., using ### Corrected Version)." | |
| ) | |
| user_prompt = f"The original task was: \"{original_prompt}\"\n\nHere is the user's response:\n---\n{user_response}\n---\nPlease provide your feedback." | |
| try: | |
| feedback_text = get_ai_text_response(model_provider, model_name, system_instruction, user_prompt) | |
| return jsonify({"feedback": feedback_text}) | |
| except Exception as e: | |
| return jsonify({"error": f"AI feedback failed: {e}"}), 500 | |
| def analyze_image(): | |
| if not genai_client: return jsonify({"error": "GEMINI_API_KEY not configured."}), 503 | |
| data = request.get_json() | |
| base64_image = data.get('image') | |
| model_value = data.get('model', 'gemini:gemini-2.5-flash-latest') | |
| model_name = 'gemini-2.5-flash-latest' | |
| if model_value.startswith('gemini:'): | |
| model_name = model_value.split(':', 1)[1] | |
| if not base64_image: return jsonify({"error": "No image data."}), 400 | |
| try: | |
| image = Image.open(io.BytesIO(base64.b64decode(base64_image.split(',')[1]))) | |
| model = genai_client.GenerativeModel(model_name) | |
| schema = { "type": "object", "properties": { "vocabulary": { "type": "array", "items": { "type": "object", "properties": { "term": {"type": "string"}, "definition": {"type": "string"} }, "required": ["term", "definition"] } } }, "required": ["vocabulary"] } | |
| prompt = [ "Act as an English teacher. Identify 5-7 key objects/concepts in this image. For each, provide its English name and a simple definition. Return a single JSON object conforming to the schema.", image ] | |
| response = model.generate_content(prompt, generation_config={"response_mime_type": "application/json", "response_schema": schema}) | |
| return jsonify(json.loads(response.text)['vocabulary']) | |
| except Exception as e: | |
| return jsonify({"error": f"Image analysis failed: {e}"}), 500 | |
| def chat_with_ai(): | |
| if not groq_client: return jsonify({"error": "GROQ_API_KEY not configured."}), 503 | |
| data = request.get_json() | |
| history, user_message = data.get('history', []), data.get('message', '') | |
| if not user_message: return jsonify({"error": "No message."}), 400 | |
| try: | |
| system = "You are 'Groq Chat', a friendly English tutor. Keep responses concise (1-2 sentences). If the user makes a grammar mistake, gently correct it. Ask questions to keep the conversation flowing. Always respond in English." | |
| messages = [{"role": "system", "content": system}] + history + [{"role": "user", "content": user_message}] | |
| response = groq_client.chat.completions.create(model="llama-3.1-8b-instant", messages=messages, temperature=0.7) | |
| return jsonify({"response": response.choices[0].message.content.strip()}) | |
| except Exception as e: | |
| return jsonify({"error": f"AI chat failed: {e}"}), 500 | |
| def pronunciation_feedback(): | |
| if not groq_client: return jsonify({"error": "GROQ_API_KEY not configured."}), 503 | |
| data = request.get_json() | |
| target_text, user_text = data.get('target_text'), data.get('user_text') | |
| if not target_text or not user_text: return jsonify({"error": "Required data missing."}), 400 | |
| try: | |
| system_instruction = "You are an expert American English pronunciation coach. The user tried to say a target sentence, and their speech was transcribed. Based on the likely pronunciation differences, provide brief, friendly, and actionable feedback in Portuguese. Focus on 1-2 key points. If it's very close, praise the user." | |
| user_prompt = f"Target: \"{target_text}\"\nTranscription: \"{user_text}\"\n\nProvide pronunciation feedback." | |
| messages = [{"role": "system", "content": system_instruction}, {"role": "user", "content": user_prompt}] | |
| response = groq_client.chat.completions.create(model="llama-3.1-8b-instant", messages=messages, temperature=0.5) | |
| return jsonify({"feedback": response.choices[0].message.content.strip()}) | |
| except Exception as e: | |
| return jsonify({"error": f"Pronunciation analysis failed: {e}"}), 500 | |
| def generate_image(): | |
| if not genai_client: return jsonify({"error": "GEMINI_API_KEY not configured."}), 503 | |
| data = request.get_json() | |
| prompt = data.get('prompt') | |
| if not prompt: return jsonify({"error": "Image prompt is required."}), 400 | |
| try: | |
| model = genai_client.GenerativeModel(model_name='gemini-2.5-flash-image-preview') | |
| response = model.generate_content(prompt) | |
| base64_image_data = response.parts[0].inline_data.data | |
| return jsonify({"image_base64": base64_image_data}) | |
| except Exception as e: | |
| return jsonify({"error": f"Image generation failed: {e}"}), 500 | |
| # --- FUNÇÃO AUXILIAR E ROTA RAIZ --- | |
| def get_ai_text_response(provider, model_name, system_instruction, user_prompt, json_schema=None): | |
| if provider == 'gemini': | |
| model = genai_client.GenerativeModel(model_name, system_instruction=system_instruction) | |
| config = {} | |
| if json_schema: | |
| config = {"response_mime_type": "application/json", "response_schema": json_schema} | |
| response = model.generate_content(user_prompt, generation_config=config) | |
| return json.loads(response.text) if json_schema else response.text.strip() | |
| elif provider == 'groq': | |
| messages = [{"role": "system", "content": system_instruction}, {"role": "user", "content": user_prompt}] | |
| config = {'response_format': {"type": "json_object"}} if json_schema else {} | |
| response = groq_client.chat.completions.create(model=model_name, messages=messages, **config) | |
| return json.loads(response.choices[0].message.content) if json_schema else response.choices[0].message.content.strip() | |
| raise Exception(f"Unsupported provider: {provider}") | |
| def root(): | |
| return send_file('index.html') | |
| if __name__ == '__main__': | |
| app.run(host='0.0.0.0', port=7860) | |