|
import os |
|
import time |
|
import uuid |
|
from typing import List, Tuple, Optional, Union |
|
from PIL import Image |
|
import google.generativeai as genai |
|
import gradio as gr |
|
from dotenv import load_dotenv |
|
|
|
|
|
load_dotenv() |
|
API_KEY = os.getenv("GOOGLE_API_KEY") |
|
|
|
if not API_KEY: |
|
raise ValueError("La clave de API 'GOOGLE_API_KEY' no est谩 configurada en el archivo .env") |
|
|
|
|
|
genai.configure(api_key=API_KEY) |
|
generation_config = { |
|
"temperature": 0.7, |
|
"top_p": 0.9, |
|
"top_k": 40, |
|
"max_output_tokens": 8192, |
|
"response_mime_type": "text/plain", |
|
} |
|
|
|
model = genai.GenerativeModel( |
|
model_name="gemini-1.5-flash", |
|
generation_config=generation_config, |
|
) |
|
|
|
|
|
chat = model.start_chat(history=[]) |
|
|
|
|
|
IMAGE_CACHE_DIRECTORY = "/tmp" |
|
IMAGE_WIDTH = 512 |
|
CHAT_HISTORY = List[Tuple[Optional[Union[Tuple[str], str]], Optional[str]]] |
|
|
|
|
|
def preprocess_image(image: Image.Image) -> Optional[Image.Image]: |
|
"""Redimensiona una imagen manteniendo la relaci贸n de aspecto.""" |
|
if image: |
|
image_height = int(image.height * IMAGE_WIDTH / image.width) |
|
return image.resize((IMAGE_WIDTH, image_height)) |
|
|
|
|
|
def cache_pil_image(image: Image.Image) -> str: |
|
"""Guarda la imagen como archivo JPEG en un directorio temporal.""" |
|
image_filename = f"{uuid.uuid4()}.jpeg" |
|
os.makedirs(IMAGE_CACHE_DIRECTORY, exist_ok=True) |
|
image_path = os.path.join(IMAGE_CACHE_DIRECTORY, image_filename) |
|
image.save(image_path, "JPEG") |
|
return image_path |
|
|
|
|
|
def transform_history(history): |
|
"""Transforma el historial del formato de Gradio al formato que Gemini espera.""" |
|
new_history = [] |
|
for chat in history: |
|
if chat[0]: |
|
new_history.append({"parts": [{"text": chat[0]}], "role": "user"}) |
|
if chat[1]: |
|
new_history.append({"parts": [{"text": chat[1]}], "role": "model"}) |
|
return new_history |
|
|
|
|
|
def response(message, history): |
|
"""Maneja la interacci贸n multimodal y env铆a texto e im谩genes al modelo.""" |
|
global chat |
|
|
|
|
|
chat.history = transform_history(history) |
|
|
|
|
|
text_prompt = message["text"] |
|
files = message["files"] |
|
|
|
|
|
image_prompts = [preprocess_image(Image.open(file).convert('RGB')) for file in files] if files else [] |
|
if files: |
|
for file in files: |
|
image = Image.open(file).convert('RGB') |
|
image_preview = preprocess_image(image) |
|
if image_preview: |
|
|
|
image_path = cache_pil_image(image) |
|
|
|
with open(image_path, "rb") as img_file: |
|
img_data = img_file.read() |
|
|
|
image_prompt = { |
|
"mime_type": "image/jpeg", |
|
"data": img_data |
|
} |
|
image_prompts.append(image_prompt) |
|
|
|
|
|
prompts = [text_prompt] + image_prompts |
|
response = chat.send_message(prompts) |
|
response.resolve() |
|
|
|
|
|
for i in range(len(response.text)): |
|
time.sleep(0.01) |
|
yield response.text[: i + 1] |
|
|
|
|
|
def vote(data: gr.LikeData): |
|
"""Procesa la retroalimentaci贸n del usuario (like/dislike).""" |
|
if data.liked: |
|
print(f"Respuesta aprobada: " + data.value["value"]) |
|
else: |
|
print(f"Respuesta desaprobada: " + data.value["value"]) |
|
|
|
|
|
with gr.Blocks() as demo: |
|
chatbot = gr.Chatbot(label="Chat con Gemini") |
|
chatbot.like(vote, None, None) |
|
|
|
gr.ChatInterface( |
|
fn=response, |
|
type="messages", |
|
chatbot=chatbot, |
|
) |
|
|
|
|
|
if __name__ == "__main__": |
|
demo.launch(debug=True, show_error=True) |
|
|