Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -41,11 +41,11 @@ except Exception as e:
|
|
| 41 |
print(f"ERRO ao inicializar o cliente Groq: {e}.")
|
| 42 |
|
| 43 |
|
| 44 |
-
# ---
|
| 45 |
@app.route('/list-models')
|
| 46 |
def list_models():
|
| 47 |
available_models = []
|
| 48 |
-
#
|
| 49 |
groq_text_models = [
|
| 50 |
"llama-3.1-8b-instant",
|
| 51 |
"llama-3.3-70b-versatile",
|
|
@@ -54,7 +54,6 @@ def list_models():
|
|
| 54 |
]
|
| 55 |
|
| 56 |
try:
|
| 57 |
-
# Busca modelos Gemini
|
| 58 |
if genai_client:
|
| 59 |
for m in genai_client.list_models():
|
| 60 |
if 'generateContent' in m.supported_generation_methods:
|
|
@@ -65,7 +64,6 @@ def list_models():
|
|
| 65 |
"name": m.display_name
|
| 66 |
})
|
| 67 |
|
| 68 |
-
# Adiciona modelos Groq
|
| 69 |
if groq_client:
|
| 70 |
for model_id in groq_text_models:
|
| 71 |
display_name = model_id.split('/')[-1].replace('-instant', '').replace('-versatile', '')
|
|
@@ -76,7 +74,6 @@ def list_models():
|
|
| 76 |
|
| 77 |
except Exception as e:
|
| 78 |
print(f"Erro ao listar modelos: {e}")
|
| 79 |
-
# Retorna uma lista de fallback em caso de erro na API
|
| 80 |
return jsonify([
|
| 81 |
{"value": "gemini:gemini-2.5-flash-latest", "name": "Gemini 2.5 Flash (Fallback)"},
|
| 82 |
{"value": "groq:llama-3.1-8b-instant", "name": "Llama 3.1 8B (Fallback)"}
|
|
@@ -91,8 +88,7 @@ def list_models():
|
|
| 91 |
def tts_proxy():
|
| 92 |
data = request.get_json()
|
| 93 |
text = data.get('text', '')
|
| 94 |
-
if not text:
|
| 95 |
-
return jsonify({"error": "No text provided"}), 400
|
| 96 |
try:
|
| 97 |
tts = gTTS(text=text, lang='en', tld='co.uk')
|
| 98 |
mp3_fp = io.BytesIO()
|
|
@@ -125,7 +121,7 @@ def explain_proxy():
|
|
| 125 |
|
| 126 |
if for_flashcard:
|
| 127 |
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"]}
|
| 128 |
-
prompt = f"Analyze '{word}' in context: '{context}'. Generate a JSON for a flashcard. The 'gapped_sentence' must replace '{word}' with '______________'.
|
| 129 |
return jsonify(get_ai_text_response(model_provider, model_name, system_instruction_base, prompt, json_schema=schema))
|
| 130 |
|
| 131 |
else:
|
|
@@ -144,8 +140,7 @@ def activity_feedback():
|
|
| 144 |
original_prompt = data.get('original_prompt', '')
|
| 145 |
user_response = data.get('user_response', '')
|
| 146 |
|
| 147 |
-
if not original_prompt or not user_response:
|
| 148 |
-
return jsonify({"error": "Original prompt and user response are required."}), 400
|
| 149 |
|
| 150 |
if (model_provider == 'gemini' and not genai_client) or (model_provider == 'groq' and not groq_client):
|
| 151 |
return jsonify({"error": f"{model_provider.upper()}_API_KEY not configured."}), 503
|
|
@@ -225,7 +220,6 @@ def generate_image():
|
|
| 225 |
prompt = data.get('prompt')
|
| 226 |
if not prompt: return jsonify({"error": "Image prompt is required."}), 400
|
| 227 |
try:
|
| 228 |
-
# Usa um modelo de imagem específico e estável
|
| 229 |
model = genai_client.GenerativeModel(model_name='gemini-2.5-flash-image-preview')
|
| 230 |
response = model.generate_content(prompt)
|
| 231 |
base64_image_data = response.parts[0].inline_data.data
|
|
@@ -236,19 +230,37 @@ def generate_image():
|
|
| 236 |
# --- FUNÇÃO AUXILIAR E ROTA RAIZ ---
|
| 237 |
def get_ai_text_response(provider, model_name, system_instruction, user_prompt, json_schema=None):
|
| 238 |
if provider == 'gemini':
|
| 239 |
-
# Usa o model_name diretamente, que agora vem da lista dinâmica
|
| 240 |
model = genai_client.GenerativeModel(model_name, system_instruction=system_instruction)
|
| 241 |
config = {}
|
| 242 |
if json_schema:
|
| 243 |
config = {"response_mime_type": "application/json", "response_schema": json_schema}
|
| 244 |
response = model.generate_content(user_prompt, generation_config=config)
|
| 245 |
-
return json.loads(response.text) if json_schema else response.text.strip()
|
| 246 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 247 |
elif provider == 'groq':
|
| 248 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 249 |
config = {'response_format': {"type": "json_object"}} if json_schema else {}
|
| 250 |
response = groq_client.chat.completions.create(model=model_name, messages=messages, **config)
|
| 251 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 252 |
raise Exception(f"Unsupported provider: {provider}")
|
| 253 |
|
| 254 |
@app.route('/')
|
|
|
|
| 41 |
print(f"ERRO ao inicializar o cliente Groq: {e}.")
|
| 42 |
|
| 43 |
|
| 44 |
+
# --- ROTA PARA LISTAR MODELOS DINAMICAMENTE ---
|
| 45 |
@app.route('/list-models')
|
| 46 |
def list_models():
|
| 47 |
available_models = []
|
| 48 |
+
# Lista de modelos Groq para geração de texto foi atualizada.
|
| 49 |
groq_text_models = [
|
| 50 |
"llama-3.1-8b-instant",
|
| 51 |
"llama-3.3-70b-versatile",
|
|
|
|
| 54 |
]
|
| 55 |
|
| 56 |
try:
|
|
|
|
| 57 |
if genai_client:
|
| 58 |
for m in genai_client.list_models():
|
| 59 |
if 'generateContent' in m.supported_generation_methods:
|
|
|
|
| 64 |
"name": m.display_name
|
| 65 |
})
|
| 66 |
|
|
|
|
| 67 |
if groq_client:
|
| 68 |
for model_id in groq_text_models:
|
| 69 |
display_name = model_id.split('/')[-1].replace('-instant', '').replace('-versatile', '')
|
|
|
|
| 74 |
|
| 75 |
except Exception as e:
|
| 76 |
print(f"Erro ao listar modelos: {e}")
|
|
|
|
| 77 |
return jsonify([
|
| 78 |
{"value": "gemini:gemini-2.5-flash-latest", "name": "Gemini 2.5 Flash (Fallback)"},
|
| 79 |
{"value": "groq:llama-3.1-8b-instant", "name": "Llama 3.1 8B (Fallback)"}
|
|
|
|
| 88 |
def tts_proxy():
|
| 89 |
data = request.get_json()
|
| 90 |
text = data.get('text', '')
|
| 91 |
+
if not text: return jsonify({"error": "No text provided"}), 400
|
|
|
|
| 92 |
try:
|
| 93 |
tts = gTTS(text=text, lang='en', tld='co.uk')
|
| 94 |
mp3_fp = io.BytesIO()
|
|
|
|
| 121 |
|
| 122 |
if for_flashcard:
|
| 123 |
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"]}
|
| 124 |
+
prompt = f"Analyze '{word}' in context: '{context}'. Generate a JSON for a flashcard. The 'gapped_sentence' must replace '{word}' with '______________'."
|
| 125 |
return jsonify(get_ai_text_response(model_provider, model_name, system_instruction_base, prompt, json_schema=schema))
|
| 126 |
|
| 127 |
else:
|
|
|
|
| 140 |
original_prompt = data.get('original_prompt', '')
|
| 141 |
user_response = data.get('user_response', '')
|
| 142 |
|
| 143 |
+
if not original_prompt or not user_response: return jsonify({"error": "Original prompt and user response are required."}), 400
|
|
|
|
| 144 |
|
| 145 |
if (model_provider == 'gemini' and not genai_client) or (model_provider == 'groq' and not groq_client):
|
| 146 |
return jsonify({"error": f"{model_provider.upper()}_API_KEY not configured."}), 503
|
|
|
|
| 220 |
prompt = data.get('prompt')
|
| 221 |
if not prompt: return jsonify({"error": "Image prompt is required."}), 400
|
| 222 |
try:
|
|
|
|
| 223 |
model = genai_client.GenerativeModel(model_name='gemini-2.5-flash-image-preview')
|
| 224 |
response = model.generate_content(prompt)
|
| 225 |
base64_image_data = response.parts[0].inline_data.data
|
|
|
|
| 230 |
# --- FUNÇÃO AUXILIAR E ROTA RAIZ ---
|
| 231 |
def get_ai_text_response(provider, model_name, system_instruction, user_prompt, json_schema=None):
|
| 232 |
if provider == 'gemini':
|
|
|
|
| 233 |
model = genai_client.GenerativeModel(model_name, system_instruction=system_instruction)
|
| 234 |
config = {}
|
| 235 |
if json_schema:
|
| 236 |
config = {"response_mime_type": "application/json", "response_schema": json_schema}
|
| 237 |
response = model.generate_content(user_prompt, generation_config=config)
|
|
|
|
| 238 |
|
| 239 |
+
parsed_json = json.loads(response.text)
|
| 240 |
+
if json_schema:
|
| 241 |
+
required_keys = json_schema.get("required", [])
|
| 242 |
+
if not all(key in parsed_json and parsed_json[key] for key in required_keys):
|
| 243 |
+
raise ValueError(f"AI response missing required keys. Required: {required_keys}, Got: {list(parsed_json.keys())}")
|
| 244 |
+
return parsed_json
|
| 245 |
+
|
| 246 |
elif provider == 'groq':
|
| 247 |
+
final_user_prompt = user_prompt
|
| 248 |
+
if json_schema:
|
| 249 |
+
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)}"
|
| 250 |
+
|
| 251 |
+
messages = [{"role": "system", "content": system_instruction}, {"role": "user", "content": final_user_prompt}]
|
| 252 |
config = {'response_format': {"type": "json_object"}} if json_schema else {}
|
| 253 |
response = groq_client.chat.completions.create(model=model_name, messages=messages, **config)
|
| 254 |
+
|
| 255 |
+
if json_schema:
|
| 256 |
+
parsed_json = json.loads(response.choices[0].message.content)
|
| 257 |
+
required_keys = json_schema.get("required", [])
|
| 258 |
+
if not all(key in parsed_json and parsed_json[key] for key in required_keys):
|
| 259 |
+
raise ValueError(f"AI response missing required keys. Required: {required_keys}, Got: {list(parsed_json.keys())}")
|
| 260 |
+
return parsed_json
|
| 261 |
+
else:
|
| 262 |
+
return response.choices[0].message.content.strip()
|
| 263 |
+
|
| 264 |
raise Exception(f"Unsupported provider: {provider}")
|
| 265 |
|
| 266 |
@app.route('/')
|