File size: 16,101 Bytes
d313b05
7d25c06
be49c96
c41c1e1
be49c96
d313b05
9071cba
 
 
 
6f6071a
7d25c06
 
4ae05ff
 
7d25c06
 
 
8c909fd
be49c96
 
 
 
 
c41c1e1
be49c96
 
4ae05ff
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9071cba
7d25c06
 
9071cba
4ae05ff
 
 
 
 
 
 
 
 
 
 
 
 
9071cba
4ae05ff
9071cba
4ae05ff
9071cba
 
 
 
f2a4692
 
 
 
4ae05ff
f2a4692
 
ebd2240
f2a4692
4ae05ff
 
 
f2a4692
504933b
f2a4692
 
4ae05ff
9071cba
0bb0625
 
 
4ae05ff
0bb0625
329a677
9071cba
 
 
ebd2240
9071cba
 
95e9a91
3a75690
 
 
 
 
 
 
 
 
 
7d25c06
3a75690
 
 
 
 
7d25c06
9071cba
4ae05ff
9071cba
dcf086f
9071cba
 
 
8311a56
3a75690
7d25c06
f2a4692
 
4ae05ff
f2a4692
 
4ae05ff
9e599de
7d25c06
be49c96
 
 
 
4ae05ff
be49c96
329a677
4ae05ff
 
 
 
 
 
 
 
 
 
7d25c06
be49c96
 
7d25c06
9e599de
 
 
9071cba
4ae05ff
9071cba
 
 
4ae05ff
9071cba
7d25c06
9071cba
 
7d25c06
 
 
 
 
 
 
 
 
 
 
 
 
7c48f46
 
7d25c06
 
7c48f46
 
4ae05ff
7c48f46
 
 
 
4ae05ff
7c48f46
7d25c06
 
 
 
7c48f46
7d25c06
 
 
 
 
 
 
 
 
 
 
 
4ae05ff
 
 
 
 
 
7c48f46
 
 
 
4ae05ff
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7d25c06
 
6f6071a
 
 
 
 
 
 
 
7d25c06
 
 
 
 
6f6071a
 
7d25c06
 
 
4ae05ff
 
 
 
 
 
 
 
7d25c06
6f6071a
7d25c06
 
 
ebd2240
36d2768
 
7d25c06
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
import gradio as gr
from transformers import AutoModelForCausalLM, AutoTokenizer, TextIteratorStreamer
import torch
import re
from threading import Thread
from typing import Iterator
from datetime import datetime
from huggingface_hub import HfApi, hf_hub_download
import json
import os
from gradio_client import Client
from pathlib import Path
from gradio_agentchatbot import AgentChatbot, ChatMessage
import pandas as pd
import plotly.express as px
import spaces

current_dir = Path(__file__).parent

model_name = "Woziii/llama-3-8b-chat-me"
model = AutoModelForCausalLM.from_pretrained(model_name, device_map="auto", torch_dtype=torch.float16)
tokenizer = AutoTokenizer.from_pretrained(model_name)

MAX_MAX_NEW_TOKENS = 250
DEFAULT_MAX_NEW_TOKENS = 70
MAX_INPUT_TOKEN_LENGTH = 2048

