from dotenv import load_dotenv import os import json import redis from transformers import ( AutoTokenizer, AutoModelForSequenceClassification, AutoModelForCausalLM, TrainingArguments, Trainer, ) from fastapi import FastAPI, HTTPException, Request from fastapi.responses import HTMLResponse import multiprocessing import time import uuid load_dotenv() REDIS_HOST = os.getenv('REDIS_HOST') REDIS_PORT = os.getenv('REDIS_PORT') REDIS_PASSWORD = os.getenv('REDIS_PASSWORD') app = FastAPI() default_language = "es" class ChatbotService: def __init__(self): self.redis_client = redis.StrictRedis(host=REDIS_HOST, port=REDIS_PORT, password=REDIS_PASSWORD, decode_responses=True) self.model_name = "response_model" self.tokenizer_name = "response_tokenizer" self.model = self.load_model_from_redis() self.tokenizer = self.load_tokenizer_from_redis() def get_response(self, user_id, message, language=default_language): if self.model is None or self.tokenizer is None: return "El modelo aún no está listo. Por favor, inténtelo de nuevo más tarde." input_text = f"Usuario: {message} Asistente:" input_ids = self.tokenizer.encode(input_text, return_tensors="pt").to("cpu") with torch.no_grad(): output = self.model.generate(input_ids=input_ids, max_length=100, num_beams=5, no_repeat_ngram_size=2, early_stopping=True) response = self.tokenizer.decode(output[0], skip_special_tokens=True) response = response.replace(input_text, "").strip() return response def load_model_from_redis(self): model_data_bytes = self.redis_client.get(f"model:{self.model_name}") if model_data_bytes: model = AutoModelForCausalLM.from_pretrained("gpt2") model.load_state_dict(torch.load(model_data_bytes)) return model return None def load_tokenizer_from_redis(self): tokenizer_data_bytes = self.redis_client.get(f"tokenizer:{self.tokenizer_name}") if tokenizer_data_bytes: tokenizer = AutoTokenizer.from_pretrained("gpt2") tokenizer.add_tokens(json.loads(tokenizer_data_bytes)) tokenizer.pad_token = tokenizer.eos_token return tokenizer return None chatbot_service = ChatbotService() class UnifiedModel(AutoModelForSequenceClassification): def __init__(self, config): super().__init__(config) @staticmethod def load_model_from_redis(redis_client): model_name = "unified_model" model_path = f"models/{model_name}" if redis_client.exists(f"model:{model_name}"): redis_client.delete(f"model:{model_name}") if not os.path.exists(model_path): model = UnifiedModel.from_pretrained("gpt2", num_labels=3) model.save_pretrained(model_path) else: model = UnifiedModel.from_pretrained(model_path) return model class SyntheticDataset(Dataset): def __init__(self, tokenizer, data): self.tokenizer = tokenizer self.data = data def __len__(self): return len(self.data) def __getitem__(self, idx): item = self.data[idx] text = item['text'] label = item['label'] tokens = self.tokenizer(text, padding="max_length", truncation=True, max_length=128, return_tensors="pt") return {"input_ids": tokens["input_ids"].squeeze(), "attention_mask": tokens["attention_mask"].squeeze(), "labels": label} conversation_history = {} @app.post("/process") async def process(request: Request): data = await request.json() redis_client = redis.StrictRedis(host=REDIS_HOST, port=REDIS_PORT, password=REDIS_PASSWORD, decode_responses=True) tokenizer_name = "unified_tokenizer" tokenizer_data_bytes = redis_client.get(f"tokenizer:{tokenizer_name}") if tokenizer_data_bytes: tokenizer = AutoTokenizer.from_pretrained("gpt2") tokenizer.add_tokens(json.loads(tokenizer_data_bytes)) tokenizer.pad_token = tokenizer.eos_token else: tokenizer = AutoTokenizer.from_pretrained("gpt2") tokenizer.pad_token = tokenizer.eos_token unified_model = UnifiedModel.load_model_from_redis(redis_client) unified_model.to(torch.device("cpu")) if data.get("train"): user_data = data.get("user_data", []) if not user_data: user_data = [ {"text": "Hola", "label": 1}, {"text": "Necesito ayuda", "label": 2}, {"text": "No entiendo", "label": 0} ] redis_client.rpush("training_queue", json.dumps({ "tokenizers": {tokenizer_name: tokenizer.get_vocab()}, "data": user_data })) return {"message": "Training data received. Model will be updated asynchronously."} elif data.get("message"): user_id = data.get("user_id") text = data['message'] language = data.get("language", default_language) if user_id not in conversation_history: conversation_history[user_id] = [] conversation_history[user_id].append(text) contextualized_text = " ".join(conversation_history[user_id][-3:]) tokenized_input = tokenizer(contextualized_text, return_tensors="pt") with torch.no_grad(): logits = unified_model(**tokenized_input).logits predicted_class = torch.argmax(logits, dim=-1).item() response = chatbot_service.get_response(user_id, contextualized_text, language) redis_client.rpush("training_queue", json.dumps({ "tokenizers": {tokenizer_name: tokenizer.get_vocab()}, "data": [{"text": contextualized_text, "label": predicted_class}] })) return {"answer": response} else: raise HTTPException(status_code=400, detail="Request must contain 'train' or 'message'.") @app.get("/") async def get_home(): user_id = str(uuid.uuid4()) html_code = f"""