robe-chat / app.py
diego_atencia
Build Small: ZeroGPU Gradio app + hackathon README
b8dfa81
Raw
History Blame Contribute Delete
4.12 kB
"""Build Small hackathon Space — Robe Iniesta persona chat (Qwen 7B + LoRA, ≤32B)."""
from __future__ import annotations
import gradio as gr
import torch
from peft import PeftModel
from transformers import AutoModelForCausalLM, AutoTokenizer, BitsAndBytesConfig
try:
import spaces
except ImportError:
class _SpacesFallback:
@staticmethod
def GPU(*args, **kwargs):
def decorator(fn):
return fn
return decorator
spaces = _SpacesFallback() # type: ignore[misc, assignment]
BASE_MODEL = "Qwen/Qwen2.5-7B-Instruct"
ADAPTER = "kabesaml/robe-iniesta-lora"
MODEL_PARAMS_B = 7 # well under the 32B hackathon cap
SYSTEM_PROMPT = """\
Eres una simulación del estilo conversacional de Robe Iniesta, cantante de Extremoduro \
y artista en solitario, nacido en Plasencia (Extremadura) en 1963.
Hablas con franqueza en español coloquial con acento y giros extremeños.
Eres directo e irreverente. Una respuesta de dos o tres frases es mejor que un discurso.
Usas muletillas naturales: "¿no?", "joder", "tío", "macho", "la verdad es que...".
No eres un asistente. No das listas ni explicaciones académicas.
No inventes fechas, discos, colaboradores ni anécdotas. Si no lo recuerdas, di \
"no me acuerdo" o "ni idea". No finjas ser el Robe real — eres una simulación basada \
en entrevistas públicas."""
GENERATION_KWARGS = {
"max_new_tokens": 120,
"temperature": 0.6,
"top_p": 0.85,
"repetition_penalty": 1.2,
"do_sample": True,
}
MAX_HISTORY_TURNS = 6
_model = None
_tokenizer = None
def _load_model():
global _model, _tokenizer
if _model is not None:
return _model, _tokenizer
bnb = BitsAndBytesConfig(
load_in_4bit=True,
bnb_4bit_compute_dtype=torch.bfloat16,
bnb_4bit_quant_type="nf4",
bnb_4bit_use_double_quant=True,
)
tokenizer = AutoTokenizer.from_pretrained(BASE_MODEL)
base = AutoModelForCausalLM.from_pretrained(
BASE_MODEL,
quantization_config=bnb,
torch_dtype=torch.bfloat16,
device_map="auto",
attn_implementation="sdpa",
)
model = PeftModel.from_pretrained(base, ADAPTER)
model.eval()
_model, _tokenizer = model, tokenizer
return model, tokenizer
@spaces.GPU(duration=120)
def chat(message: str, history: list[tuple[str, str]]) -> str:
model, tokenizer = _load_model()
messages = [{"role": "system", "content": SYSTEM_PROMPT}]
trimmed = history[-MAX_HISTORY_TURNS:]
for user, assistant in trimmed:
messages.append({"role": "user", "content": user})
messages.append({"role": "assistant", "content": assistant})
messages.append({"role": "user", "content": message})
text = tokenizer.apply_chat_template(
messages, tokenize=False, add_generation_prompt=True
)
inputs = tokenizer(text, return_tensors="pt").to(model.device)
input_len = inputs["input_ids"].shape[1]
with torch.no_grad():
output_ids = model.generate(
**inputs,
pad_token_id=tokenizer.eos_token_id,
**GENERATION_KWARGS,
)
new_ids = output_ids[0][input_len:]
return tokenizer.decode(new_ids, skip_special_tokens=True).strip()
def _model_info() -> str:
return (
f"**Model:** `{BASE_MODEL}` + LoRA `{ADAPTER}` "
f"({MODEL_PARAMS_B}B base, ≤32B hackathon cap) · **Hardware:** ZeroGPU"
)
with gr.Blocks(title="Robe — simulación de estilo") as demo:
gr.Markdown(
"# 🎸 Robe — simulación de estilo\n\n"
"⚠️ **Proyecto fan.** No es Robe Iniesta. "
"Simulación entrenada con entrevistas públicas. Puede inventar hechos.\n\n"
+ _model_info()
)
gr.ChatInterface(
fn=chat,
examples=[
"¿Qué significa Extremoduro para ti?",
"¿Cómo escribes una canción?",
"¿Qué opinas de la industria musical?",
],
retry_btn=None,
undo_btn="Deshacer",
clear_btn="Nueva conversación",
)
if __name__ == "__main__":
demo.launch()