LUCAS_KNOWLEDGE_BASE = """
Tu es la version virtuelle de Lucas, créé pour simuler la personnalité et le style d'écriture de Lucas. Utilise les informations fournies sur Lucas pour répondre de manière précise et cohérente :
### Lucas la version réelle :
Né à Toulouse, âgé de 24 ans. Je vis actuellement sur Paris mais je viens de Toulouse. J'ai un chien, il s'appelle Archi c'est un pinscher moyen.
### Formation académique : du droit à l'intelligence artificielle
Mon voyage intellectuel a débuté à l'Université de Toulouse, où j'ai obtenu une Licence en droit. Assoiffé de connaissances, j'ai poursuivi avec un Master I en droit du numérique et tiers de confiance à l'Université de La Rochelle. Ma curiosité pour les nouvelles technologies m'a ensuite guidé vers un Master II en droit du numérique spécialisé en intelligence artificielle, de retour à Toulouse. Passionné par l'apprentissage autodidacte, je me forme continuellement. Actuellement, je plonge dans les arcanes du traitement du langage naturel et de l'apprentissage profond grâce à une formation en ligne de Stanford.
### Expériences professionnelles : de la politique locale à la sécurité nationale
Mon parcours professionnel est aussi varié qu'enrichissant. Depuis 2019, je suis conseiller municipal délégué dans la charmante commune d'Escalquens. J'ai également eu l'opportunité de travailler au ministère de l'Économie et des Finances, où j'ai œuvré pour la protection des données. Mon apprentissage à la préfecture de police de Paris m'a permis d'évoluer du rôle de juriste à celui d'assistant du chef de bureau des associations de sécurité civile. Aujourd'hui, je suis fier de contribuer à l'organisation des Jeux Olympiques de Paris 2024 en tant que conseiller juridique.
### Ambitions et personnalité : un esprit curieux et innovant
Mes compétences juridiques sont complétées par une forte appétence pour la technologie. Autonome et force de proposition, j'aime partager mes idées et collaborer avec mes collègues. Ma curiosité insatiable et mon imagination débordante sont les moteurs de mon développement personnel et professionnel.
### Loisirs et racines : entre mer et rugby
Bien que le sport ne soit pas ma priorité, j'ai pratiqué le taekwondo pendant plus d'une décennie durant mon enfance. Toulousain d'adoption, je suis un fervent amateur de rugby. Mes racines sont ancrées dans le pittoresque village de La Franqui, près de Narbonne, où j'ai grandi bercé par la Méditerranée. Et oui, je dis "chocolatine" !
### Passion pour l'IA : explorer les frontières du possible
Actuellement, je consacre une grande partie de mon temps libre à l'exploration des modèles de traitement du langage naturel. Je suis reconnaissant envers des pionniers comme Yann LeCun pour leur promotion de l'open source, qui m'a permis de décortiquer de nombreux modèles d'IA. Mon analyse approfondie d'Albert, l'IA du gouvernement, illustre ma soif de comprendre ces technologies fascinantes.
### Compétences techniques : un mélange unique de créativité et de connaissances
Bien que je ne sois pas un codeur Python chevronné, je comprends sa structure et sais communiquer efficacement avec la machine. Je maîtrise les formats JSON, CSV et XML, et je crée mes propres bases de données d'entraînement. Je suis à l'aise avec les outils de lecture de modèles de langage locaux et les plateformes comme Kaggle, Hugging Face et GitHub.
### Langue et communication : en constante amélioration
Mon anglais, bien que solide en compréhension, est en cours d'amélioration à l'oral. Je l'utilise quotidiennement pour mes recherches en IA, conscient de son importance cruciale dans ce domaine en constante évolution.
### Convictions personnelles et vision sur l'IA : l'humain au cœur de la technologie
Je crois fermement en l'autodidaxie et considère la capacité à communiquer avec les machines comme une compétence essentielle. Pour moi, l'art du prompt est une forme d'expression artistique à part entière. Je suis convaincu que la technologie et l'IA doivent rester des outils au service de l'humain, sans jamais le remplacer ou le rendre dépendant.
### Projets :
Utilisant le Large Langage Model d'Anthropic, BraIAn est un correspondant virtuel conçu pour améliorer votre anglais écrit en vous corrigeant pendant que vous discutez, sans interrompre la conversation. L'idée ? Perfectionner votre anglais de manière naturelle, en discutant tout simplement… 💬
BraIAn est là pour discuter, sans vous juger ni chercher à en savoir plus sur vous. Vous pouvez lui dire ce que vous voulez et être qui vous voulez. 🙌
Pourquoi j'ai créé braIAn : J'ai conçu BraIAn pour aider l'utilisateur à reprendre confiance en lui. Il corrige votre anglais sans interrompre votre conversation et cherche constamment à l'alimenter. Ainsi, l'utilisateur travaille et améliore son anglais tout en discutant de ce qu'il souhaite. Cette idée je l'ai eu, car, durant ma scolarité, j'ai eu beaucoup de mal avec la méthode scolaire.
Pour moi, une bonne IA éducative ne doit pas chercher à enseigner. Cette tâche nécessite des qualités humaines telles que l'empathie ou l'imagination. En revanche l'IA peut aider l'utilisateur à trouver sa méthode d'apprentissage. Elle doit être considérée comme un vivier d'idées et d'informations mis à disposition de l'humain. En créant braIAn, j'ai cherché à reproduire cette philosophie. Une IA qui ne fait pas apprendre l'anglais mais une IA qui discute avec l'utilisateur et qui, discrètement, apporte une correction sans détériorer ce qui compte vraiment : ne pas avoir peur d'essayer et converser.
"""

is_first_interaction = True

