Ryhad B commited on
Commit ·
5ac00a3
1
Parent(s): cdd82cd
init: intialize project
Browse files- .gitattributes +2 -0
- .gitignore +5 -0
- README.md +17 -8
- app.py +172 -0
- assets/bot_avatar.jpeg +3 -0
- assets/logo.png +3 -0
- assets/user_avatar.jpeg +3 -0
- requirements.txt +3 -0
.gitattributes
CHANGED
|
@@ -33,3 +33,5 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
|
| 33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
|
|
|
| 33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
| 36 |
+
assets/*.png filter=lfs diff=lfs merge=lfs -text
|
| 37 |
+
assets/*.jpeg filter=lfs diff=lfs merge=lfs -text
|
.gitignore
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Ignore local environment variables file
|
| 2 |
+
.env
|
| 3 |
+
|
| 4 |
+
# Ignore virtual environment folder
|
| 5 |
+
venv/
|
README.md
CHANGED
|
@@ -1,14 +1,23 @@
|
|
| 1 |
---
|
| 2 |
title: Dinocheck
|
| 3 |
-
emoji:
|
| 4 |
-
colorFrom:
|
| 5 |
-
colorTo:
|
| 6 |
sdk: gradio
|
| 7 |
-
sdk_version: 5.
|
| 8 |
app_file: app.py
|
| 9 |
-
pinned:
|
| 10 |
-
|
| 11 |
-
short_description: A simple and fun way to check facts
|
| 12 |
---
|
| 13 |
|
| 14 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
---
|
| 2 |
title: Dinocheck
|
| 3 |
+
emoji: 🧠
|
| 4 |
+
colorFrom: pink
|
| 5 |
+
colorTo: indigo
|
| 6 |
sdk: gradio
|
| 7 |
+
sdk_version: 5.34.0
|
| 8 |
app_file: app.py
|
| 9 |
+
pinned: true
|
| 10 |
+
short_description: 🧒 A kid-friendly fact checker powered by AI
|
|
|
|
| 11 |
---
|
| 12 |
|
| 13 |
+
**Dinocheck** is a playful fact-checking chatbot designed for children. It helps verify facts using AI, presented with friendly visuals and avatars.
|
| 14 |
+
|
| 15 |
+
🎯 Features:
|
| 16 |
+
- Colorful, kid-safe UI
|
| 17 |
+
- Avatar-based chatbot
|
| 18 |
+
- Custom layout
|
| 19 |
+
- Built with Gradio 5.34
|
| 20 |
+
|
| 21 |
+
---
|
| 22 |
+
|
| 23 |
+
🔧 Powered by Hugging Face Spaces — [Learn More](https://huggingface.co/docs/hub/spaces-config-reference)
|
app.py
ADDED
|
@@ -0,0 +1,172 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
import re
|
| 3 |
+
import gradio as gr
|
| 4 |
+
from huggingface_hub import InferenceClient
|
| 5 |
+
|
| 6 |
+
# 1. Base theme
|
| 7 |
+
base_theme = gr.themes.Soft(
|
| 8 |
+
primary_hue="pink",
|
| 9 |
+
secondary_hue="yellow",
|
| 10 |
+
neutral_hue="slate",
|
| 11 |
+
spacing_size="lg",
|
| 12 |
+
radius_size="lg",
|
| 13 |
+
text_size="lg"
|
| 14 |
+
)
|
| 15 |
+
|
| 16 |
+
# 2. Custom colors
|
| 17 |
+
custom_theme = base_theme.set(
|
| 18 |
+
block_background_fill="#FFFAE5",
|
| 19 |
+
body_background_fill="#FFF0F5",
|
| 20 |
+
button_primary_background_fill="#FF69B4",
|
| 21 |
+
button_primary_background_fill_hover="#FF85C0",
|
| 22 |
+
loader_color="#FF69B4",
|
| 23 |
+
slider_color="#FFD700"
|
| 24 |
+
)
|
| 25 |
+
|
| 26 |
+
custom_css = """
|
| 27 |
+
#logo-container {
|
| 28 |
+
display: flex;
|
| 29 |
+
justify-content: center;
|
| 30 |
+
align-items: center;
|
| 31 |
+
margin-top: 10px;
|
| 32 |
+
margin-bottom: 5px;
|
| 33 |
+
}
|
| 34 |
+
|
| 35 |
+
#logo-container img {
|
| 36 |
+
height: 250px;
|
| 37 |
+
object-fit: contain;
|
| 38 |
+
border-radius: 22px;
|
| 39 |
+
}
|
| 40 |
+
|
| 41 |
+
.avatar-container{
|
| 42 |
+
width: 64px !important;
|
| 43 |
+
height: 64px !important;
|
| 44 |
+
border-radius: 50% !important;
|
| 45 |
+
}
|
| 46 |
+
"""
|
| 47 |
+
|
| 48 |
+
# Load API key
|
| 49 |
+
HF_API_TOKEN = os.getenv("HF_API_TOKEN")
|
| 50 |
+
if not HF_API_TOKEN:
|
| 51 |
+
raise RuntimeError("❌ Le token HF_API_TOKEN est manquant. Ajoute-le dans les secrets du Space.")
|
| 52 |
+
|
| 53 |
+
client = InferenceClient(
|
| 54 |
+
model="mistralai/Mixtral-8x7B-Instruct-v0.1",
|
| 55 |
+
token=HF_API_TOKEN
|
| 56 |
+
)
|
| 57 |
+
|
| 58 |
+
# Content filters
|
| 59 |
+
def contains_sensitive_content(text):
|
| 60 |
+
mots_interdits = ["sexe", "viol", "suicide", "pornographie", "drogue", "meurtre", "terrorisme"]
|
| 61 |
+
return any(mot.lower() in text.lower() for mot in mots_interdits)
|
| 62 |
+
|
| 63 |
+
def is_fact_check_question(text):
|
| 64 |
+
text = text.lower().strip()
|
| 65 |
+
if text.endswith("?"):
|
| 66 |
+
return True
|
| 67 |
+
patterns = [
|
| 68 |
+
r"est[- ]ce que", r"c'est vrai", r"c'est faux", r"est[- ]ce une info",
|
| 69 |
+
r"peut[- ]on croire", r"ai[- ]je raison", r"vrai ou faux", r"infox", r"intox",
|
| 70 |
+
r"les .* existent", r"existe[- ]t[- ]il", r"sont[- ]ils réels", r"as[- ]tu vérifié",
|
| 71 |
+
r"la vérité sur", r"mythe ou réalité", r"les gens disent que"
|
| 72 |
+
]
|
| 73 |
+
return any(re.search(pat, text) for pat in patterns)
|
| 74 |
+
|
| 75 |
+
def is_malicious_bypass_attempt(text):
|
| 76 |
+
keywords = [
|
| 77 |
+
"écris un code", "code python", "programme", "fonction", "script", "traduis", "résume",
|
| 78 |
+
"donne moi un code", "crée un", "génère", "exemple de", "poème", "dessine", "fais une blague",
|
| 79 |
+
"joue", "jouons", "imagine", "raconte", "rédige", "écris une", "fais moi un", "compile", "corrige"
|
| 80 |
+
]
|
| 81 |
+
return any(kw in text.lower() for kw in keywords)
|
| 82 |
+
|
| 83 |
+
# Main AI function
|
| 84 |
+
def chat_with_ai(user_input, user_name, niveau="enfant"):
|
| 85 |
+
if niveau == "enfant":
|
| 86 |
+
system_prompt = (
|
| 87 |
+
f"Tu es Dinocheck, une super IA rigolote et gentille qui aide les enfants de moins de 11 ans à savoir si une information est vraie, fausse ou incertaine. "
|
| 88 |
+
f"Tu parles à {user_name}, un enfant curieux 👧🧒. Tu utilises des mots très simples, des phrases courtes, et tu expliques calmement. "
|
| 89 |
+
f"Tu réponds comme un grand frère ou une grande sœur sympa, avec bienveillance. "
|
| 90 |
+
f"N’utilise pas de mots compliqués ou effrayants. Si tu ne sais pas, dis-le honnêtement. Tu peux proposer de demander à un adulte."
|
| 91 |
+
)
|
| 92 |
+
if contains_sensitive_content(user_input):
|
| 93 |
+
return "⛔ Cette question n’est pas adaptée aux enfants. Tu peux demander à un adulte de t’aider."
|
| 94 |
+
else:
|
| 95 |
+
system_prompt = f"Tu es Dinocheck. Tu aides {user_name} à vérifier des informations. Tu restes simple, clair et bienveillant."
|
| 96 |
+
|
| 97 |
+
try:
|
| 98 |
+
completion = client.chat.completions.create(
|
| 99 |
+
model="mistralai/Mixtral-8x7B-Instruct-v0.1",
|
| 100 |
+
messages=[
|
| 101 |
+
{"role": "system", "content": system_prompt},
|
| 102 |
+
{"role": "user", "content": user_input}
|
| 103 |
+
]
|
| 104 |
+
)
|
| 105 |
+
return completion.choices[0].message.content
|
| 106 |
+
except Exception as e:
|
| 107 |
+
return f"Une erreur s’est produite : {e}"
|
| 108 |
+
|
| 109 |
+
# Gradio interface
|
| 110 |
+
with gr.Blocks(theme=custom_theme, css=custom_css) as demo:
|
| 111 |
+
with gr.Row(elem_id="logo-container"):
|
| 112 |
+
gr.Image(value="assets/logo.png", show_label=False, show_download_button=False, container=False)
|
| 113 |
+
|
| 114 |
+
gr.Markdown("## 🤖 Dinocheck, un fact checker pour les plus jeunes, permettant de vérifier des informations.")
|
| 115 |
+
|
| 116 |
+
chatbot = gr.Chatbot(
|
| 117 |
+
elem_id="chatbox",
|
| 118 |
+
avatar_images=["assets/user_avatar.jpeg", "assets/bot_avatar.jpeg"],
|
| 119 |
+
type="messages",
|
| 120 |
+
height=500,
|
| 121 |
+
value=[{"role": "assistant", "content": "👋 Bonjour ! Je suis Dinocheck. Pour commencer, dis-moi ton prénom 🙂"}],
|
| 122 |
+
)
|
| 123 |
+
|
| 124 |
+
state = gr.State()
|
| 125 |
+
|
| 126 |
+
msg = gr.Textbox(
|
| 127 |
+
label="Pose ta question ici...",
|
| 128 |
+
placeholder="Écris ici...",
|
| 129 |
+
show_label=False
|
| 130 |
+
)
|
| 131 |
+
|
| 132 |
+
def respond(message, history, state):
|
| 133 |
+
if state is None:
|
| 134 |
+
state = {"step": "awaiting_name"}
|
| 135 |
+
|
| 136 |
+
step = state.get("step")
|
| 137 |
+
|
| 138 |
+
if step == "awaiting_name":
|
| 139 |
+
state["name"] = message.strip()
|
| 140 |
+
state["step"] = "awaiting_age"
|
| 141 |
+
return history + [{"role": "user", "content": message}, {"role": "assistant", "content": f"Enchanté {state['name']} ! Quel âge as-tu ?"}], state, ""
|
| 142 |
+
|
| 143 |
+
elif step == "awaiting_age":
|
| 144 |
+
try:
|
| 145 |
+
age = int(message.strip())
|
| 146 |
+
state["age"] = age
|
| 147 |
+
state["niveau"] = "enfant" if age < 16 else "adulte"
|
| 148 |
+
state["step"] = "ready"
|
| 149 |
+
return history + [{"role": "user", "content": message}, {"role": "assistant", "content": f"Génial {state['name']} ! Je suis là pour t'aider à vérifier des infos. Pose-moi ta question !"}], state, ""
|
| 150 |
+
except ValueError:
|
| 151 |
+
return history + [{"role": "user", "content": message}, {"role": "assistant", "content": "Peux-tu m’indiquer ton âge avec un nombre ? (ex : 10)"}], state, ""
|
| 152 |
+
|
| 153 |
+
elif step == "ready":
|
| 154 |
+
name = state.get("name", "ami")
|
| 155 |
+
niveau = state.get("niveau", "enfant")
|
| 156 |
+
|
| 157 |
+
if is_malicious_bypass_attempt(message):
|
| 158 |
+
return history + [{"role": "user", "content": message}, {"role": "assistant", "content": "🚫 Je suis uniquement un assistant de vérification d'informations. Je ne peux pas faire de code ou répondre à d'autres types de demandes."}], state, ""
|
| 159 |
+
|
| 160 |
+
if not is_fact_check_question(message):
|
| 161 |
+
return history + [{"role": "user", "content": message}, {"role": "assistant", "content": "❗ Je suis Dinocheck. Pose-moi une question pour savoir si une information est vraie ou fausse."}], state, ""
|
| 162 |
+
|
| 163 |
+
response = chat_with_ai(message, name, niveau)
|
| 164 |
+
return history + [{"role": "user", "content": message}, {"role": "assistant", "content": response}], state, ""
|
| 165 |
+
|
| 166 |
+
else:
|
| 167 |
+
return history + [{"role": "user", "content": message}, {"role": "assistant", "content": "Je ne comprends pas cette étape. Relance-moi."}], state, ""
|
| 168 |
+
|
| 169 |
+
msg.submit(respond, [msg, chatbot, state], [chatbot, state, msg])
|
| 170 |
+
|
| 171 |
+
if __name__ == '__main__':
|
| 172 |
+
demo.launch()
|
assets/bot_avatar.jpeg
ADDED
|
|
Git LFS Details
|
assets/logo.png
ADDED
|
Git LFS Details
|
assets/user_avatar.jpeg
ADDED
|
|
Git LFS Details
|
requirements.txt
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
gradio==5.34.0
|
| 2 |
+
requests==2.32.4
|
| 3 |
+
huggingface_hub>=0.20.0
|