🐾
NutriPaw AI
Modal Serverless GPU Β· Arquitectura de ProducciΓ³n
● Live L40S GPU Modal v1.4
Latencia P50
0.8s
↓ 62% vs CPU
GPU VRAM usada
18.4GB
de 48 GB L40S
Cold Start
3.2s
Pesos en Volumen
Batch Nocturno
5.000
planes / 8 min
Coste estimado
187€/mes
↓ presupuesto 200€
app.py β€” Despliegue Modal Completo
Python 3.11
nutripaw_modal/app.py
# NutriPaw AI β€” Modal Serverless GPU Deployment # Skill: modal-serverless-gpu-cloud | CULTIVA IA import modal from pydantic import BaseModel app = modal.App("nutripaw-ai") # ── Imagen con vLLM + dependencias ────────────────── image = ( modal.Image.debian_slim(python_version="3.11") .uv_pip_install( "vllm==0.6.4", "fastapi==0.115.0", "pydantic==2.8.0", ) ) # ── Volumen persistente para pesos del modelo ──────── weights_vol = modal.Volume.from_name( "nutripaw-model-weights", create_if_missing=True, ) # Ruta: /weights/nutripaw-llama3-8b-q4.gguf (16 GB) # ── Secret HuggingFace ─────────────────────────────── hf_secret = modal.Secret.from_name("huggingface-token") # ── Clase principal con lifecycle hooks ────────────── @app.cls( gpu="L40S", # 48 GB VRAM, mejor precio/perf image=image, volumes={"/weights": weights_vol}, secrets=[hf_secret], min_containers=1, # Warm: latencia baja max_containers=10, scaledown_window=600, ) class NutriPawAI: @modal.enter() def load_model(self): from vllm import LLM, SamplingParams self.llm = LLM( model="/weights/nutripaw-llama3-8b-q4", dtype="float16", max_model_len=4096, ) self.sampling = SamplingParams( temperature=0.3, max_tokens=512, ) # ── Web endpoint (POST /generate) ──────────────── @modal.method() @modal.fastapi_endpoint(method="POST", path="/generate") def generate_plan(self, request: "PetProfileRequest"): prompt = build_prompt(request) outputs = self.llm.generate([prompt], self.sampling) return { "plan": outputs[0].outputs[0].text, "tokens": len(outputs[0].outputs[0].token_ids), } @modal.exit() def cleanup(self): del self.llm # ── Batch: regenerar 5.000 planes en paralelo ──────── @app.function( gpu="L40S", image=image, cpu=4.0, memory=16384, volumes={"/weights": weights_vol}, max_containers=20, timeout=1800, ) def batch_generate_plan(pet_profile: dict) -> dict: # Cada invocaciΓ³n = 1 mascota; .map() paralela from vllm import LLM, SamplingParams llm = LLM(model="/weights/nutripaw-llama3-8b-q4") out = llm.generate([build_prompt(pet_profile)]) return {"pet_id": pet_profile["id"], "plan": out[0].outputs[0].text} # ── Job nocturno programado 02:00 UTC ──────────────── @app.function( schedule=modal.Cron("0 2 * * *"), secrets=[modal.Secret.from_name("nutripaw-db")], timeout=3600, ) def nightly_batch(): import os, json profiles = fetch_all_pets_from_db(os.environ["DATABASE_URL"]) results = list(batch_generate_plan.map(profiles)) save_plans_to_db(results) print(f"βœ“ {len(results)} planes regenerados")
Flujo de Arquitectura
πŸ“± App NutriPaw
iOS / Android
↓ POST /generate
🌐 Modal Router
URL permanente Β· autoscale
↓ despacho
⚑ NutriPawAI cls
L40S GPU Β· vLLM Β· min_containers=1
↑ pesos ↑ token
πŸ’Ύ Volume
nutripaw-model-weights
16 GB Q4_K_M
πŸ”‘ Secret
huggingface-token
nutripaw-db
πŸ•‘ Cron 02:00 UTC
nightly_batch β†’ .map(5000) β†’ DB
Pasos de Despliegue
1
Autenticar Modal
modal setup o variables MODAL_TOKEN_ID/SECRET
2
Subir pesos al Volumen
modal volume put nutripaw-model-weights ./model
3
Crear Secrets
modal secret create huggingface-token HF_TOKEN=hf_xxx
4
Test en dev
modal serve app.py β†’ URL temporal + hot reload
5
Deploy producciΓ³n
modal deploy app.py β†’ URL permanente + cron activo
Recursos Configurados
FunciΓ³n / Cls GPU / CPU Tipo Containers Timeout
NutriPawAI L40S 48GB Web Endpoint 1 – 10 β€”
batch_generate_plan L40S 48GB .map() Batch 0 – 20 30 min
nightly_batch 0.125 CPU Cron 02:00 1 60 min
weights_vol Volume persistente (write-once / read-many) β€” β€”
Estimacion de Coste / mes
Inference API
94 €
min_containers=1 Β· 720h Β· L40S
Batch nocturno
62 €
30 dias Γ— 8 min Γ— 20 L40S
Volume storage
18 €
~16 GB modelo + logs
Requests spike
13 €
buffer picos demand
Total estimado
187 €/mes
Endpoints ProducciΓ³n
POST /generate
Genera plan nutricional Β· auth Bearer Β· JSON β†’ {plan, tokens}
GET /health
Estado del servicio Β· latencia actual Β· GPU metrics
🌐 URL permanente: nutripaw-ai--nutripawai.modal.run
⚑ Cold start: ~3.2s (pesos pre-cargados en volumen)
πŸ“Š Dashboard: modal.com/apps/nutripaw-ai
Benchmarks de Rendimiento β€” L40S vs alternativas
Latencia P50 (tokens/s)
L40S (elegido)
180 t/s
A100-40GB
160 t/s
H100
200 t/s
T4 (baseline)
40 t/s
Coste relativo (inferencia, 1M tokens)
T4
0.45 €
L40S (elegido)
0.40 €
A100-40GB
0.70 €
H100
1.00 €