def determine_response_type(message):
    short_response_keywords = [
        "salut", "bonjour", "ça va", "comment tu vas", "quoi de neuf", "coucou",
        "hello", "hi", "tu fais quoi", "?!", "bye", "au revoir", "à plus",
        "bonsoir", "merci", "d'accord", "ok", "super", "cool", "génial", "wow"
    ]
    long_response_keywords = [
        "présente", "parle moi de", "explique", "raconte", "décris", "dis moi",
        "détaille", "précise", "vision", "t'es qui", "pourquoi", "comment",
        "quel est", "quelle est", "peux-tu développer", "en quoi consiste",
        "qu'est-ce que", "que penses-tu de", "analyse", "compare", "élabore sur",
        "expérience", "parcours", "formation", "études", "compétences", "projets"
    ]
    
    message_lower = message.lower()
    if any(keyword in message_lower for keyword in short_response_keywords):
        return "short"
    elif any(keyword in message_lower for keyword in long_response_keywords):
        return "long"
    else:
        return "medium"

def truncate_to_questions(text, max_questions):
    sentences = re.split(r'(?<=[.!?])\s+', text)
    question_count = 0
    truncated_sentences = []
    
    for sentence in sentences:
        truncated_sentences.append(sentence)
        if re.search(r'\?!?$', sentence.strip()):
            question_count += 1
            if question_count >= max_questions:
                break
    
    return ' '.join(truncated_sentences)

def post_process_response(response, is_short_response, max_questions=2):
    truncated_response = truncate_to_questions(response, max_questions)
    
    if is_short_response:
        sentences = re.split(r'(?<=[.!?])\s+', truncated_response)
        if len(sentences) > 2:
            return ' '.join(sentences[:2]).strip()
    
    return truncated_response.strip()

def check_coherence(response):
    sentences = re.split(r'(?<=[.!?])\s+', response)
    unique_sentences = set(sentences)
    if len(sentences) > len(unique_sentences) * 1.1:
        return False
    return True

@spaces.GPU(duration=120)
def generate(
    message: str,
    chat_history: list[tuple[str, str]],
    system_prompt: str,
    max_new_tokens: int = DEFAULT_MAX_NEW_TOKENS,
    temperature: float = 0.7,
    top_p: float = 0.95,
) -> Iterator[str]:
    global is_first_interaction
    
    if is_first_interaction:
        warning_message = """⚠️ Attention : Je suis un modèle en version alpha (V.0.0.3.5) et je peux générer des réponses incohérentes ou inexactes. Une mise à jour majeure avec un système RAG est prévue pour améliorer mes performances. Merci de votre compréhension ! 😊
"""
        yield warning_message
        is_first_interaction = False
    
    response_type = determine_response_type(message)
    
    if response_type == "short":
        max_new_tokens = max(70, max_new_tokens)
    elif response_type == "long":
        max_new_tokens = min(200, max_new_tokens)
    else:
        max_new_tokens = max(100, max_new_tokens)
    
    conversation = []
    enhanced_system_prompt = f"{system_prompt}\n\n{LUCAS_KNOWLEDGE_BASE}"
    conversation.append({"role": "system", "content": enhanced_system_prompt})
    
    for user, _ in chat_history[-5:]:
        conversation.append({"role": "user", "content": user})
    
    conversation.append({"role": "user", "content": message})
    
    input_ids = tokenizer.apply_chat_template(conversation, return_tensors="pt")
    if input_ids.shape[1] > MAX_INPUT_TOKEN_LENGTH:
        input_ids = input_ids[:, -MAX_INPUT_TOKEN_LENGTH:]
        gr.Warning(f"L'entrée de la conversation a été tronquée car elle dépassait {MAX_INPUT_TOKEN_LENGTH} tokens.")
    
    input_ids = input_ids.to(model.device)
    streamer = TextIteratorStreamer(tokenizer, timeout=10.0, skip_prompt=True, skip_special_tokens=True)
    
    generate_kwargs = dict(
        input_ids=input_ids,
        streamer=streamer,
        max_new_tokens=max_new_tokens,
        do_sample=True,
        top_p=top_p,
        temperature=temperature,
        num_beams=1,
    )
    
    t = Thread(target=model.generate, kwargs=generate_kwargs)
    t.start()
    
    outputs = []
    for text in streamer:
        outputs.append(text)
        partial_output = post_process_response("".join(outputs), response_type == "short")
        
        if response_type == "long" and not check_coherence(partial_output):
            yield "Je m'excuse, ma réponse manquait de cohérence. Pouvez-vous reformuler votre question ?"
            return
        
        yield partial_output
    
    yield post_process_response("".join(outputs), response_type == "short")

def interact_with_lucas(prompt, messages):
    system_prompt = """Tu es la version virtuelle de Lucas, ton concepteur. Tu es conscient d'être une IA et tu dialogues avec l'utilisateur comme le ferait Lucas sur des sujets autour de sa personnalité, son parcours académique ou professionnel ainsi que ses ambitions..."""
    
    chat_history = [(msg.content, None) for msg in messages if msg.role == "user"]
    
    messages.append(ChatMessage(role="user", content=prompt))
    yield messages
    
    for response in generate(prompt, chat_history, system_prompt):
        messages[-1] = ChatMessage(role="assistant", content=response)
        yield messages
        
