amauricunha commited on
Commit
202e2b1
·
verified ·
1 Parent(s): 4aaf4df

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +51 -29
app.py CHANGED
@@ -41,7 +41,8 @@ except Exception as e:
41
  print(f"ERRO ao inicializar o cliente Groq: {e}.")
42
 
43
 
44
- # --- ROTA 1: TEXT-TO-SPEECH (TTS) ---
 
45
  @app.route('/tts-proxy', methods=['POST'])
46
  def tts_proxy():
47
  data = request.get_json()
@@ -53,57 +54,85 @@ def tts_proxy():
53
  mp3_fp = io.BytesIO()
54
  tts.write_to_fp(mp3_fp)
55
  mp3_fp.seek(0)
56
- return send_file(mp3_fp, mimetype='audio/mpeg', as_attachment=True, download_name='audio.mp3')
57
  except Exception as e:
58
  return jsonify({"error": f"Failed to generate audio via gTTS: {e}"}), 500
59
 
60
-
61
- # --- ROTA 2: ANÁLISE DE TEXTO (FLASHCARDS, ETC.) ---
62
  @app.route('/explain-proxy', methods=['POST'])
63
  def explain_proxy():
64
  data = request.get_json()
65
- # CORREÇÃO: Atualizado o modelo padrão para a versão mais recente
66
  model_provider, model_name = data.get('model', 'gemini:gemini-2.5-flash-latest').split(':', 1)
67
- word, context = data.get('word', '').strip(), data.get('context', '')
68
- for_flashcard, context_focus = data.get('for_flashcard', False), data.get('context_focus', 'General/Social')
69
  custom_prompt = data.get('custom_prompt', None)
 
 
 
70
 
71
  if (model_provider == 'gemini' and not genai_client) or (model_provider == 'groq' and not groq_client):
72
  return jsonify({"error": f"{model_provider.upper()}_API_KEY not configured."}), 503
73
 
74
- system_instruction_base = f"You are a professional English tutor focused on the context '{context_focus}'."
75
  try:
76
- # Se for uma requisição de Geração de Atividade (custom_prompt)
77
  if custom_prompt:
78
- # A resposta será texto simples, então retornamos em um campo JSON
79
  activity_text = get_ai_text_response(model_provider, model_name, system_instruction_base, custom_prompt)
80
- return jsonify({"explanation": activity_text}) # Reutilizando a chave 'explanation'
81
 
82
  if not word: return jsonify({"error": "No word selected."}), 400
83
 
84
- # Lógica para Flashcard
85
  if for_flashcard:
86
  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"]}
87
- prompt = f"Analyze '{word}' in context: '{context}'. Generate a JSON for a flashcard. The 'gapped_sentence' must replace '{word}' with '______________'."
88
  return jsonify(get_ai_text_response(model_provider, model_name, system_instruction_base, prompt, json_schema=schema))
89
 
90
- # Lógica para tradução rápida
91
- else:
92
  prompt = f"Analyze '{word}' in context: '{context}'. Provide a one-sentence English explanation, then '---', then the Portuguese translation."
93
  parts = get_ai_text_response(model_provider, model_name, system_instruction_base, prompt).split('---', 1)
94
  return jsonify({"explanation": parts[0].strip(), "translation": parts[1].strip() if len(parts) > 1 else 'N/A'})
 
95
  except Exception as e:
96
  return jsonify({"error": f"AI analysis failed: {e}"}), 500
97
 
98
- # --- ROTA 3: ANÁLISE DE IMAGEM ---
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
99
  @app.route('/analyze-image', methods=['POST'])
100
  def analyze_image():
101
  if not genai_client: return jsonify({"error": "GEMINI_API_KEY not configured."}), 503
102
  data = request.get_json()
103
  base64_image = data.get('image')
104
- # CORREÇÃO: Pega o modelo do seletor, com um padrão seguro caso não seja um modelo Gemini.
105
  model_value = data.get('model', 'gemini:gemini-2.5-flash-latest')
