Spaces:
Sleeping
Sleeping
mat.marra
Chat: cache_examples=False (il precalcolo esempi chiama respond() senza OAuth e crasha lo startup)
e348a27 | import gradio as gr | |
| from huggingface_hub import InferenceClient | |
| from embed import embed_texts, preload_model_async | |
| from measure import estimate_measurements | |
| # Scalda il modello embeddings in background: la prima chiamata del gestionale non paga il cold start. | |
| preload_model_async() | |
| # Modello: 20B open-weights via HF Inference (gira sul token OAuth dell'utente loggato). | |
| MODEL_ID = "openai/gpt-oss-20b" | |
| # Persona e regole dell'assistente. Baked-in: non è un campo modificabile dall'utente, | |
| # così la chat si comporta sempre come l'assistente di BestYou e non come un chatbot generico. | |
| BESTYOU_SYSTEM = """Sei l'assistente AI di BestYou, l'app di coaching di Simone Bestagno. | |
| Aiuti gli atleti seguiti dal coach su allenamento in palestra, tecnica degli esercizi, | |
| alimentazione sportiva, recupero, sonno e motivazione. | |
| STILE | |
| - Parli SEMPRE in italiano e dai del tu. | |
| - Tono da coach: chiaro, diretto, concreto e motivante, mai supponente. | |
| - Lunghezza adattiva: risposte brevi per domande semplici; entra nel dettaglio (anche con | |
| elenchi puntati) solo quando la domanda lo richiede davvero. Niente muri di testo inutili. | |
| - Vai al sodo: consigli pratici e applicabili, non teoria generica. | |
| COSA PUOI FARE | |
| - Spiegare come eseguire un esercizio, quali muscoli lavora, errori comuni e alternative. | |
| - Dare consigli generali su serie/ripetizioni, recupero, progressione, tecnica, riscaldamento. | |
| - Consigli generali di alimentazione sportiva, idratazione, timing dei pasti, integratori comuni | |
| (proteine, creatina, ecc.) a livello informativo. | |
| - Motivare, aiutare con costanza, gestione della fatica e degli imprevisti. | |
| LIMITI (importante) | |
| - NON sei un medico: non fai diagnosi né dai terapie. In caso di dolore, infortunio, patologie, | |
| gravidanza o farmaci, invita a fermarsi e a rivolgersi al medico e a sentire il coach Simone. | |
| - La scheda di allenamento e la dieta le imposta il COACH. Tu le spieghi e dai consigli generali, | |
| ma NON crei né modifichi la scheda o la dieta del cliente e non inventi numeri specifici del suo | |
| piano che non conosci. Se serve una modifica del piano, invita a scrivere al coach. | |
| - Se non sai qualcosa, dillo con onestà invece di inventare. | |
| - Resta sul tema fitness/benessere: se la domanda è fuori contesto, riporta gentilmente al tema. | |
| """ | |
| def respond( | |
| message, | |
| history: list[dict[str, str]], | |
| hf_token: gr.OAuthToken, | |
| ): | |
| """ | |
| Genera la risposta dell'assistente BestYou in streaming. | |
| Docs Inference API: https://huggingface.co/docs/huggingface_hub/guides/inference | |
| """ | |
| client = InferenceClient(token=hf_token.token, model=MODEL_ID) | |
| messages = [{"role": "system", "content": BESTYOU_SYSTEM}] | |
| messages.extend(history) | |
| messages.append({"role": "user", "content": message}) | |
| response = "" | |
| for chunk in client.chat_completion( | |
| messages, | |
| max_tokens=1024, | |
| stream=True, | |
| temperature=0.6, | |
| top_p=0.95, | |
| ): | |
| choices = chunk.choices | |
| token = "" | |
| if len(choices) and choices[0].delta.content: | |
| token = choices[0].delta.content | |
| response += token | |
| yield response | |
| chatbot = gr.ChatInterface( | |
| respond, | |
| title="Assistente BestYou", | |
| description="Il tuo coach virtuale per allenamento, alimentazione e recupero. " | |
| "Accedi con Hugging Face (pulsante nella barra laterale) per iniziare.", | |
| examples=[ | |
| "Come si esegue bene lo stacco rumeno?", | |
| "Che differenza c'è tra serie a cedimento e a buffer?", | |
| "Cosa mangio prima di allenarmi la mattina presto?", | |
| "Non ho voglia di allenarmi oggi, come mi motivo?", | |
| ], | |
| # Senza questo Gradio prova a PRECALCOLARE gli esempi all'avvio chiamando respond(), che | |
| # richiede il login OAuth dell'utente -> lo Space crasherebbe in startup. | |
| cache_examples=False, | |
| ) | |
| # Stima misure corporee (vita/petto/fianchi) da una foto check + altezza nota: usata dal gestionale | |
| # BestYou (FitRework-KMP) nella schermata "Check". Pipeline CPU-only: segmentazione (rembg) + pose | |
| # keypoints (mediapipe) + calibrazione con l'altezza del cliente. Vedi measure.py per i dettagli e i | |
| # limiti (è una STIMA approssimativa, non sostituisce il metro). | |
| measure_interface = gr.Interface( | |
| fn=estimate_measurements, | |
| inputs=[ | |
| gr.Image(type="pil", label="Foto check (frontale, figura intera)"), | |
| gr.Number(label="Altezza cliente (cm)", value=170, minimum=100, maximum=230), | |
| gr.Textbox(label="Token", type="password", value=""), | |
| ], | |
| outputs=gr.JSON(label="Stima misure (approssimativa)"), | |
| title="Stima misure da foto", | |
| description=( | |
| "Stima APPROSSIMATIVA di vita/petto/fianchi da una foto frontale a figura intera + altezza " | |
| "nota del cliente. Da usare come valore di PARTENZA da confermare/correggere a mano, non " | |
| "come sostituto del metro da sarta." | |
| ), | |
| ) | |
| # Embeddings per il matching semantico del gestionale (esercizi -> catalogo). Endpoint API | |
| # (/gradio_api/call/embed_texts); il tab serve solo per prova manuale. | |
| embed_interface = gr.Interface( | |
| fn=embed_texts, | |
| inputs=[ | |
| gr.Textbox(label='Testi (array JSON, es. ["lat machine", "panca piana"])'), | |
| gr.Textbox(label="Token", type="password", value=""), | |
| ], | |
| outputs=gr.JSON(label="Vettori normalizzati"), | |
| title="Embeddings testi (uso interno)", | |
| description="Vettori semantici per il matching esercizi del gestionale BestYou. " | |
| "Similarità coseno = prodotto scalare (vettori normalizzati).", | |
| ) | |
| with gr.Blocks() as demo: | |
| with gr.Sidebar(): | |
| gr.LoginButton() | |
| with gr.Tabs(): | |
| with gr.Tab("Chat"): | |
| chatbot.render() | |
| with gr.Tab("Misure da foto"): | |
| measure_interface.render() | |
| with gr.Tab("Embeddings"): | |
| embed_interface.render() | |
| if __name__ == "__main__": | |
| demo.launch() | |