Spaces:
Sleeping
Sleeping
File size: 11,283 Bytes
81e20b3 0638ab8 3bfff15 96f9398 194f0cb d2083e1 0638ab8 5d57748 194f0cb 96f9398 0638ab8 81e20b3 96f9398 3bfff15 96f9398 db6c3a6 3bfff15 5d57748 194f0cb 3bfff15 194f0cb db6c3a6 3bfff15 96f9398 202e2b1 5d57748 0638ab8 5d57748 01d6342 6b6af6b 202e2b1 5d57748 0638ab8 96f9398 0638ab8 4aaf4df 202e2b1 81e20b3 202e2b1 0638ab8 378b3e3 d2083e1 378b3e3 202e2b1 96f9398 d2083e1 d2b8207 202e2b1 d2b8207 378b3e3 d2b8207 0638ab8 d2b8207 202e2b1 d2b8207 202e2b1 378b3e3 202e2b1 96f9398 0638ab8 202e2b1 3bfff15 378b3e3 01d6342 4aaf4df 202e2b1 4aaf4df 378b3e3 3bfff15 378b3e3 4aaf4df 378b3e3 202e2b1 378b3e3 3bfff15 378b3e3 3bfff15 378b3e3 202e2b1 378b3e3 01d6342 202e2b1 01d6342 3bfff15 01d6342 202e2b1 01d6342 0fa52bc 3bfff15 4aaf4df 01d6342 3bfff15 01d6342 3bfff15 378b3e3 3bfff15 9f9f17f 490f79a 0fa52bc d2b8207 490f79a d2b8207 0fa52bc 3bfff15 378b3e3 d2b8207 378b3e3 3bfff15 0638ab8 81e20b3 5d57748 01d6342 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 | #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 ---
@app.route('/tts-proxy', methods=['POST'])
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
@app.route('/explain-proxy', methods=['POST'])
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 ---
@app.route('/activity-feedback', methods=['POST'])
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
@app.route('/analyze-image', methods=['POST'])
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
@app.route('/chat-with-ai', methods=['POST'])
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
@app.route('/pronunciation-feedback', methods=['POST'])
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
@app.route('/generate-image', methods=['POST'])
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}")
@app.route('/')
def root():
return send_file('index.html')
if __name__ == '__main__':
app.run(host='0.0.0.0', port=7860)
|