106
- # Garante que estamos usando um modelo Gemini para análise de imagem
107
  model_name = 'gemini-2.5-flash-latest'
108
  if model_value.startswith('gemini:'):
109
  model_name = model_value.split(':', 1)[1]
@@ -111,16 +140,14 @@ def analyze_image():
111
  if not base64_image: return jsonify({"error": "No image data."}), 400
112
  try:
113
  image = Image.open(io.BytesIO(base64.b64decode(base64_image.split(',')[1])))
114
- # CORREÇÃO: Usa o nome do modelo dinâmico
115
  model = genai_client.GenerativeModel(model_name)
116
  schema = { "type": "object", "properties": { "vocabulary": { "type": "array", "items": { "type": "object", "properties": { "term": {"type": "string"}, "definition": {"type": "string"} }, "required": ["term", "definition"] } } }, "required": ["vocabulary"] }
117
- prompt = [ "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 ]
118
  response = model.generate_content(prompt, generation_config={"response_mime_type": "application/json", "response_schema": schema})
119
  return jsonify(json.loads(response.text)['vocabulary'])
120
  except Exception as e:
121
  return jsonify({"error": f"Image analysis failed: {e}"}), 500
122
 
123
- # --- ROTA 4: CHAT COM IA ---
124
  @app.route('/chat-with-ai', methods=['POST'])
125
  def chat_with_ai():
126
  if not groq_client: return jsonify({"error": "GROQ_API_KEY not configured."}), 503
@@ -130,38 +157,33 @@ def chat_with_ai():
130
  try:
131
  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."
132
  messages = [{"role": "system", "content": system}] + history + [{"role": "user", "content": user_message}]
133
- # Mantido fixo para garantir a experiência de baixa latência
134
  response = groq_client.chat.completions.create(model="llama-3.1-8b-instant", messages=messages, temperature=0.7)
135
  return jsonify({"response": response.choices[0].message.content.strip()})
136
  except Exception as e:
137
  return jsonify({"error": f"AI chat failed: {e}"}), 500
138
 
139
- # --- ROTA 5: ANÁLISE DE PRONÚNCIA ---
140
  @app.route('/pronunciation-feedback', methods=['POST'])
141
  def pronunciation_feedback():
142
- if not groq_client: return jsonify({"error": "GROQ_API_KEY not configured for pronunciation analysis."}), 503
143
  data = request.get_json()
144
  target_text, user_text = data.get('target_text'), data.get('user_text')
145
- if not target_text or not user_text: return jsonify({"error": "Target and user text are required."}), 400
146
  try:
147
  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."
148
  user_prompt = f"Target: \"{target_text}\"\nTranscription: \"{user_text}\"\n\nProvide pronunciation feedback."
149
  messages = [{"role": "system", "content": system_instruction}, {"role": "user", "content": user_prompt}]
150
- # Mantido fixo para garantir a experiência de baixa latência
151
  response = groq_client.chat.completions.create(model="llama-3.1-8b-instant", messages=messages, temperature=0.5)
152
  return jsonify({"feedback": response.choices[0].message.content.strip()})
153
  except Exception as e:
154
  return jsonify({"error": f"Pronunciation analysis failed: {e}"}), 500
155
 
156
- # --- ROTA 6: GERAÇÃO DE IMAGEM ---
157
  @app.route('/generate-image', methods=['POST'])
158
  def generate_image():
159
- if not genai_client: return jsonify({"error": "GEMINI_API_KEY not configured for image generation."}), 503
160
  data = request.get_json()
161
  prompt = data.get('prompt')
162
  if not prompt: return jsonify({"error": "Image prompt is required."}), 400
163
  try:
164
- # CORREÇÃO: Atualizado para o modelo de geração de imagem mais recente
165
  model = genai_client.GenerativeModel(model_name='gemini-2.5-flash-image-preview')
166
  response = model.generate_content(prompt)
167
  base64_image_data = response.parts[0].inline_data.data
 
41
  print(f"ERRO ao inicializar o cliente Groq: {e}.")
42
 
43
 
44
+ # --- ROTAS PRINCIPAIS ---
45
+
46
  @app.route('/tts-proxy', methods=['POST'])
47
  def tts_proxy():
48
  data = request.get_json()
 
54
  mp3_fp = io.BytesIO()
55
  tts.write_to_fp(mp3_fp)
56
  mp3_fp.seek(0)
57
+ return send_file(mp3_fp, mimetype='audio/mpeg')
58
  except Exception as e:
59
  return jsonify({"error": f"Failed to generate audio via gTTS: {e}"}), 500
60
 
 
 
61
  @app.route('/explain-proxy', methods=['POST'])
62
  def explain_proxy():
63
  data = request.get_json()
 
64
  model_provider, model_name = data.get('model', 'gemini:gemini-2.5-flash-latest').split(':', 1)
65
+ context_focus = data.get('context_focus', 'General/Social')
 
66
  custom_prompt = data.get('custom_prompt', None)
67
+ word = data.get('word', '').strip()
68
+ context = data.get('context', '')
69
+ for_flashcard = data.get('for_flashcard', False)
70
 
71
  if (model_provider == 'gemini' and not genai_client) or (model_provider == 'groq' and not groq_client):
72
  return jsonify({"error": f"{model_provider.upper()}_API_KEY not configured."}), 503
73
 
74
+ 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."
75
  try:
 
76
  if custom_prompt:
 
77
  activity_text = get_ai_text_response(model_provider, model_name, system_instruction_base, custom_prompt)
78
+ return jsonify({"explanation": activity_text})
79
 
80
  if not word: return jsonify({"error": "No word selected."}), 400
81
 
 
82
  if for_flashcard:
83
  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"]}
84
+ 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."
85
  return jsonify(get_ai_text_response(model_provider, model_name, system_instruction_base, prompt, json_schema=schema))
86
 
87
+ else: # Quick translation logic (not currently used in UI, but kept for potential future use)
 
88
  prompt = f"Analyze '{word}' in context: '{context}'. Provide a one-sentence English explanation, then '---', then the Portuguese translation."
89
  parts = get_ai_text_response(model_provider, model_name, system_instruction_base, prompt).split('---', 1)
90
  return jsonify({"explanation": parts[0].strip(), "translation": parts[1].strip() if len(parts) > 1 else 'N/A'})
91
+
92
  except Exception as e:
93
  return jsonify({"error": f"AI analysis failed: {e}"}), 500
94
 
95
+ # --- NOVA ROTA PARA FEEDBACK DE ATIVIDADES ---
96
+ @app.route('/activity-feedback', methods=['POST'])
97
+ def activity_feedback():
98
+ data = request.get_json()
99
+ model_provider, model_name = data.get('model', 'gemini:gemini-2.5-flash-latest').split(':', 1)
100
+ context_focus = data.get('context_focus', 'General/Social')
101
+ original_prompt = data.get('original_prompt', '')
102
+ user_response = data.get('user_response', '')
103
+
104
+ if not original_prompt or not user_response:
105
+ return jsonify({"error": "Original prompt and user response are required."}), 400
106
+
107
+ if (model_provider == 'gemini' and not genai_client) or (model_provider == 'groq' and not groq_client):
108
+ return jsonify({"error": f"{model_provider.upper()}_API_KEY not configured."}), 503
109
+
110
+ system_instruction = (
111
+ "You are an expert English teacher providing feedback. "
112
+ f"The user's study focus is '{context_focus}'. "
113
+ "Your entire response MUST be in English. "
114
+ "Provide clear, constructive feedback on the user's writing. "
115
+ "Point out grammar, spelling, or style errors. "
116
+ "Offer a corrected or improved version of their text. "
117
+ "Structure your feedback with markdown for clarity (e.g., using ### Corrected Version)."
118
+ )
119
+
120
+ 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."
121
+
122
+ try:
123
+ feedback_text = get_ai_text_response(model_provider, model_name, system_instruction, user_prompt)
124
+ return jsonify({"feedback": feedback_text})
125
+ except Exception as e:
126
+ return jsonify({"error": f"AI feedback failed: {e}"}), 500
127
+
128
+
129
  @app.route('/analyze-image', methods=['POST'])
130
  def analyze_image():
131
  if not genai_client: return jsonify({"error": "GEMINI_API_KEY not configured."}), 503
132
  data = request.get_json()
133
  base64_image = data.get('image')
 
134
  model_value = data.get('model', 'gemini:gemini-2.5-flash-latest')
135
+
136
  model_name = 'gemini-2.5-flash-latest'
137
  if model_value.startswith('gemini:'):
138
  model_name = model_value.split(':', 1)[1]
 
140
  if not base64_image: return jsonify({"error": "No image data."}), 400
141
  try:
142
  image = Image.open(io.BytesIO(base64.b64decode(base64_image.split(',')[1])))
 
143
  model = genai_client.GenerativeModel(model_name)
144
  schema = { "type": "object", "properties": { "vocabulary": { "type": "array", "items": { "type": "object", "properties": { "term": {"type": "string"}, "definition": {"type": "string"} }, "required": ["term", "definition"] } } }, "required": ["vocabulary"] }
145
+ 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 ]
146
  response = model.generate_content(prompt, generation_config={"response_mime_type": "application/json", "response_schema": schema})
