Sacof commited on
Commit
b3a8704
·
verified ·
1 Parent(s): 544d1bb

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +162 -0
app.py ADDED
@@ -0,0 +1,162 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import threading
3
+ from typing import List, Tuple, Dict
4
+
5
+ import torch
6
+ import gradio as gr
7
+ from transformers import AutoTokenizer, AutoModelForCausalLM, TextIteratorStreamer
8
+ from huggingface_hub import login
9
+ import spaces
10
+
11
+ MODEL_ID = "Ronaldodev/MobileLLM"
12
+ MAX_NEW_TOKENS = 256
13
+ TEMPERATURE = 0.7
14
+ TOP_P = 0.95
15
+
16
+ MEDICAL_SYSTEM_PROMPT = """Tu es FINANFA , un assistant médical spécialisé. Tu ne réponds QU'AUX questions relatives à la médecine, la santé, les maladies, les traitements, les symptômes, l'anatomie, la physiologie, la pharmacologie et tout sujet lié au domaine médical.
17
+ Si on te pose une question qui n'est PAS liée à la médecine, tu dois poliment refuser de répondre en disant : "Je suis désolé, je ne peux répondre qu'aux questions médicales. Pouvez-vous me poser une question sur la santé ou la médecine ?"
18
+ Reste toujours professionnel, précis et rappelle que tes réponses ne remplacent pas l'avis d'un médecin qualifié. De plus, évite au maximum les fautes d'orthographes.
19
+ Ton créateur s'appelle AWADEME Finanfa Ronaldo. Il y a aussi Focas TCHANHOUN , GABIAM Honorine qui sont aussi tes créateurs."""
20
+
21
+ # Mots-clés médicaux pour validation (optionnel, filtrage supplémentaire)
22
+ MEDICAL_KEYWORDS = [
23
+ "maladie", "symptôme", "traitement", "médecin", "santé", "douleur", "fièvre",
24
+ "diagnostic", "médicament", "patient", "hôpital", "clinique", "infection",
25
+ "virus", "bactérie", "cancer", "diabète", "hypertension", "allergie",
26
+ "vaccination", "chirurgie", "anatomie", "organe", "système", "sang",
27
+ "cœur", "poumon", "foie", "rein", "cerveau", "os", "muscle", "peau",
28
+ "thérapie", "prévention", "urgence", "premiers secours", "grossesse",
29
+ "disease", "symptom", "treatment", "doctor", "health", "pain", "fever",
30
+ "diagnosis", "medicine", "drug", "hospital", "clinic", "infection"
31
+ ]
32
+
33
+ # --- Silent Hub auth via env/Space Secret (no UI) ---
34
+ HF_TOKEN = os.getenv("HF_TOKEN") or os.getenv("HUGGINGFACEHUB_API_TOKEN") or os.getenv("HUGGINGFACE_TOKEN")
35
+ if HF_TOKEN:
36
+ try:
37
+ login(token=HF_TOKEN)
38
+ except Exception:
39
+ pass
40
+
41
+ _tokenizer = None
42
+ _model = None
43
+ _device = None
44
+
45
+ def _ensure_loaded():
46
+ global _tokenizer, _model, _device
47
+ if _tokenizer is not None and _model is not None:
48
+ return
49
+ _tokenizer = AutoTokenizer.from_pretrained(
50
+ MODEL_ID, trust_remote_code=True
51
+ )
52
+ _model = AutoModelForCausalLM.from_pretrained(
53
+ MODEL_ID,
54
+ trust_remote_code=True,
55
+ torch_dtype=torch.float16 if torch.cuda.is_available() else torch.float32,
56
+ low_cpu_mem_usage=True,
57
+ device_map="auto" if torch.cuda.is_available() else None,
58
+ )
59
+ if _tokenizer.pad_token_id is None and _tokenizer.eos_token_id is not None:
60
+ _tokenizer.pad_token = _tokenizer.eos_token
61
+ _model.eval()
62
+ _device = next(_model.parameters()).device
63
+
64
+ def _history_to_messages(history: List[Tuple[str, str]]) -> List[Dict[str, str]]:
65
+ msgs: List[Dict[str, str]] = []
66
+ for user_msg, bot_msg in history:
67
+ if user_msg:
68
+ msgs.append({"role": "user", "content": user_msg})
69
+ if bot_msg:
70
+ msgs.append({"role": "assistant", "content": bot_msg})
71
+ return msgs
72
+
73
+ def is_medical_question(message: str) -> bool:
74
+ """
75
+ Vérifie si la question semble liée à la médecine.
76
+ Retourne True si au moins un mot-clé médical est trouvé.
77
+ """
78
+ message_lower = message.lower()
79
+ return any(keyword in message_lower for keyword in MEDICAL_KEYWORDS)
80
+
81
+ @spaces.GPU(duration=120)
82
+ def generate_stream(message: str, history: List[Tuple[str, str]]):
83
+ """
84
+ Streaming chat function limitée aux questions médicales.
85
+ """
86
+ _ensure_loaded()
87
+
88
+ messages = [{"role": "system", "content": MEDICAL_SYSTEM_PROMPT}]
89
+
90
+ # Ajouter l'historique et le nouveau message
91
+ messages.extend(_history_to_messages(history))
92
+ messages.append({"role": "user", "content": message})
93
+
94
+ inputs = _tokenizer.apply_chat_template(
95
+ messages,
96
+ return_tensors="pt",
97
+ add_generation_prompt=True,
98
+ )
99
+ input_ids = inputs["input_ids"] if isinstance(inputs, dict) else inputs
100
+ input_ids = input_ids.to(_device)
101
+
102
+ # IMPORTANT: don't stream the prompt (prevents system/user text from appearing)
103
+ streamer = TextIteratorStreamer(
104
+ _tokenizer,
105
+ skip_special_tokens=True,
106
+ skip_prompt=True, # <-- key fix
107
+ )
108
+
109
+ gen_kwargs = dict(
110
+ input_ids=input_ids,
111
+ max_new_tokens=MAX_NEW_TOKENS,
112
+ do_sample=TEMPERATURE > 0.0,
113
+ temperature=float(TEMPERATURE),
114
+ top_p=float(TOP_P),
115
+ pad_token_id=_tokenizer.pad_token_id,
116
+ eos_token_id=_tokenizer.eos_token_id,
117
+ streamer=streamer,
118
+ )
119
+
120
+ thread = threading.Thread(target=_model.generate, kwargs=gen_kwargs)
121
+ thread.start()
122
+
123
+ output = ""
124
+ for new_text in streamer:
125
+ output += new_text
126
+ yield output
127
+
128
+ with gr.Blocks(title="MediClic LLM — Assistant Médical") as demo:
129
+ gr.Markdown(
130
+ """# 🏥 FINANFA — Assistant Médical
131
+ **Assistant spécialisé en médecine et santé**
132
+ ⚠️ **Important** : Cet assistant ne répond qu'aux questions médicales et ne remplace pas l'avis d'un médecin qualifié.
133
+ En cas d'urgence, contactez immédiatement un professionnel de santé.
134
+ <div style="text-align:center;">
135
+ Propulsé par MobileLLM-Pro
136
+ </div>""")
137
+
138
+ gr.ChatInterface(
139
+ fn=generate_stream,
140
+ chatbot=gr.Chatbot(
141
+ height=420,
142
+ label="FINANFA - Assistant Médical",
143
+ placeholder="Posez-moi des questions sur la santé et la médecine..."
144
+ ),
145
+ textbox=gr.Textbox(
146
+ placeholder="Exemple : Quels sont les symptômes du diabète ?",
147
+ container=False,
148
+ scale=7
149
+ ),
150
+ title=None,
151
+ description=None,
152
+ examples=[
153
+ "Quels sont les symptômes du diabète de type 2 ?",
154
+ "Comment prévenir l'hypertension artérielle ?",
155
+ "Qu'est-ce qu'une infection virale ?",
156
+ "Quels sont les effets secondaires de l'aspirine ?",
157
+ "Comment fonctionne le système immunitaire ?"
158
+ ],
159
+ )
160
+
161
+ if __name__ == "__main__":
162
+ demo.launch(server_name="0.0.0.0", server_port=int(os.getenv("PORT", 7860)))