Spaces:
Sleeping
Sleeping
| import gradio as gr | |
| import torch | |
| from transformers import AutoTokenizer, AutoModelForCausalLM | |
| from peft import PeftModel | |
| # 1. Configurazioni iniziali | |
| BASE_MODEL = "Qwen/Qwen2.5-1.5B-Instruct" | |
| ADAPTER_CIRO = "YecoAI/napoletano-ciro" | |
| ADAPTER_GIOAN = "YecoAI/piemontese-gioan" | |
| print("Caricamento del Tokenizer...") | |
| tokenizer = AutoTokenizer.from_pretrained(BASE_MODEL) | |
| print("Caricamento del Modello Base (Qwen2.5-1.5B)...") | |
| base_model = AutoModelForCausalLM.from_pretrained( | |
| BASE_MODEL, | |
| dtype=torch.float32, | |
| device_map="cpu" | |
| ) | |
| print("Caricamento degli Adapter LoRA...") | |
| model = PeftModel.from_pretrained(base_model, ADAPTER_CIRO, adapter_name="ciro") | |
| model.load_adapter(ADAPTER_GIOAN, adapter_name="gioan") | |
| def respond(message, history, persona): | |
| selected_persona = persona if persona is not None else "🌋 Ciro (Napoletano)" | |
| if "Ciro" in selected_persona: | |
| model.set_adapter("ciro") | |
| system_prompt = "Sei Ciro, un ragazzo di Napoli verace, amichevole e ironico. Parli in dialetto napoletano. Rispondi in modo breve e simpatico." | |
| else: | |
| model.set_adapter("gioan") | |
| system_prompt = "Sei Gioan, un signore del Piemonte cordiale, pacato e ironico. Parli in dialetto piemontese. Rispondi in modo breve e gentile." | |
| messages = [{"role": "system", "content": system_prompt}] | |
| # Gradio 6.0 può inviare history come lista di dizionari con content come stringa o lista | |
| for msg in history: | |
| content = msg.get("content", "") | |
| if isinstance(content, list): | |
| # Estraiamo il testo dal formato lista di dict | |
| text_content = " ".join([item.get("text", "") for item in content if item.get("type") == "text"]) | |
| messages.append({"role": msg["role"], "content": text_content}) | |
| else: | |
| messages.append({"role": msg["role"], "content": content}) | |
| messages.append({"role": "user", "content": message}) | |
| text = tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True) | |
| model_inputs = tokenizer([text], return_tensors="pt").to(model.device) | |
| with torch.no_grad(): | |
| generated_ids = model.generate( | |
| **model_inputs, | |
| max_new_tokens=150, | |
| temperature=0.8, | |
| top_p=0.9, | |
| repetition_penalty=1.1, | |
| pad_token_id=tokenizer.eos_token_id | |
| ) | |
| generated_ids = [output_ids[len(input_ids):] for input_ids, output_ids in zip(model_inputs.input_ids, generated_ids)] | |
| return tokenizer.batch_decode(generated_ids, skip_special_tokens=True)[0] | |
| # Interfaccia custom | |
| custom_css = """ | |
| .gradio-container { | |
| background-color: #0f1115 !important; | |
| font-family: 'Inter', system-ui, -apple-system, sans-serif !important; | |
| } | |
| .main-header { | |
| text-align: center; | |
| padding: 40px 20px; | |
| background: linear-gradient(135deg, #1e293b 0%, #0f172a 100%); | |
| border-radius: 20px; | |
| margin-bottom: 30px; | |
| border: 1px solid #334155; | |
| } | |
| .chatbot-container { | |
| border-radius: 20px !important; | |
| border: 1px solid #334155 !important; | |
| overflow: hidden; | |
| } | |
| .persona-card { | |
| background: #1e293b; | |
| padding: 15px; | |
| border-radius: 15px; | |
| border: 1px solid #334155; | |
| } | |
| .footer-text { | |
| text-align: center; | |
| margin-top: 30px; | |
| color: #94a3b8; | |
| font-size: 0.9rem; | |
| } | |
| """ | |
| with gr.Blocks() as demo: | |
| with gr.Column(elem_classes="main-header"): | |
| gr.HTML(""" | |
| <h1 style="color: #f97316; font-size: 3.2rem; font-weight: 900; margin-bottom: 10px; letter-spacing: -1px;"> | |
| YecoAI <span style="color: #fff; font-weight: 300;">| Ciro & Gioan</span> | |
| </h1> | |
| <p style="color: #cbd5e1; font-size: 1.2rem; max-width: 600px; margin: 0 auto; line-height: 1.6;"> | |
| La prima intelligenza artificiale a parlare il dialetto in modo ironico. <br> | |
| <span style="font-style: italic; opacity: 0.8;">L'unica AI che ti risponde col cuore (e un po' di pregiudizio regionale).</span> | |
| </p> | |
| """) | |
| with gr.Row(): | |
| with gr.Column(scale=1): | |
| with gr.Column(elem_classes="persona-card"): | |
| persona_radio = gr.Radio( | |
| choices=["🌋 Ciro (Napoletano)", "🍷 Gioan (Piemontese)"], | |
| value="🌋 Ciro (Napoletano)", | |
| label="Seleziona la Personalità", | |
| info="Il cambio di personalità resetterà la conversazione." | |
| ) | |
| gr.Markdown(""" | |
| ### 💡 Info Rapide | |
| - **Ciro**: Napoletano, ironico, probabilmente sta mangiando una sfogliatella. | |
| - **Gioan**: Piemontese, garbato, ti corregge la grammatica con eleganza. | |
| """) | |
| with gr.Column(scale=3): | |
| chatbot = gr.Chatbot( | |
| label="Regional Conversation", | |
| height=500, | |
| avatar_images=(None, "https://www.yecoai.com/favicon.ico"), | |
| elem_classes="chatbot-container" | |
| ) | |
| # Definiamo i componenti prima ma senza renderizzarli subito | |
| msg = gr.Textbox( | |
| placeholder="Type your message here... (e.g. 'Uè wagliò' or 'Bondì binel')", | |
| label=None, | |
| container=False, | |
| scale=10, | |
| render=False | |
| ) | |
| submit = gr.Button("Send 🚀", variant="primary", scale=2, render=False) | |
| # Suggerimenti di messaggi (Esempi) sopra l'input, stile ChatGPT | |
| gr.Examples( | |
| examples=[ | |
| ["Uè Ciro, comm'a va?"], | |
| ["Mi consigli un posto dove mangiare una pizza vera?"], | |
| ["Bondì Gioan, com'a va?"], | |
| ["Gioan, cosa vuol dire 'bogia nen'?"], | |
| ], | |
| inputs=msg, | |
| label=None | |
| ) | |
| with gr.Row(): | |
| msg.render() | |
| submit.render() | |
| with gr.Row(): | |
| clear = gr.Button("🗑️ Reset Chat", variant="secondary", size="sm") | |
| # Rimosso il vecchio Row degli esempi | |
| # Logica di reset al cambio modello | |
| def reset_chat(): | |
| return [], "" | |
| persona_radio.change(fn=reset_chat, inputs=None, outputs=[chatbot, msg]) | |
| # Logica di invio (Formato Gradio 6.0: lista di dict) | |
| def user_msg(user_input, history): | |
| if not user_input: return "", history | |
| history.append({"role": "user", "content": user_input}) | |
| return "", history | |
| def bot_msg(history, persona): | |
| if not history or history[-1]["role"] == "assistant": return history | |
| # Estraiamo l'ultimo messaggio dell'utente correttamente | |
| last_user_msg = history[-1]["content"] | |
| if isinstance(last_user_msg, list): | |
| last_user_msg = " ".join([item.get("text", "") for item in last_user_msg if item.get("type") == "text"]) | |
| bot_response = respond(last_user_msg, history[:-1], persona) | |
| history.append({"role": "assistant", "content": bot_response}) | |
| return history | |
| msg.submit(user_msg, [msg, chatbot], [msg, chatbot], queue=False).then( | |
| bot_msg, [chatbot, persona_radio], chatbot | |
| ) | |
| submit.click(user_msg, [msg, chatbot], [msg, chatbot], queue=False).then( | |
| bot_msg, [chatbot, persona_radio], chatbot | |
| ) | |
| clear.click(lambda: [], None, chatbot, queue=False) | |
| gr.HTML(""" | |
| <div class="footer-text"> | |
| © 2026 <a href="https://yecoai.com" style="color: #f97316; text-decoration: none; font-weight: bold;">YecoAI Research</a> | |
| • All rights reserved • Open Source on Hugging Face | |
| </div> | |
| """) | |
| if __name__ == "__main__": | |
| demo.launch( | |
| server_name="0.0.0.0", | |
| server_port=7860, | |
| theme=gr.themes.Soft(primary_hue="orange", secondary_hue="slate"), | |
| css=custom_css | |
| ) | |