147
  return jsonify(json.loads(response.text)['vocabulary'])
148
  except Exception as e:
149
  return jsonify({"error": f"Image analysis failed: {e}"}), 500
150
 
 
151
  @app.route('/chat-with-ai', methods=['POST'])
152
  def chat_with_ai():
153
  if not groq_client: return jsonify({"error": "GROQ_API_KEY not configured."}), 503
 
157
  try:
158
  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."
159
  messages = [{"role": "system", "content": system}] + history + [{"role": "user", "content": user_message}]
 
160
  response = groq_client.chat.completions.create(model="llama-3.1-8b-instant", messages=messages, temperature=0.7)
161
  return jsonify({"response": response.choices[0].message.content.strip()})
162
  except Exception as e:
163
  return jsonify({"error": f"AI chat failed: {e}"}), 500
164
 
 
165
  @app.route('/pronunciation-feedback', methods=['POST'])
166
  def pronunciation_feedback():
167
+ if not groq_client: return jsonify({"error": "GROQ_API_KEY not configured."}), 503
168
  data = request.get_json()
169
  target_text, user_text = data.get('target_text'), data.get('user_text')
170
+ if not target_text or not user_text: return jsonify({"error": "Required data missing."}), 400
171
  try:
172
  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."
173
  user_prompt = f"Target: \"{target_text}\"\nTranscription: \"{user_text}\"\n\nProvide pronunciation feedback."
174
  messages = [{"role": "system", "content": system_instruction}, {"role": "user", "content": user_prompt}]
 
175
  response = groq_client.chat.completions.create(model="llama-3.1-8b-instant", messages=messages, temperature=0.5)
176
  return jsonify({"feedback": response.choices[0].message.content.strip()})
177
  except Exception as e:
178
  return jsonify({"error": f"Pronunciation analysis failed: {e}"}), 500
179
 
 
180
  @app.route('/generate-image', methods=['POST'])
181
  def generate_image():
182
+ if not genai_client: return jsonify({"error": "GEMINI_API_KEY not configured."}), 503
183
  data = request.get_json()
184
  prompt = data.get('prompt')
185
  if not prompt: return jsonify({"error": "Image prompt is required."}), 400
186
  try:
 
187
  model = genai_client.GenerativeModel(model_name='gemini-2.5-flash-image-preview')
188
  response = model.generate_content(prompt)
189
  base64_image_data = response.parts[0].inline_data.data