Hjgugugjhuhjggg commited on
Commit
0e63678
verified
1 Parent(s): 227ec7b

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +80 -113
app.py CHANGED
@@ -1,29 +1,23 @@
1
  import os
2
  import json
 
3
  import boto3
4
- import uvicorn
5
  from fastapi import FastAPI, HTTPException
6
- from pydantic import BaseModel
7
  from transformers import AutoModelForCausalLM, AutoTokenizer, pipeline
8
  from huggingface_hub import hf_hub_download
9
- from io import BytesIO
10
- import torch
11
- import safetensors
12
- from dotenv import load_dotenv
13
- import tqdm
14
- import re
15
 
16
- # Cargar las variables de entorno desde el archivo .env
17
- load_dotenv()
18
 
19
- # Configuraci贸n de AWS y Hugging Face
20
  AWS_ACCESS_KEY_ID = os.getenv("AWS_ACCESS_KEY_ID")
21
  AWS_SECRET_ACCESS_KEY = os.getenv("AWS_SECRET_ACCESS_KEY")
22
  AWS_REGION = os.getenv("AWS_REGION")
23
- S3_BUCKET_NAME = os.getenv("S3_BUCKET_NAME") # Nombre del bucket S3
24
- HUGGINGFACE_TOKEN = os.getenv("HUGGINGFACE_TOKEN") # Token de Hugging Face
25
 
