| import asyncio |
| import os |
| import torch |
| import redis |
| import io |
| import numpy as np |
| import base64 |
| from PIL import Image |
| from dotenv import load_dotenv |
| from transformers import AutoModelForCausalLM, AutoTokenizer, Trainer, TrainingArguments |
| from transformers import pipeline |
| from fastapi import FastAPI, HTTPException |
| from fastapi.responses import HTMLResponse |
| from typing import List, Dict, Any |
| import logging |
|
|
| |
| logging.basicConfig(level=logging.DEBUG) |
| logger = logging.getLogger(__name__) |
|
|
| |
| load_dotenv() |
| HUGGINGFACE_TOKEN = os.getenv("HUGGINGFACE_TOKEN") |
| REDIS_HOST = os.getenv("REDIS_HOST") |
| REDIS_PORT = os.getenv("REDIS_PORT") |
| REDIS_PASSWORD = os.getenv("REDIS_PASSWORD") |
|
|
| |
| redis_client = redis.StrictRedis(host=REDIS_HOST, port=REDIS_PORT, password=REDIS_PASSWORD, decode_responses=True) |
|
|
| |
| app = FastAPI() |
|
|
| |
| model_dict: Dict[str, Any] = {} |
| model_properties: Dict[str, Dict[str, Any]] = {} |
| model_lock = asyncio.Lock() |
|
|
| |
| message_history: List[str] = [] |
| TRAINING_DATA: List[Dict[str, torch.Tensor]] = [] |
| MUSIC_TRAINING_DATA: List[Dict[str, torch.Tensor]] = [] |
| IMAGE_TRAINING_DATA: List[Dict[str, torch.Tensor]] = [] |
|
|
| |
| TEMPERATURE = 0.7 |
| TOP_PROBABILITY = 0.9 |
| TOP_K = 50 |
| FREQUENCY_PENALTY = 0.7 |
| MAX_TOKENS = 1024 |
| UNIQUE_RESPONSES = set() |
|
|
| |
| musicgen_pipeline = pipeline("text-to-audio", model="facebook/musicgen-small") |
| image_pipeline = pipeline("text-to-image", model="black-forest-labs/FLUX.1-schnell") |
|
|
| |
| def store_in_redis(key: str, value: Any): |
| if isinstance(value, bytes): |
| redis_client.set(key, value) |
| else: |
| redis_client.set(key, str(value)) |
|
|
| |
| def retrieve_from_redis(key: str): |
| value = redis_client.get(key) |
| if value is None: |
| return None |
| try: |
| return value.encode('latin1') |
| except AttributeError: |
| return value |
|
|
| |
| async def load_models(): |
| global model_dict, model_properties |
| if not model_dict: |
| for model_name in ["gpt2-medium", "gpt2-large", "gpt2", "google/gemma-2-9b", "meta-llama/Meta-Llama-3.1-8B-Instruct"]: |
| model_key = f"model:{model_name}" |
| tokenizer_key = f"tokenizer:{model_name}" |
| |
| model_data = retrieve_from_redis(model_key) |
| tokenizer_data = retrieve_from_redis(tokenizer_key) |
| |
| if model_data and tokenizer_data: |
| model = torch.load(io.BytesIO(model_data)) |
| tokenizer = torch.load(io.BytesIO(tokenizer_data)) |
| model_dict[model_name] = (model, tokenizer) |
| logger.info(f"Loaded {model_name} from Redis") |
| else: |
| model = AutoModelForCausalLM.from_pretrained(model_name) |
| tokenizer = AutoTokenizer.from_pretrained(model_name) |
| model_dict[model_name] = (model, tokenizer) |
| |
| |
| store_in_redis(model_key, torch.save(model, io.BytesIO())) |
| store_in_redis(tokenizer_key, torch.save(tokenizer, io.BytesIO())) |
| |
| model_properties[model_name] = { |
| 'pad_token': tokenizer.pad_token, |
| 'pad_token_id': tokenizer.pad_token_id, |
| 'eos_token': tokenizer.eos_token, |
| 'eos_token_id': tokenizer.eos_token_id, |
| 'bos_token': tokenizer.bos_token, |
| 'bos_token_id': tokenizer.bos_token_id, |
| 'unk_token': tokenizer.unk_token, |
| 'unk_token_id': tokenizer.unk_token_id, |
| 'padding_side': tokenizer.padding_side, |
| 'special_tokens_map': tokenizer.special_tokens_map, |
| 'model': model, |
| 'tokenizer': tokenizer |
| } |
| logger.info(f"Successfully loaded {model_name} model and tokenizer") |
|
|
| |
| asyncio.run(load_models()) |
|
|
| |
| def generate_music(prompt: str) -> bytes: |
| |
| audio = musicgen_pipeline(prompt)['audio'] |
| return audio |
|
|
| def generate_image(prompt: str) -> bytes: |
| |
| outputs = image_pipeline(prompt)["sample"][0] |
| buffered = io.BytesIO() |
| outputs.save(buffered, format="PNG") |
| return buffered.getvalue() |
|
|
| |
| @app.get('/') |
| async def main(): |
| html_code = """ |
| <!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="UTF-8"> |
| <meta http-equiv="X-UA-Compatible" content="IE=edge"> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| <title>ChatGPT Chatbot</title> |
| <style> |
| body, html { |
| height: 100%; |
| margin: 0; |
| padding: 0; |
| font-family: Arial, sans-serif; |
| } |
| .container { |
| height: 100%; |
| display: flex; |
| flex-direction: column; |
| justify-content: center; |
| align-items: center; |
| } |
| .chat-container { |
| border-radius: 10px; |
| overflow: hidden; |
| box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); |
| width: 100%; |
| height: 100%; |
| animation: fadeIn 1s ease; |
| } |
| .chat-box { |
| height: calc(100% - 60px); |
| overflow-y: auto; |
| padding: 10px; |
| } |
| .chat-input { |
| width: calc(100% - 100px); |
| padding: 10px; |
| border: none; |
| border-top: 1px solid #ccc; |
| font-size: 16px; |
| flex-grow: 1; |
| box-sizing: border-box; |
| } |
| .input-container { |
| display: flex; |
| align-items: center; |
| justify-content: space-between; |
| padding: 10px; |
| background-color: #f5f5f5; |
| border-top: 1px solid #ccc; |
| width: 100%; |
| box-sizing: border-box; |
| } |
| button { |
| padding: 10px; |
| border: none; |
| cursor: pointer; |
| background-color: #007bff; |
| color: #fff; |
| font-size: 16px; |
| flex-shrink: 0; |
| } |
| .user-message { |
| background-color: #cce5ff; |
| border-radius: 5px; |
| align-self: flex-end; |
| max-width: 70%; |
| margin-left: auto; |
| margin-right: 10px; |
| margin-bottom: 10px; |
| animation: slideInFromRight 0.5s ease; |
| } |
| .bot-message { |
| background-color: #d1ecf1; |
| border-radius: 5px; |
| align-self: flex-start; |
| max-width: 70%; |
| margin-bottom: 10px; |
| animation: slideInFromLeft 0.5s ease; |
| } |
| |
| @keyframes fadeIn { |
| 0% { |
| opacity: 0; |
| } |
| 100% { |
| opacity: 1; |
| } |
| } |
| |
| @keyframes slideInFromRight { |
| 0% { |
| transform: translateX(100%); |
| } |
| 100% { |
| transform: translateX(0); |
| } |
| } |
| |
| @keyframes slideInFromLeft { |
| 0% { |
| transform: translateX(-100%); |
| } |
| 100% { |
| transform: translateX(0); |
| } |
| } |
| </style> |
| </head> |
| <body> |
| <div class="container"> |
| <div class="chat-container"> |
| <div id="chat-box" class="chat-box"></div> |
| <div class="input-container"> |
| <input id="chat-input" class="chat-input" type="text" placeholder="Type your message here..."> |
| <button id="send-button">Send</button> |
| </div> |
| </div> |
| </div> |
| <script> |
| const chatBox = document.getElementById('chat-box'); |
| const chatInput = document.getElementById('chat-input'); |
| const sendButton = document.getElementById('send-button'); |
| |
| function appendMessage(text, isUser) { |
| const div = document.createElement('div'); |
| div.className = isUser ? 'user-message' : 'bot-message'; |
| div.innerHTML = text; |
| chatBox.appendChild(div); |
| chatBox.scrollTop = chatBox.scrollHeight; |
| } |
| |
| async function sendMessage() { |
| const message = chatInput.value; |
| if (message.trim() === '') return; |
| appendMessage(message, true); |
| chatInput.value = ''; |
| |
| const response = await fetch('/generate', { |
| method: 'POST', |
| headers: { |
| 'Content-Type': 'application/json', |
| }, |
| body: JSON.stringify({ query: message }), |
| }); |
| |
| const data = await response.json(); |
| if (data) { |
| appendMessage(data.responses.join('<br>'), false); |
| |
| if (data.music) { |
| const audio = new Audio('data:audio/wav;base64,' + data.music); |
| audio.play(); |
| } |
| |
| if (data.image) { |
| const img = document.createElement('img'); |
| img.src = 'data:image/png;base64,' + data.image; |
| chatBox.appendChild(img); |
| } |
| } |
| } |
| |
| sendButton.addEventListener('click', sendMessage); |
| chatInput.addEventListener('keypress', (e) => { |
| if (e.key === 'Enter') { |
| sendMessage(); |
| } |
| }); |
| </script> |
| </body> |
| </html> |
| """ |
| return HTMLResponse(content=html_code) |
|
|
| |
| @app.post('/generate') |
| async def generate_content(query: str): |
| async def generate_unique_response(q): |
| attempts = 0 |
| while attempts < 5: |
| responses = await generate_responses(q) |
| unique_responses = [response for response in responses if is_unique(response)] |
| if unique_responses: |
| parts = [] |
| for response in unique_responses: |
| parts.extend(split_response(response, model_properties[next(iter(model_dict))]['tokenizer'])) |
| return parts |
| attempts += 1 |
| raise HTTPException(status_code=500, detail="No unique response found after multiple attempts") |
|
|
| def is_unique(response): |
| if response in UNIQUE_RESPONSES: |
| return False |
| else: |
| UNIQUE_RESPONSES.add(response) |
| return True |
|
|
| async def generate_responses(q): |
| responses = [] |
| for model_name, (model, tokenizer) in model_dict.items(): |
| input_ids = tokenizer.encode(q, return_tensors='pt') |
| output = model.generate( |
| input_ids, |
| max_length=MAX_TOKENS, |
| num_return_sequences=1, |
| temperature=TEMPERATURE, |
| top_p=TOP_PROBABILITY, |
| top_k=TOP_K, |
| frequency_penalty=FREQUENCY_PENALTY |
| ) |
| response = tokenizer.decode(output[0], skip_special_tokens=True) |
| responses.append(response) |
| return responses |
|
|
| async def train_model(): |
| global TRAINING_DATA |
| if not TRAINING_DATA: |
| raise ValueError("No training data available") |
| |
| model_name = 'gpt2' |
| tokenizer = AutoTokenizer.from_pretrained(model_name) |
| model = AutoModelForCausalLM.from_pretrained(model_name) |
| training_args = TrainingArguments( |
| output_dir='./results', |
| per_device_train_batch_size=4, |
| num_train_epochs=1, |
| save_steps=10_000, |
| save_total_limit=2, |
| ) |
| trainer = Trainer( |
| model=model, |
| args=training_args, |
| train_dataset=TRAINING_DATA |
| ) |
| trainer.train() |
| |
| model_key = "model:trained" |
| tokenizer_key = "tokenizer:trained" |
| store_in_redis(model_key, torch.save(model, io.BytesIO())) |
| store_in_redis(tokenizer_key, torch.save(tokenizer, io.BytesIO())) |
| return model, tokenizer |
|
|
| async def auto_learn(): |
| global TRAINING_DATA |
| if message_history: |
| new_data = "\n".join(message_history) |
| TRAINING_DATA.append(new_data) |
| await train_model() |
|
|
| async def auto_learn_music(): |
| global MUSIC_TRAINING_DATA |
| if MUSIC_TRAINING_DATA: |
| inputs = musicgen_pipeline.tokenizer(MUSIC_TRAINING_DATA, return_tensors="pt", padding=True) |
| model = musicgen_pipeline.model |
| model.train() |
| optimizer = torch.optim.Adam(model.parameters(), lr=5e-5) |
| loss_fn = torch.nn.CrossEntropyLoss() |
|
|
| for epoch in range(1): |
| outputs = model(**inputs) |
| loss = loss_fn(outputs.logits, inputs['labels']) |
| optimizer.zero_grad() |
| loss.backward() |
| optimizer.step() |
|
|
| MUSIC_TRAINING_DATA = [] |
|
|
| async def auto_learn_images(): |
| global IMAGE_TRAINING_DATA |
| if IMAGE_TRAINING_DATA: |
| for image_data in IMAGE_TRAINING_DATA: |
| image = Image.open(io.BytesIO(image_data)) |
| image_tensor = torch.tensor(np.array(image)).unsqueeze(0) |
| |
| model = image_pipeline.model |
| model.train() |
| optimizer = torch.optim.Adam(model.parameters(), lr=1e-5) |
| loss_fn = torch.nn.MSELoss() |
| target_tensor = torch.zeros_like(image_tensor) |
| for epoch in range(1): |
| outputs = model(image_tensor) |
| loss = loss_fn(outputs, target_tensor) |
| optimizer.zero_grad() |
| loss.backward() |
| optimizer.step() |
| IMAGE_TRAINING_DATA = [] |
|
|
| def generate_music_from_api(prompt: str) -> bytes: |
| |
| audio = generate_music(prompt) |
| store_in_redis(f"music:{prompt}", audio) |
| return audio |
|
|
| def generate_image_from_api(prompt: str) -> bytes: |
| |
| image = generate_image(prompt) |
| store_in_redis(f"image:{prompt}", image) |
| return image |
|
|
| try: |
| tokenizer = model_properties[next(iter(model_dict))]['tokenizer'] |
| final_responses = await generate_unique_response(query) |
| await auto_learn() |
| await auto_learn_music() |
| await auto_learn_images() |
| music = generate_music_from_api(query) |
| image = generate_image_from_api(query) |
| |
| |
| buffered = io.BytesIO(image) |
| img_str = base64.b64encode(buffered.getvalue()).decode('utf-8') |
| |
| return {"responses": final_responses, "music": base64.b64encode(music).decode('utf-8'), "image": img_str} |
| except Exception as e: |
| logger.error(f"Error processing the request: {e}") |
| raise HTTPException(status_code=500, detail="Error processing the request") |
|
|
| |
| @app.post('/music') |
| async def generate_music_endpoint(prompt: str): |
| try: |
| music = generate_music_from_api(prompt) |
| return {"music": base64.b64encode(music).decode('utf-8')} |
| except Exception as e: |
| logger.error(f"Error generating music: {e}") |
| raise HTTPException(status_code=500, detail="Error generating music") |
|
|
| |
| @app.post('/image') |
| async def generate_image_endpoint(prompt: str): |
| try: |
| image = generate_image_from_api(prompt) |
| img_str = base64.b64encode(image).decode('utf-8') |
| return {"image": img_str} |
| except Exception as e: |
| logger.error(f"Error generating image: {e}") |
| raise HTTPException(status_code=500, detail="Error generating image") |
|
|
| if __name__ == "__main__": |
| import uvicorn |
| uvicorn.run(app, host="0.0.0.0", port=8000) |
|
|