File size: 7,445 Bytes
d3d8124 227fa34 de3a711 d3d8124 e43df9f d3d8124 e43df9f d3d8124 e43df9f 227fa34 d3d8124 227fa34 e43df9f d3d8124 e43df9f 227fa34 e43df9f d3d8124 e43df9f d3d8124 e43df9f 227fa34 e43df9f d3d8124 e43df9f d3d8124 de3a711 d3d8124 227fa34 d3d8124 e43df9f d3d8124 227fa34 e43df9f 227fa34 e43df9f de3a711 227fa34 d3d8124 e43df9f d3d8124 e43df9f d3d8124 de3a711 e43df9f 227fa34 e43df9f d3d8124 e43df9f de3a711 e43df9f de3a711 e43df9f de3a711 e43df9f de3a711 e43df9f de3a711 d3d8124 e43df9f de3a711 d3d8124 de3a711 d3d8124 e43df9f d3d8124 de3a711 d3d8124 e43df9f de3a711 d3d8124 e43df9f de3a711 e43df9f 227fa34 d3d8124 227fa34 d3d8124 227fa34 de3a711 227fa34 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 |
# modal_app.py
import modal
import sys
from pathlib import Path
import os
import traceback
# --- Configuraci贸n ---
PYTHON_VERSION = "3.10"
APP_NAME = "bioprocess-custom-eq-agent-modal"
LOCAL_APP_DIR = Path(__file__).parent
REMOTE_APP_DIR = "/app" # Directorio donde se copiar谩n los archivos en el contenedor Modal
stub = modal.Stub(APP_NAME)
app_image = (
modal.Image.debian_slim(python_version=PYTHON_VERSION)
.pip_install_from_requirements(LOCAL_APP_DIR / "requirements.txt") # Lee desde tu requirements.txt
.copy_mount( # Copia todos los archivos de la app
modal.Mount.from_local_dir(LOCAL_APP_DIR, remote_path=REMOTE_APP_DIR)
)
.env({ # Configura variables de entorno dentro del contenedor
"PYTHONPATH": REMOTE_APP_DIR, # Permite importar m贸dulos desde /app
"HF_HOME": "/cache/huggingface",
"HF_HUB_CACHE": "/cache/huggingface/hub",
"TRANSFORMERS_CACHE": "/cache/huggingface/hub",
"MPLCONFIGDIR": "/tmp/matplotlib_cache" # Evita warnings de matplotlib
})
.run_commands( # Comandos a ejecutar durante la construcci贸n de la imagen
"apt-get update && apt-get install -y git git-lfs && rm -rf /var/lib/apt/lists/*",
"mkdir -p /cache/huggingface/hub /tmp/matplotlib_cache" # Crea directorios de cach茅
)
)
# --- Funci贸n Modal para LLM (sin cambios significativos respecto a la respuesta completa anterior) ---
@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
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, trust_remote_code=True)
model = AutoModelForCausalLM.from_pretrained(
model_path_config,
torch_dtype="auto",
device_map="auto",
cache_dir="/cache/huggingface/hub",
token=hf_token,
trust_remote_code=True
)
model_context_window = getattr(model.config, 'max_position_embeddings', getattr(model.config, 'sliding_window', 4096))
if model_context_window is None : model_context_window = 4096
max_prompt_len = model_context_window - max_new_tokens_config - 50
if max_prompt_len <=0 : max_prompt_len = model_context_window // 2
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(image=app_image)
def serve_gradio_app_asgi():
import gradio as gr
# El PYTHONPATH ya est谩 configurado en la imagen, pero una verificaci贸n no da帽a.
if REMOTE_APP_DIR not in sys.path:
sys.path.insert(0, REMOTE_APP_DIR)
print(f"INFO (modal_app.py @asgi): A帽adido {REMOTE_APP_DIR} a sys.path")
# --- Neutralizaci贸n del decorador GPU de Spaces, DENTRO del contexto Modal ---
# Esto asegura que cualquier importaci贸n de `gradio` o `decorators`
# no cause problemas con el chequeo de `@spaces.GPU`.
try:
import decorators # Importa TU decorators.py
class _GPUNeutralizerInModal:
def __init__(self, *args, **kwargs): pass
def __call__(self, func): return func
# Sobrescribir la clase GPU y el decorador en el m贸dulo 'decorators'
# para esta instancia del servidor ASGI.
decorators.ActualSpacesGPU = _GPUNeutralizerInModal # Si decorators.py usa este nombre
decorators._GPU_decorator_target = _GPUNeutralizerInModal # Si decorators.py usa este
decorators.gpu_decorator = lambda duration=0: lambda func: func # Neutralizar el decorador
print("INFO (modal_app.py @asgi): Decoradores GPU de 'spaces' neutralizados para el entorno Modal.")
except ImportError:
print("ADVERTENCIA (modal_app.py @asgi): M贸dulo 'decorators' no encontrado durante la neutralizaci贸n. Puede ser OK.")
except Exception as e_neut_modal:
print(f"ADVERTENCIA (modal_app.py @asgi): Error durante la neutralizaci贸n de decoradores en Modal: {e_neut_modal}")
# --- Fin de la neutralizaci贸n ---
# Importar los m贸dulos de la aplicaci贸n DESPU脡S de la neutralizaci贸n y config de path
from UI import create_interface
import interface as app_interface_module
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_modal(prompt: str) -> str:
print("Gradio Backend (Modal): Llamando a generate_analysis_llm_modal_remote.remote...")
return generate_analysis_llm_modal_remote.remote(prompt, cfg_MODEL_PATH, cfg_MAX_LENGTH)
app_interface_module.generate_analysis_from_modal = analysis_func_wrapper_for_interface_modal
app_interface_module.USE_MODAL_FOR_LLM_ANALYSIS = True
print("INFO (modal_app.py @asgi): Runner de LLM Modal inyectado en el m贸dulo 'interface'.")
gradio_ui_instance = create_interface(process_function_for_button=app_interface_module.process_and_plot)
print("INFO (modal_app.py @asgi): Interfaz Gradio creada y lista para ser servida por Modal.")
return gr.routes.App.create_app(gradio_ui_instance)
@stub.local_entrypoint()
def test_llm_local_entry():
print("Probando la generaci贸n de LLM con Modal (local_entrypoint)...")
if str(LOCAL_APP_DIR) not in sys.path:
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."
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_local_entry: {e}")
traceback.print_exc() |