26
- # Cliente S3 de Amazon
27
  s3_client = boto3.client(
28
  's3',
29
  aws_access_key_id=AWS_ACCESS_KEY_ID,
@@ -33,11 +27,16 @@ s3_client = boto3.client(
33
 
34
  app = FastAPI()
35
 
36
- # Pydantic Model para el cuerpo de la solicitud del endpoint /predict/
37
- class DownloadModelRequest(BaseModel):
38
- model_name: str
39
- pipeline_task: str
40
- input_text: str
 
 
 
 
 
41
 
42
  class S3DirectStream:
43
  def __init__(self, bucket_name):
@@ -51,155 +50,123 @@ class S3DirectStream:
51
 
52
  def stream_from_s3(self, key):
53
  try:
54
- print(f"[INFO] Descargando archivo {key} desde S3...")
55
  response = self.s3_client.get_object(Bucket=self.bucket_name, Key=key)
56
- return BytesIO(response['Body'].read()) # Devolver el cuerpo como BytesIO
57
  except self.s3_client.exceptions.NoSuchKey:
 
58
  raise HTTPException(status_code=404, detail=f"El archivo {key} no existe en el bucket S3.")
59
  except Exception as e:
60
- print(f"[ERROR] Error al descargar {key}: {str(e)}")
61
- raise HTTPException(status_code=500, detail=f"Error al descargar archivo {key} desde S3.")
62
 
63
- def file_exists_in_s3(self, key):
64
  try:
65
- self.s3_client.head_object(Bucket=self.bucket_name, Key=key)
66
- return True
67
- except self.s3_client.exceptions.ClientError:
68
- return False
 
 
 
 
69
 
70
  def load_model_from_s3(self, model_name):
71
  try:
72
- print(f"[INFO] Cargando el modelo {model_name} desde S3...")
73
-
74
- # Verificar si el modelo existe en S3
75
  model_prefix = model_name.lower()
76
  model_files = self.get_model_file_parts(model_prefix)
77
 
78
  if not model_files:
79
- print(f"[INFO] El modelo {model_name} no est谩 en S3. Procediendo a descargar desde Hugging Face...")
80
  self.download_and_upload_from_huggingface(model_name)
81
  model_files = self.get_model_file_parts(model_prefix)
82
 
83
  if not model_files:
 
84
  raise HTTPException(status_code=404, detail=f"Archivos del modelo {model_name} no encontrados en S3.")
85
 
86
- # Cargar todos los archivos del modelo desde S3
87
- model_streams = []
88
- for model_file in tqdm.tqdm(model_files, desc="Cargando archivos del modelo", unit="archivo"):
89
- model_streams.append(self.stream_from_s3(model_file))
90
-
91
  config_stream = self.stream_from_s3(f"{model_prefix}/config.json")
92
- config_data = json.loads(config_stream.read().decode("utf-8"))
93
-
94
- # Cargar el modelo dependiendo del tipo de archivo (torch o safetensors)
95
- if any(file.endswith("model.safetensors") for file in model_files):
96
- print("[INFO] Cargando el modelo como safetensor...")
97
- model = AutoModelForCausalLM.from_config(config_data)
98
- model.load_state_dict(safetensors.torch.load_stream(model_streams[0]))
99
- else:
100
- print("[INFO] Cargando el modelo como archivo binario de PyTorch...")
101
- model = AutoModelForCausalLM.from_config(config_data)
102
- model.load_state_dict(torch.load(model_streams[0], map_location="cpu"))
103
-
104
- print("[INFO] Modelo cargado con 茅xito.")
105
  return model
106
 
107
  except Exception as e:
108
- print(f"[ERROR] Error al cargar el modelo desde S3: {e}")
109
- raise HTTPException(status_code=500, detail="Error al cargar el modelo desde S3.")
110
 
111
  def load_tokenizer_from_s3(self, model_name):
112
  try:
113
- print(f"[INFO] Cargando el tokenizer {model_name} desde S3...")
114
  tokenizer_stream = self.stream_from_s3(f"{model_name}/tokenizer.json")
115
- tokenizer = AutoTokenizer.from_pretrained(tokenizer_stream)
 
116
  return tokenizer
117
  except Exception as e:
118
- print(f"[ERROR] Error al cargar el tokenizer desde S3: {e}")
119
- raise HTTPException(status_code=500, detail="Error al cargar el tokenizer desde S3.")
120
-
121
- def get_model_file_parts(self, model_name):
122
- print(f"[INFO] Listando archivos del modelo en S3 con prefijo {model_name}...")
123
- files = self.s3_client.list_objects_v2(Bucket=self.bucket_name, Prefix=model_name)
124
- model_files = []
125
- for obj in tqdm.tqdm(files.get('Contents', []), desc="Verificando archivos", unit="archivo"):
126
- key = obj['Key']
127
- # Verificar si es un archivo relevante del modelo
128
- if re.match(rf"{model_name}/.*", key):
129
- model_files.append(key)
130
- if not model_files:
131
- print(f"[WARNING] No se encontraron archivos para el modelo {model_name}.")
132
- return model_files
133
 
134
  def download_and_upload_from_huggingface(self, model_name):
135
  try:
136
- print(f"[INFO] Descargando {model_name} desde Hugging Face...")
137
- # Descargar todos los archivos del modelo
138
- files_to_download = hf_hub_download(repo_id=model_name, use_auth_token=HUGGINGFACE_TOKEN)
139
 
140
- # Subir a S3 los archivos descargados
141
- for file in files_to_download:
142
  file_name = os.path.basename(file)
143
  s3_key = f"{model_name}/{file_name}"
144
  if not self.file_exists_in_s3(s3_key):
145
  self.upload_file_to_s3(file, s3_key)
146
 
147
  except Exception as e:
148
- print(f"[ERROR] Error al descargar y subir modelo desde Hugging Face: {e}")
149
- raise HTTPException(status_code=500, detail="Error al descargar y subir modelo desde Hugging Face.")
150
 
151
  def upload_file_to_s3(self, file_path, s3_key):
152
  try:
153
- print(f"[INFO] Subiendo archivo {file_path} a S3 con key {s3_key}...")
154
  with open(file_path, 'rb') as data:
155
  self.s3_client.put_object(Bucket=self.bucket_name, Key=s3_key, Body=data)
156
- # Eliminar archivo local despu茅s de ser subido
157
  os.remove(file_path)
 
158
  except Exception as e:
159
- print(f"[ERROR] Error al subir archivo a S3: {e}")
160
- raise HTTPException(status_code=500, detail="Error al subir archivo a S3.")
161
-
162
 
163
  @app.post("/predict/")
164
- async def predict(model_request: DownloadModelRequest):
165
  try:
166
- print(f"[INFO] Recibiendo solicitud para predecir con el modelo {model_request.model_name}...")
 
 
 
167
  streamer = S3DirectStream(S3_BUCKET_NAME)
168
- model = streamer.load_model_from_s3(model_request.model_name)
169
- tokenizer = streamer.load_tokenizer_from_s3(model_request.model_name)
170
 
171
- task = model_request.pipeline_task
172
- if task not in ["text-generation", "sentiment-analysis", "translation", "fill-mask", "question-answering",
173
- "text-to-speech", "text-to-video", "text-to-image"]:
174
  raise HTTPException(status_code=400, detail="Pipeline task no soportado")
175
 
176
- # Configurar el pipeline de transformers seg煤n la tarea
177
- nlp_pipeline = None
178
- if task == "text-generation":
179
- nlp_pipeline = pipeline("text-generation", model=model, tokenizer=tokenizer)
180
- elif task == "sentiment-analysis":
181
- nlp_pipeline = pipeline("sentiment-analysis", model=model, tokenizer=tokenizer)
182
- elif task == "translation":
183
- nlp_pipeline = pipeline("translation", model=model, tokenizer=tokenizer)
184
- elif task == "fill-mask":
185
- nlp_pipeline = pipeline("fill-mask", model=model, tokenizer=tokenizer)
186
- elif task == "question-answering":
187
- nlp_pipeline = pipeline("question-answering", model=model, tokenizer=tokenizer)
188
- elif task == "text-to-speech":
189
- nlp_pipeline = pipeline("text-to-speech", model=model, tokenizer=tokenizer)
190
- elif task == "text-to-video":
191
- nlp_pipeline = pipeline("text-to-video", model=model, tokenizer=tokenizer)
192
- elif task == "text-to-image":
193
- nlp_pipeline = pipeline("text-to-image", model=model, tokenizer=tokenizer)
194
-
195
- result = nlp_pipeline(model_request.input_text)
196
- return {"result": result}
197
 
198
- except Exception as e:
199
- print(f"[ERROR] Error en el proceso de predicci贸n: {str(e)}")
200
- raise HTTPException(status_code=500, detail="Error en el proceso de predicci贸n")
 
 
 
201
 
 
 
 
202
 
203
- # Ejecutar la app con Uvicorn
204
  if __name__ == "__main__":
 
205
  uvicorn.run(app, host="0.0.0.0", port=8000)
 
1
  import os
2
  import json
3
+ import logging
4
  import boto3
 
5
  from fastapi import FastAPI, HTTPException
6
+ from fastapi.responses import JSONResponse
7
  from transformers import AutoModelForCausalLM, AutoTokenizer, pipeline
8
  from huggingface_hub import hf_hub_download
9
+ from tqdm import tqdm
10
+ import io
 
 
 
 
11
 
12
+ logging.basicConfig(level=logging.INFO)
13
+ logger = logging.getLogger(__name__)
14
 
 
15
  AWS_ACCESS_KEY_ID = os.getenv("AWS_ACCESS_KEY_ID")
16
  AWS_SECRET_ACCESS_KEY = os.getenv("AWS_SECRET_ACCESS_KEY")
17
  AWS_REGION = os.getenv("AWS_REGION")
18
+ S3_BUCKET_NAME = os.getenv("S3_BUCKET_NAME")
19
+ HUGGINGFACE_TOKEN = os.getenv("HUGGINGFACE_TOKEN")
20
 
 
21
  s3_client = boto3.client(
22
  's3',
23
  aws_access_key_id=AWS_ACCESS_KEY_ID,
 
27
 
28
  app = FastAPI()
29
 
30
+ PIPELINE_MAP = {
31
+ "text-generation": "text-generation",
32
+ "sentiment-analysis": "sentiment-analysis",
33
+ "translation": "translation",
34
+ "fill-mask": "fill-mask",
35
+ "question-answering": "question-answering",
36
+ "text-to-speech": "text-to-speech",
37
+ "text-to-video": "text-to-video",
38
+ "text-to-image": "text-to-image"
39
+ }
40
 
41
  class S3DirectStream:
42
  def __init__(self, bucket_name):
 
50
 
51
  def stream_from_s3(self, key):
52
  try:
53
+ logger.info(f"Descargando {key} desde S3...")
54
  response = self.s3_client.get_object(Bucket=self.bucket_name, Key=key)
55
+ return response['Body']
56
  except self.s3_client.exceptions.NoSuchKey:
57
+ logger.error(f"El archivo {key} no existe en el bucket S3.")
58
  raise HTTPException(status_code=404, detail=f"El archivo {key} no existe en el bucket S3.")
59
  except Exception as e:
60
+ logger.error(f"Error al descargar {key} desde S3: {str(e)}")
61
+ raise HTTPException(status_code=500, detail=f"Error al descargar {key} desde S3: {str(e)}")
62
 
63
+ def get_model_file_parts(self, model_name):
64
  try:
65
+ model_prefix = model_name.lower()
66
+ logger.info(f"Obteniendo archivos para el modelo {model_name} desde S3...")
67
+ files = self.s3_client.list_objects_v2(Bucket=self.bucket_name, Prefix=model_prefix)
68
+ model_files = [obj['Key'] for obj in files.get('Contents', []) if model_prefix in obj['Key']]
69
+ return model_files
70
+ except Exception as e:
71
+ logger.error(f"Error al obtener archivos del modelo {model_name} desde S3: {e}")
72
+ raise HTTPException(status_code=500, detail=f"Error al obtener archivos del modelo {model_name} desde S3: {e}")
73
 
74
  def load_model_from_s3(self, model_name):
75
  try:
 
 
 
76
  model_prefix = model_name.lower()
77
  model_files = self.get_model_file_parts(model_prefix)
78
 
79
  if not model_files:
80
+ logger.info(f"El modelo {model_name} no est谩 en S3, descargando desde Hugging Face...")
81
  self.download_and_upload_from_huggingface(model_name)
82
  model_files = self.get_model_file_parts(model_prefix)
83
 
84
  if not model_files:
85
+ logger.error(f"Archivos del modelo {model_name} no encontrados en S3.")
86
  raise HTTPException(status_code=404, detail=f"Archivos del modelo {model_name} no encontrados en S3.")
87
 
88
+ logger.info(f"Cargando archivos del modelo {model_name}...")
 
 
 
 
89
  config_stream = self.stream_from_s3(f"{model_prefix}/config.json")
90
+ config_data = config_stream.read()
91
+
92
+ if not config_data:
93
+ logger.error(f"El archivo de configuraci贸n {model_prefix}/config.json est谩 vac铆o.")
94
+ raise HTTPException(status_code=500, detail=f"El archivo de configuraci贸n {model_prefix}/config.json est谩 vac铆o.")
95
+
96
+ config_text = config_data.decode("utf-8")
97
+ config_json = json.loads(config_text)
98
+
99
+ model = AutoModelForCausalLM.from_pretrained(f"s3://{self.bucket_name}/{model_prefix}", config=config_json, from_tf=False)
 
 
 
100
  return model
101
 
102
  except Exception as e:
103
+ logger.error(f"Error al cargar el modelo desde S3: {e}")
104
+ raise HTTPException(status_code=500, detail=f"Error al cargar el modelo desde S3: {e}")
105
 
106
  def load_tokenizer_from_s3(self, model_name):
107
  try:
108
+ logger.info(f"Cargando el tokenizer del modelo {model_name} desde S3...")
109
  tokenizer_stream = self.stream_from_s3(f"{model_name}/tokenizer.json")
110
+ tokenizer_data = tokenizer_stream.read().decode("utf-8")
111
+ tokenizer = AutoTokenizer.from_pretrained(f"s3://{self.bucket_name}/{model_name}")
112
  return tokenizer
113
  except Exception as e:
114
+ logger.error(f"Error al cargar el tokenizer desde S3: {e}")
115
+ raise HTTPException(status_code=500, detail=f"Error al cargar el tokenizer desde S3: {e}")
 
 
 
 
 
 
 
 
 
 
 
 
 
116
 
117
  def download_and_upload_from_huggingface(self, model_name):
118
  try:
119
+ logger.info(f"Descargando modelo {model_name} desde Hugging Face...")
120
+ files_to_download = hf_hub_download(repo_id=model_name, use_auth_token=HUGGINGFACE_TOKEN, local_dir=model_name)
 
121
 
122
+ for file in tqdm(files_to_download, desc="Subiendo archivos a S3"):
 
123
  file_name = os.path.basename(file)
124
  s3_key = f"{model_name}/{file_name}"
125
  if not self.file_exists_in_s3(s3_key):
126
  self.upload_file_to_s3(file, s3_key)
127
 
128
  except Exception as e:
129
+ logger.error(f"Error al descargar y subir modelo desde Hugging Face: {e}")
130
+ raise HTTPException(status_code=500, detail=f"Error al descargar y subir modelo desde Hugging Face: {e}")
131
 
132
  def upload_file_to_s3(self, file_path, s3_key):
133
  try:
 
134
  with open(file_path, 'rb') as data:
135
  self.s3_client.put_object(Bucket=self.bucket_name, Key=s3_key, Body=data)
 
136
  os.remove(file_path)
137
+ logger.info(f"Archivo {file_path} subido correctamente a S3 y eliminado localmente.")
138
  except Exception as e:
139
+ logger.error(f"Error al subir archivo a S3: {e}")
140
+ raise HTTPException(status_code=500, detail=f"Error al subir archivo a S3: {e}")
 
141
 
142
  @app.post("/predict/")
143
+ async def predict(model_request: dict):
144
  try:
145
+ model_name = model_request.get("model_name")
146
+ task = model_request.get("pipeline_task")
147
+ input_text = model_request.get("input_text")
148
+
149
  streamer = S3DirectStream(S3_BUCKET_NAME)
150
+ model = streamer.load_model_from_s3(model_name)
151
+ tokenizer = streamer.load_tokenizer_from_s3(model_name)
152
 
153
+ if task not in PIPELINE_MAP:
154
+ logger.error("Pipeline task no soportado")
 
155
  raise HTTPException(status_code=400, detail="Pipeline task no soportado")
156
 
157
+ nlp_pipeline = pipeline(PIPELINE_MAP[task], model=model, tokenizer=tokenizer)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
158
 
159
+ result = nlp_pipeline(input_text)
160
+
161
+ if isinstance(result, dict) and 'file' in result:
162
+ return JSONResponse(content={"file": result['file']})
163
+ else:
164
+ return JSONResponse(content={"result": result})
165
 
166
+ except Exception as e:
167
+ logger.error(f"Error al realizar la predicci贸n: {e}")
168
+ raise HTTPException(status_code=500, detail=f"Error al realizar la predicci贸n: {e}")
169
 
 
170
  if __name__ == "__main__":
171
+ import uvicorn
172
  uvicorn.run(app, host="0.0.0.0", port=8000)