feat: versión inicial del microservicio funcionando

parents
Showing with 198 additions and 0 deletions
# Created by venv; see https://docs.python.org/3/library/venv.html
*
!.gitignore
!app.py
!requirements.txt
\ No newline at end of file
import re, json, uuid
from flask import Flask, request, jsonify
from dotenv import load_dotenv
import google.generativeai as genai
# Cargar variables de entorno
load_dotenv()
# Modelo generativo
model = genai.GenerativeModel('gemini-1.5-flash')
# "Memoria" para las sesiones de chat activas
CHAT_SESSIONS = {}
app = Flask(__name__)
# Limpia y parsea una cadena de texto para extraer un objeto JSON.
def extract_json_from_response(response_text):
# Busca el bloque JSON delimitado por ```json y ```
match = re.search(r"```json\s*([\s\S]*?)\s*```", response_text)
if match:
cleaned = match.group(1)
else:
# Si no hay ```, asume que toda la cadena es JSON
cleaned = response_text.strip()
try:
return json.loads(cleaned)
except json.JSONDecodeError as e:
print(f"Error al decodificar JSON: {e}")
print(f"Respuesta recibida que causó el error:\n{response_text}")
raise ValueError("La respuesta de la API no contenía un JSON válido.")
# Inicia una nueva búsqueda de recetas
@app.route("/recipe/search", methods=["POST"])
def search_recipes():
data = request.get_json()
ingredients = data.get("ingredients")
auth_header = request.headers.get("Authorization")
token = None
if auth_header and auth_header.startswith("Bearer "):
token = auth_header[len("Bearer "):]
if not ingredients:
return jsonify({"error": "El campo 'ingredients' es requerido"}), 400
if not token:
return jsonify({"error": "Token requerido"}), 401
# Iniciar una nueva sesión de chat
session_id = str(uuid.uuid4())
chat = model.start_chat(history=[])
# Guardar chat, recetas y token juntos
CHAT_SESSIONS[session_id] = {
"chat": chat,
"recipes": [],
"token": token
}
prompt = f"""
Dame 4 recetas que usen '{ingredients}' como ingrediente principal.
Devuélvelas en un formato JSON que contenga una única clave "recipes", que será una lista de objetos.
Cada objeto debe tener esta estructura:
{{
"name": "Nombre de la receta",
"description": "Breve descripción"
}}
No incluyas ningún texto adicional, solo el JSON.
"""
try:
response = chat.send_message(prompt)
recipes_data = extract_json_from_response(response.text)
# Guardar solo las recetas parseadas para uso posterior
CHAT_SESSIONS[session_id]["recipes"] = recipes_data.get("recipes", [])
return jsonify({
"sessionId": session_id,
"recipes": CHAT_SESSIONS[session_id]["recipes"]
})
except (Exception, ValueError) as e:
return jsonify({"error": str(e)}), 500
# Genera más recetas para una sesión de chat existente.
@app.route("/recipe/search/more", methods=["POST"])
def search_more_recipes():
session_id = request.headers.get('X-Session-ID')
if not session_id or session_id not in CHAT_SESSIONS:
return jsonify({"error": "ID de sesión inválido o no encontrado"}), 404
auth_header = request.headers.get("Authorization")
token = None
if auth_header and auth_header.startswith("Bearer "):
token = auth_header[len("Bearer "):]
print("ANTES DEL IF--------------")
if not token or token != CHAT_SESSIONS[session_id]["token"]:
print(f"TOKEN : {token} ----- GUARDADO : {CHAT_SESSIONS[session_id]["token"]}")
return jsonify({"error": "Token inválido para esta sesión"}), 401
chat = CHAT_SESSIONS[session_id]["chat"]
prompt = "Perfecto. Ahora dame otras 4 recetas más, que sean distintas a las anteriores. Con el mismo formato (solo JSON)"
try:
response = chat.send_message(prompt)
new_recipes_data = extract_json_from_response(response.text)
new_recipes = new_recipes_data.get("recipes", [])
# Añadir las nuevas recetas a nuestra lista de sesión
CHAT_SESSIONS[session_id]["recipes"].extend(new_recipes)
return jsonify({
"sessionId": session_id,
"recipes": new_recipes
})
except (Exception, ValueError) as e:
return jsonify({"error": str(e)}), 500
# Obtiene el detalle completo de una receta específica de la sesión.
@app.route("/recipe/detail/<int:index>", methods=["POST"])
def get_recipe_detail(index: int):
session_id = request.headers.get('X-Session-ID')
if not session_id or session_id not in CHAT_SESSIONS:
return jsonify({"error": "ID de sesión inválido o no encontrado"}), 404
auth_header = request.headers.get("Authorization")
token = None
if auth_header and auth_header.startswith("Bearer "):
token = auth_header[len("Bearer "):]
session_data = CHAT_SESSIONS[session_id]
if not token or token != session_data["token"]:
return jsonify({"error": "Token inválido para esta sesión"}), 401
session_recipes = session_data.get("recipes", [])
if index < 0 or index >= len(session_recipes):
print(f"INDEX : {index} ------ LONGITUD : {len(session_recipes)}")
return jsonify({"error": "Índice de receta fuera de rango"}), 400
# Comprobar si ya tenemos el detalle
if "ingredients" in session_recipes[index] and "steps" in session_recipes[index]:
return jsonify(session_recipes[index])
selected_recipe_name = session_recipes[index]["name"]
prompt = f"""
Ahora dame la receta completa y detallada para '{selected_recipe_name}'.
Devuélvela en formato JSON con la siguiente estructura, sin texto adicional:
{{
"name": "{selected_recipe_name}",
"description": "La descripción original",
"ingredients": [
{{
"name": "Ingrediente",
"quantity": "cantidad",
"unitOfMeasure": "unidad"
}}
],
"steps": [
{{
"number": 1,
"description": "Descripción del paso"
}}
]
}}
No añadas ningún texto adicional, solo el JSON.
"""
try:
response = session_data["chat"].send_message(prompt)
recipe_detail = extract_json_from_response(response.text)
# Actualizamos la receta en nuestra lista de sesión con el detalle completo
session_recipes[index] = recipe_detail
return jsonify(recipe_detail)
except (Exception, ValueError) as e:
return jsonify({"error": str(e)}), 500
if __name__ == "__main__":
app.run(port=5000)
\ No newline at end of file
No preview for this file type
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment