BioRAG / model_app.py
C2MV's picture
Upload 3 files
227fa34 verified
raw
history blame
6.33 kB
# modal_app.py
import modal
import sys
from pathlib import Path
import os
import traceback # Para imprimir traceback detallado
# --- Configuraci贸n ---
PYTHON_VERSION = "3.10"
APP_NAME = "bioprocess-custom-eq-agent-modal"
LOCAL_APP_DIR = Path(__file__).parent
REMOTE_APP_DIR = "/app"
stub = modal.Stub(APP_NAME)
app_image = (
modal.Image.debian_slim(python_version=PYTHON_VERSION)
.pip_install_from_requirements(LOCAL_APP_DIR / "requirements.txt")
.copy_mount(
modal.Mount.from_local_dir(LOCAL_APP_DIR, remote_path=REMOTE_APP_DIR)
)
.env({
"PYTHONPATH": REMOTE_APP_DIR,
"HF_HOME": "/cache/huggingface",
"HF_HUB_CACHE": "/cache/huggingface/hub",
"TRANSFORMERS_CACHE": "/cache/huggingface/hub",
"MPLCONFIGDIR": "/tmp/matplotlib_cache"
})
.run_commands(
"apt-get update && apt-get install -y git git-lfs && rm -rf /var/lib/apt/lists/*",
"mkdir -p /cache/huggingface/hub /tmp/matplotlib_cache"
)
)
# --- Funci贸n Modal para LLM (sin cambios respecto a la anterior respuesta) ---
@stub.function(
image=app_image,
gpu="any",
secrets=[modal.Secret.from_name("huggingface-read-token", optional=True)],
timeout=600,
volumes={"/cache/huggingface": modal.Volume.persisted(f"{APP_NAME}-hf-cache-vol")}
)
def generate_analysis_llm_modal_remote(prompt: str, model_path_config: str, max_new_tokens_config: int) -> str:
import torch # Mover importaciones pesadas dentro de la funci贸n Modal
from transformers import AutoTokenizer, AutoModelForCausalLM
hf_token = os.environ.get("HUGGING_FACE_TOKEN")
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
print(f"LLM Modal Func: Usando dispositivo: {device}")
print(f"LLM Modal Func: Cargando modelo: {model_path_config} con token: {'S铆' if hf_token else 'No'}")
try:
tokenizer = AutoTokenizer.from_pretrained(model_path_config, cache_dir="/cache/huggingface/hub", token=hf_token)
model = AutoModelForCausalLM.from_pretrained(
model_path_config,
torch_dtype="auto",
device_map="auto",
cache_dir="/cache/huggingface/hub",
token=hf_token,
)
# Ajustar longitud de truncamiento para el prompt para dejar espacio a max_new_tokens
# La longitud total (prompt + generado) no debe exceder el context window del modelo.
# Asumamos un context window conservador de 4096 si no se conoce.
model_context_window = getattr(model.config, 'max_position_embeddings', 4096)
max_prompt_len = model_context_window - max_new_tokens_config - 50 # 50 tokens de buffer
inputs = tokenizer(prompt, return_tensors="pt", truncation=True, max_length=max_prompt_len).to(model.device)
with torch.no_grad():
outputs = model.generate(
**inputs,
max_new_tokens=max_new_tokens_config,
eos_token_id=tokenizer.eos_token_id,
pad_token_id=tokenizer.pad_token_id if tokenizer.pad_token_id is not None else tokenizer.eos_token_id,
do_sample=True,
temperature=0.6,
top_p=0.9,
)
input_length = inputs.input_ids.shape[1]
generated_ids = outputs[0][input_length:]
analysis = tokenizer.decode(generated_ids, skip_special_tokens=True)
print(f"LLM Modal Func: Longitud del an谩lisis generado: {len(analysis)} caracteres.")
return analysis.strip()
except Exception as e:
error_traceback = traceback.format_exc()
print(f"Error en generate_analysis_llm_modal_remote: {e}\n{error_traceback}")
return f"Error al generar an谩lisis con el modelo LLM: {str(e)}"
# --- Servidor Gradio ---
@stub.asgi_app()
def serve_gradio_app_asgi():
# Estas importaciones ocurren DENTRO del contenedor Modal
import gradio as gr
# sys.path ya est谩 configurado por la imagen, pero por si acaso:
if REMOTE_APP_DIR not in sys.path:
sys.path.insert(0, REMOTE_APP_DIR)
from UI import create_interface # De tu UI.py
import interface as app_interface_module # El m贸dulo interface.py
from config import MODEL_PATH as cfg_MODEL_PATH, MAX_LENGTH as cfg_MAX_LENGTH
# Wrapper para llamar a la funci贸n Modal remota
def analysis_func_wrapper_for_interface(prompt: str) -> str:
print("Gradio Backend: Llamando a generate_analysis_llm_modal_remote.remote...")
return generate_analysis_llm_modal_remote.remote(prompt, cfg_MODEL_PATH, cfg_MAX_LENGTH)
# Inyectar esta funci贸n wrapper en el m贸dulo `interface`
app_interface_module.generate_analysis_from_modal = analysis_func_wrapper_for_interface
app_interface_module.USE_MODAL_FOR_LLM_ANALYSIS = True
# Crear la app Gradio, pas谩ndole la funci贸n de procesamiento que ahora est谩 en app_interface_module
# create_interface ahora toma la funci贸n de callback como argumento.
gradio_ui = create_interface(process_function_for_button=app_interface_module.process_and_plot)
return gr.routes.App.create_app(gradio_ui)
@stub.local_entrypoint()
def test_llm():
print("Probando la generaci贸n de LLM con Modal (localmente)...")
# Necesitas importar config para que MODEL_PATH y MAX_LENGTH est茅n definidos
# Esto debe hacerse dentro de un contexto donde los m贸dulos de la app sean accesibles.
# Es mejor llamar a la funci贸n stub desde aqu铆.
if REMOTE_APP_DIR not in sys.path: # Asegurar path para pruebas locales tambi茅n
sys.path.insert(0, str(LOCAL_APP_DIR))
from config import MODEL_PATH, MAX_LENGTH
sample_prompt = "Explica brevemente el concepto de R cuadrado (R虏) en el ajuste de modelos."
# Ejecuta la funci贸n Modal directamente (no .remote() para prueba local de l贸gica)
# Para probar la ejecuci贸n remota real, necesitar铆as `modal run modal_app.py test_llm`
# o que test_llm llame a .remote().
# Aqu铆 vamos a probar la llamada remota.
try:
analysis = generate_analysis_llm_modal_remote.remote(sample_prompt, MODEL_PATH, MAX_LENGTH)
print("\nRespuesta del LLM:")
print(analysis)
except Exception as e:
print(f"Error durante test_llm: {e}")
traceback.print_exc()