"""Gradio chat for the C++ compiler-tuned model.""" from __future__ import annotations import os import re import torch import gradio as gr from peft import PeftModel from transformers import AutoModelForCausalLM, AutoTokenizer, TextIteratorStreamer from threading import Thread BASE_MODEL = os.environ.get("BASE_MODEL", "Qwen/Qwen2.5-1.5B-Instruct") SFT_ADAPTER = os.environ.get("SFT_ADAPTER", "gonzalolinares/qwen25-1.5b-cpp-sft") DPO_ADAPTER = os.environ.get("DPO_ADAPTER", "gonzalolinares/qwen25-1.5b-cpp-dpo") GRPO_ADAPTER = os.environ.get("GRPO_ADAPTER", "gonzalolinares/qwen25-1.5b-cpp-grpo") SYSTEM_PROMPT = ( "Eres un asistente que solo programa en C++ moderno (C++20). " "Responde siempre con un único bloque de código ```cpp``` completo y compilable primero, " "y después una breve explicación en español o inglés según el idioma del usuario." ) EXAMPLES = [ "Escribe un programa C++ que imprima hola en una línea.", "Crea un std::vector con {1,2,3} e imprime su tamaño con size().", "Ordena el vector {3,1,2} con std::sort e imprime los valores separados por espacio.", "Usa std::make_unique(42) e imprime el valor.", "Write a C++ program that prints the sum of 10 and 5.", ] print("Loading tokenizer...") tokenizer = AutoTokenizer.from_pretrained(BASE_MODEL) if tokenizer.pad_token is None: tokenizer.pad_token = tokenizer.eos_token device = "cuda" if torch.cuda.is_available() else "cpu" dtype = torch.float16 if device == "cuda" else torch.float32 print(f"Loading base model on {device}...") model = AutoModelForCausalLM.from_pretrained( BASE_MODEL, torch_dtype=dtype, device_map=device if device == "cuda" else None, low_cpu_mem_usage=True, ) print("Merging SFT adapter...") model = PeftModel.from_pretrained(model, SFT_ADAPTER) model = model.merge_and_unload() print("Loading DPO adapter...") model = PeftModel.from_pretrained(model, DPO_ADAPTER) model = model.merge_and_unload() print("Loading GRPO adapter...") model = PeftModel.from_pretrained(model, GRPO_ADAPTER) if device == "cpu": model = model.to(device) model.eval() print("Model ready.") def extract_cpp(text: str) -> str: m = re.search(r"```(?:cpp|c\+\+)?\s*([\s\S]*?)```", text, re.IGNORECASE) return m.group(1).strip() if m else "" def build_messages(history: list[list[str | None]], user_message: str) -> list[dict]: messages = [{"role": "system", "content": SYSTEM_PROMPT}] for user_msg, assistant_msg in history: if user_msg: messages.append({"role": "user", "content": user_msg}) if assistant_msg: messages.append({"role": "assistant", "content": assistant_msg}) messages.append({"role": "user", "content": user_message}) return messages def stream_reply(history: list, max_tokens: int, temperature: float): user_message = history[-1][0] messages = build_messages(history[:-1], user_message) text = tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True) inputs = tokenizer(text, return_tensors="pt").to(model.device) streamer = TextIteratorStreamer(tokenizer, skip_prompt=True, skip_special_tokens=True) gen_kwargs = dict( **inputs, streamer=streamer, max_new_tokens=int(max_tokens), do_sample=temperature > 0.01, temperature=max(float(temperature), 0.01), top_p=0.9, pad_token_id=tokenizer.eos_token_id, ) thread = Thread(target=model.generate, kwargs=gen_kwargs) thread.start() partial = "" for chunk in streamer: partial += chunk history[-1][1] = partial yield history, extract_cpp(partial) thread.join() with gr.Blocks(title="C++ Compiler Chat", theme=gr.themes.Soft()) as demo: gr.Markdown( """ # ⚙️ C++ Compiler Chat Modelo fine-tuned para **C++20** (`gonzalolinares/qwen25-1.5b-cpp-grpo` — SFT + DPO + GRPO con `g++`). Pide un programa en lenguaje natural; la respuesta empieza con ` ```cpp `. """ ) with gr.Row(): max_tokens = gr.Slider(64, 1024, value=512, step=64, label="Max tokens") temperature = gr.Slider(0.0, 1.0, value=0.1, step=0.05, label="Temperature") chatbot = gr.Chatbot(height=420, label="Chat", type="tuples") msg = gr.Textbox( placeholder="Ej: Escribe un programa que imprima los números del 1 al 5...", label="Tu mensaje", lines=2, ) code_preview = gr.Code(language="cpp", label="Código extraído", lines=14) with gr.Row(): send = gr.Button("Enviar", variant="primary") clear = gr.Button("Limpiar") gr.Examples(examples=[[e] for e in EXAMPLES], inputs=msg, label="Ejemplos") def add_message(user_message, history): if not user_message.strip(): return "", history return "", history + [[user_message, None]] def respond(history, max_tok, temp): yield from stream_reply(history, max_tok, temp) msg.submit(add_message, [msg, chatbot], [msg, chatbot], queue=False).then( respond, [chatbot, max_tokens, temperature], [chatbot, code_preview] ) send.click(add_message, [msg, chatbot], [msg, chatbot], queue=False).then( respond, [chatbot, max_tokens, temperature], [chatbot, code_preview] ) clear.click(lambda: ([], ""), None, [chatbot, code_preview]) if __name__ == "__main__": demo.queue(max_size=8).launch()