amauricunha commited on
Commit
c7c44dd
·
verified ·
1 Parent(s): 006bd9f

Delete app.py

Browse files
Files changed (1) hide show
  1. app.py +0 -272
app.py DELETED
@@ -1,272 +0,0 @@
1
- #app.py
2
- import os
3
- import io
4
- import json
5
- import base64
6
- from PIL import Image
7
-
8
- from flask import Flask, request, jsonify, send_file
9
- from gtts import gTTS
10
- from groq import Groq
11
- import google.generativeai as genai
12
- from google.generativeai.types import GenerationConfig
13
-
14
- # --- CONFIGURAÇÃO INICIAL ---
15
- app = Flask(__name__)
16
-
17
- # --- CONFIGURAÇÃO DAS APIS LLM ---
18
- genai_client = None
19
- groq_client = None
20
-
21
- # 1. Configuração Gemini
22
- try:
23
- GEMINI_API_KEY = os.environ.get("GEMINI_API_KEY")
24
- if GEMINI_API_KEY:
25
- genai.configure(api_key=GEMINI_API_KEY)
26
- genai_client = genai
27
- else:
28
- print("AVISO: GEMINI_API_KEY não configurada.")
29
- except Exception as e:
30
- genai_client = None
31
- print(f"ERRO ao inicializar o cliente Gemini: {e}.")
32
-
33
- # 2. Configuração Groq
34
- try:
35
- GROQ_API_KEY = os.environ.get("GROQ_API_KEY")
36
- if GROQ_API_KEY:
37
- groq_client = Groq(api_key=GROQ_API_KEY)
38
- else:
39
- print("AVISO: GROQ_API_KEY não configurada.")
40
- except Exception as e:
41
- groq_client = None
42
- print(f"ERRO ao inicializar o cliente Groq: {e}.")
43
-
44
-
45
- # --- ROTA PARA LISTAR MODELOS DINAMICAMENTE ---
46
- @app.route('/list-models')
47
- def list_models():
48
- available_models = []
49
- groq_text_models = [
50
- "llama-3.1-8b-instant",
51
- "llama-3.3-70b-versatile",
52
- "openai/gpt-oss-120b",
53
- "openai/gpt-oss-20b"
54
- ]
55
- try:
56
- if genai_client:
57
- for m in genai_client.list_models():
58
- if 'generateContent' in m.supported_generation_methods:
59
- model_name = m.name.replace("models/", "")
60
- if "flash" in model_name or "pro" in model_name:
61
- available_models.append({
62
- "value": f"gemini:{model_name}",
63
- "name": m.display_name
64
- })
65
- if groq_client:
66
- for model_id in groq_text_models:
67
- display_name = model_id.split('/')[-1].replace('-instant', '').replace('-versatile', '')
68
- available_models.append({
69
- "value": f"groq:{model_id}",
70
- "name": f"Groq: {display_name}"
71
- })
72
- except Exception as e:
73
- print(f"Erro ao listar modelos: {e}")
74
- return jsonify([
75
- {"value": "gemini:gemini-2.5-flash-latest", "name": "Gemini 2.5 Flash (Fallback)"},
76
- {"value": "groq:llama-3.1-8b-instant", "name": "Llama 3.1 8B (Fallback)"}
77
- ])
78
- return jsonify(available_models)
79
-
80
-
81
- # --- ROTAS PRINCIPAIS ---
82
-
83
- @app.route('/tts-proxy', methods=['POST'])
84
- def tts_proxy():
85
- data = request.get_json()
86
- text = data.get('text', '')
87
- tld = data.get('tld', 'co.uk')
88
- if not text: return jsonify({"error": "No text provided"}), 400
89
- try:
90
- tts = gTTS(text=text, lang='en', tld=tld)
91
- mp3_fp = io.BytesIO()
92
- tts.write_to_fp(mp3_fp)
93
- mp3_fp.seek(0)
94
- return send_file(mp3_fp, mimetype='audio/mpeg')
95
- except Exception as e:
96
- return jsonify({"error": f"Failed to generate audio via gTTS: {e}"}), 500
97
-
98
- @app.route('/explain-proxy', methods=['POST'])
99
- def explain_proxy():
100
- data = request.get_json()
101
- model_provider, model_name = data.get('model', 'gemini:gemini-2.5-flash-latest').split(':', 1)
102
- context_focus = data.get('context_focus', 'General/Social')
103
- custom_prompt = data.get('custom_prompt', None)
104
- word = data.get('word', '').strip()
105
- context = data.get('context', '')
106
- for_flashcard = data.get('for_flashcard', False)
107
-
108
- if (model_provider == 'gemini' and not genai_client) or (model_provider == 'groq' and not groq_client):
109
- return jsonify({"error": f"{model_provider.upper()}_API_KEY not configured."}), 503
110
-
111
- 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."
112
- try:
113
- if custom_prompt:
114
- activity_text = get_ai_text_response(model_provider, model_name, system_instruction_base, custom_prompt)
115
- return jsonify({"explanation": activity_text})
116
-
117
- if not word: return jsonify({"error": "No word selected."}), 400
118
-
119
- if for_flashcard:
120
- 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"]}
121
- prompt = f"Analyze '{word}' in context: '{context}'. Generate a JSON for a flashcard. The 'gapped_sentence' must replace '{word}' with '______________'."
122
- return jsonify(get_ai_text_response(model_provider, model_name, system_instruction_base, prompt, json_schema=schema))
123
- else:
124
- prompt = f"Analyze '{word}' in context: '{context}'. Provide a one-sentence English explanation, then '---', then the Portuguese translation."
125
- parts = get_ai_text_response(model_provider, model_name, system_instruction_base, prompt).split('---', 1)
126
- return jsonify({"explanation": parts[0].strip(), "translation": parts[1].strip() if len(parts) > 1 else 'N/A'})
127
- except Exception as e:
128
- print(f"AI ANALYSIS ERROR in /explain-proxy: {e}")
129
- return jsonify({"error": f"AI analysis failed: {e}"}), 500
130
-
131
- @app.route('/activity-feedback', methods=['POST'])
132
- def activity_feedback():
133
- data = request.get_json()
134
- model_provider, model_name = data.get('model', 'gemini:gemini-2.5-flash-latest').split(':', 1)
135
- context_focus = data.get('context_focus', 'General/Social')
136
- original_prompt = data.get('original_prompt', '')
137
- user_response = data.get('user_response', '')
138
-
139
- if not original_prompt or not user_response: return jsonify({"error": "Original prompt and user response are required."}), 400
140
- if (model_provider == 'gemini' and not genai_client) or (model_provider == 'groq' and not groq_client):
141
- return jsonify({"error": f"{model_provider.upper()}_API_KEY not configured."}), 503
142
-
143
- system_instruction = (
144
- "You are an expert English teacher providing feedback. "
145
- f"The user's study focus is '{context_focus}'. "
146
- "Your entire response MUST be in English. "
147
- "Provide clear, constructive feedback on the user's writing. "
148
- "Point out grammar, spelling, or style errors. "
149
- "Offer a corrected or improved version of their text. "
150
- "Structure your feedback with markdown for clarity (e.g., using ### Corrected Version)."
151
- )
152
- 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."
153
- try:
154
- feedback_text = get_ai_text_response(model_provider, model_name, system_instruction, user_prompt)
155
- return jsonify({"feedback": feedback_text})
156
- except Exception as e:
157
- return jsonify({"error": f"AI feedback failed: {e}"}), 500
158
-
159
- @app.route('/analyze-image', methods=['POST'])
160
- def analyze_image():
161
- if not genai_client: return jsonify({"error": "GEMINI_API_KEY not configured."}), 503
162
- data = request.get_json()
163
- base64_image = data.get('image')
164
- model_value = data.get('model', 'gemini:gemini-2.5-flash-latest')
165
-
166
- model_name = 'gemini-2.5-flash-latest' # Default
167
- if model_value.startswith('gemini:'):
168
- model_name = model_value.split(':', 1)[1]
169
-
170
- if not base64_image: return jsonify({"error": "No image data."}), 400
171
- try:
172
- image = Image.open(io.BytesIO(base64.b64decode(base64_image.split(',')[1])))
173
- model = genai_client.GenerativeModel(model_name)
174
- schema = { "type": "object", "properties": { "vocabulary": { "type": "array", "items": { "type": "object", "properties": { "term": {"type": "string"}, "definition": {"type": "string"} }, "required": ["term", "definition"] } } }, "required": ["vocabulary"] }
175
- 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 ]
176
-
177
- config = GenerationConfig(response_mime_type="application/json", response_schema=schema)
178
- response = model.generate_content(prompt, generation_config=config)
179
-
180
- return jsonify(json.loads(response.text)['vocabulary'])
181
- except Exception as e:
182
- return jsonify({"error": f"Image analysis failed: {e}"}), 500
183
-
184
- @app.route('/chat-with-ai', methods=['POST'])
185
- def chat_with_ai():
186
- if not groq_client: return jsonify({"error": "GROQ_API_KEY not configured."}), 503
187
- data = request.get_json()
188
- history, user_message = data.get('history', []), data.get('message', '')
189
- if not user_message: return jsonify({"error": "No message."}), 400
190
- try:
191
- 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."
192
- messages = [{"role": "system", "content": system}] + history + [{"role": "user", "content": user_message}]
193
- response = groq_client.chat.completions.create(model="llama-3.1-8b-instant", messages=messages, temperature=0.7)
194
- return jsonify({"response": response.choices[0].message.content.strip()})
195
- except Exception as e:
196
- return jsonify({"error": f"AI chat failed: {e}"}), 500
197
-
198
- @app.route('/pronunciation-feedback', methods=['POST'])
199
- def pronunciation_feedback():
200
- if not groq_client: return jsonify({"error": "GROQ_API_KEY not configured."}), 503
201
- data = request.get_json()
202
- target_text, user_text = data.get('target_text'), data.get('user_text')
203
- if not target_text or not user_text: return jsonify({"error": "Required data missing."}), 400
204
- try:
205
- 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."
206
- user_prompt = f"Target: \"{target_text}\"\nTranscription: \"{user_text}\"\n\nProvide pronunciation feedback."
207
- messages = [{"role": "system", "content": system_instruction}, {"role": "user", "content": user_prompt}]
208
- response = groq_client.chat.completions.create(model="llama-3.1-8b-instant", messages=messages, temperature=0.5)
209
- return jsonify({"feedback": response.choices[0].message.content.strip()})
210
- except Exception as e:
211
- return jsonify({"error": f"Pronunciation analysis failed: {e}"}), 500
212
-
213
- @app.route('/generate-image', methods=['POST'])
214
- def generate_image():
215
- if not genai_client: return jsonify({"error": "GEMINI_API_KEY not configured."}), 503
216
- data = request.get_json()
217
- prompt = data.get('prompt')
218
- if not prompt: return jsonify({"error": "Image prompt is required."}), 400
219
- try:
220
- model = genai_client.GenerativeModel(model_name='gemini-2.5-flash-image-preview')
221
- response = model.generate_content(prompt)
222
- base64_image_data = response.parts[0].inline_data.data
223
- return jsonify({"image_base64": base64_image_data})
224
- except Exception as e:
225
- return jsonify({"error": f"Image generation failed: {e}"}), 500
226
-
227
- # --- FUNÇÃO AUXILIAR E ROTA RAIZ ---
228
- def get_ai_text_response(provider, model_name, system_instruction, user_prompt, json_schema=None):
229
- if provider == 'gemini':
230
- model = genai_client.GenerativeModel(model_name, system_instruction=system_instruction)
231
-
232
- config = None
233
- if json_schema:
234
- config = GenerationConfig(response_mime_type="application/json", response_schema=json_schema)
235
-
236
- response = model.generate_content(user_prompt, generation_config=config)
237
-
238
- if json_schema:
239
- parsed_json = json.loads(response.text)
240
- required_keys = json_schema.get("required", [])
241
- if not all(key in parsed_json and parsed_json[key] for key in required_keys):
242
- raise ValueError(f"AI response missing required keys or has empty values.")
243
- return parsed_json
244
- else:
245
- return response.text.strip()
246
-
247
- elif provider == 'groq':
248
- final_user_prompt = user_prompt
249
- if json_schema:
250
- final_user_prompt += f"\n\nYou MUST respond with a single JSON object that strictly follows this schema. Do not add any other text before or after the JSON object:\n{json.dumps(json_schema)}"
251
-
252
- messages = [{"role": "system", "content": system_instruction}, {"role": "user", "content": final_user_prompt}]
253
- config = {'response_format': {"type": "json_object"}} if json_schema else {}
254
- response = groq_client.chat.completions.create(model=model_name, messages=messages, **config)
255
-
256
- if json_schema:
257
- parsed_json = json.loads(response.choices[0].message.content)
258
- required_keys = json_schema.get("required", [])
259
- if not all(key in parsed_json and parsed_json[key] for key in required_keys):
260
- raise ValueError(f"AI response missing required keys or has empty values.")
261
- return parsed_json
262
- else:
263
- return response.choices[0].message.content.strip()
264
-
265
- raise Exception(f"Unsupported provider: {provider}")
266
-
267
- @app.route('/')
268
- def root():
269
- return send_file('index.html')
270
-
271
- if __name__ == '__main__':
272
- app.run(host='0.0.0.0', port=7860)