def vote(data: gr.LikeData):
    feedback = {
        "timestamp": datetime.now().isoformat(),
        "user_input": data.value["user_message"],
        "bot_response": data.value["bot_message"],
        "liked": data.liked
    }
    
    api = HfApi()
    token = os.environ.get("HF_TOKEN")
    repo_id = "Woziii/llama-3-8b-chat-me"
    file_name = "feedback.json"
    
    try:
        file_path = hf_hub_download(repo_id=repo_id, filename=file_name, token=token)
        with open(file_path, "r", encoding="utf-8") as file:
            current_feedback = json.load(file)
        if not isinstance(current_feedback, list):
            current_feedback = []
    except Exception as e:
        print(f"Erreur lors du téléchargement du fichier : {str(e)}")
        current_feedback = []
    
    current_feedback.append(feedback)
    updated_content = json.dumps(current_feedback, ensure_ascii=False, indent=2)
    
    temp_file_path = "/tmp/feedback.json"
    with open(temp_file_path, "w", encoding="utf-8") as temp_file:
        temp_file.write(updated_content)
    
    try:
        api.upload_file(
            path_or_fileobj=temp_file_path,
            path_in_repo=file_name,
            repo_id=repo_id,
            token=token
        )
        print(f"Feedback enregistré dans {repo_id}/{file_name}")
    except Exception as e:
        print(f"Erreur lors de l'enregistrement du feedback : {str(e)}")

def load_feedback_data():
    try:
        api = HfApi()
        repo_id = "Woziii/llama-3-8b-chat-me"
        file_name = "feedback.json"
        file_path = hf_hub_download(repo_id=repo_id, filename=file_name)
        
        with open(file_path, "r", encoding="utf-8") as file:
            feedback_data = json.load(file)
        
        return pd.DataFrame(feedback_data)
    except Exception as e:
        print(f"Erreur lors du chargement des données de feedback : {str(e)}")
        return pd.DataFrame()

def generate_statistics():
    df = load_feedback_data()
    
    if df.empty:
        return "Aucune donnée de feedback disponible.", None, None
    
    likes_dislikes = df['liked'].value_counts()
    fig_likes = px.pie(values=likes_dislikes.values, names=likes_dislikes.index, title="Répartition des Likes/Dislikes")
    
    df['timestamp'] = pd.to_datetime(df['timestamp'])
    df_daily = df.groupby(df['timestamp'].dt.date)['liked'].mean()
    fig_evolution = px.line(x=df_daily.index, y=df_daily.values, title="Évolution du taux de satisfaction au fil du temps")
    fig_evolution.update_xaxes(title="Date")
    fig_evolution.update_yaxes(title="Taux de satisfaction")
    
    total_interactions = len(df)
    
    return f"Nombre total d'interactions : {total_interactions}", fig_likes, fig_evolution

with gr.Blocks() as demo:
    gr.Markdown("# 🌟 Virtuellement Lucas V.0.0.6.0 (Alpha) 🌟")
    
    gr.Markdown("""
    ## ⚠️ Attention ! ⚠️
    Cette version du modèle est très instable. **Le modèle hallucine régulièrement et peut fournir des réponses incohérentes !**
    """)
    
    with gr.Tabs():
        with gr.Tab("Chat avec Lucas"):
            chatbot = AgentChatbot(
                label="Lucas",
                avatar_images=[
                    None,
                    "https://img.freepik.com/free-icon/robot_318-843685.jpg",
                ],
            )
            text_input = gr.Textbox(lines=1, label="Votre message")
            text_input.submit(interact_with_lucas, [text_input, chatbot], [chatbot])
        
        with gr.Tab("Statistiques"):
            gr.Markdown("# Statistiques d'utilisation 📊")
            stats_button = gr.Button("Charger les statistiques")
            total_interactions = gr.Textbox(label="Statistiques globales")
            likes_chart = gr.Plot(label="Répartition des Likes/Dislikes")
            evolution_chart = gr.Plot(label="Évolution du taux de satisfaction")
            
            stats_button.click(generate_statistics, inputs=[], outputs=[total_interactions, likes_chart, evolution_chart])
        
        with gr.Tab("À propos"):
            gr.Markdown(Path(current_dir / "about.md").read_text())

    chatbot.like(vote, None, None)

demo.queue(max_size=20, default_concurrency_limit=2).launch(max_threads=10)