Eric Marchand
commited on
Commit
·
f4f9c98
1
Parent(s):
941f36a
Meilleur gestion du multiclient
Browse files- .gitignore +1 -1
- app.py +35 -41
- git-commit-push.bat +4 -0
- src/model_huggingface.py +1 -1
- src/model_mistral.py +0 -63
- src/model_ollama.py +0 -49
- src/model_openai.py +0 -65
- src/rag.py +0 -1
- src/store.py +2 -1
.gitignore
CHANGED
@@ -1,6 +1,6 @@
|
|
|
|
1 |
venv/
|
2 |
__pycache__/
|
3 |
.vscode/
|
4 |
.gradio/
|
5 |
.env
|
6 |
-
files/rag_app/
|
|
|
1 |
+
db/**
|
2 |
venv/
|
3 |
__pycache__/
|
4 |
.vscode/
|
5 |
.gradio/
|
6 |
.env
|
|
app.py
CHANGED
@@ -1,78 +1,72 @@
|
|
1 |
from pathlib import Path
|
2 |
-
# import base64
|
3 |
import gradio as gr
|
4 |
from src.rag import Rag
|
5 |
from src.amodel import ModelType
|
6 |
|
7 |
-
STORE_DIR = "./
|
8 |
-
|
|
|
9 |
|
10 |
def main():
|
11 |
-
# Création du rag
|
12 |
-
rag:Rag = Rag(ModelType.MTHUGGINGFACE, store_dir=STORE_DIR)
|
13 |
-
# Reset de la base à chaque démarrage du serveur
|
14 |
-
rag.reset_store()
|
15 |
-
|
16 |
# UI
|
17 |
with gr.Blocks() as demo:
|
18 |
-
|
19 |
def upload_file(file_path):
|
20 |
-
# Récupérer la liste des collections du store
|
21 |
-
names = list(rag.emb_store.get_collection_names())
|
22 |
-
# Supprimer la première s'il y en a déjà 4 (le max)
|
23 |
-
if len(names) == MAX_DOCS:
|
24 |
-
rag.delete_collection(names[0])
|
25 |
-
# Ajouter le pdf
|
26 |
name:str = Path(file_path).name
|
|
|
|
|
|
|
|
|
|
|
|
|
27 |
rag.add_pdf_to_store(file_name=file_path, collection_name=name)
|
28 |
-
|
29 |
-
names = list(rag.emb_store.get_collection_names())
|
30 |
-
combo.choices = names
|
31 |
-
return gr.update(choices=names, value=name, interactive=True), gr.update(value="")
|
32 |
|
33 |
-
def ask_rag(question:str,
|
34 |
-
col_name
|
35 |
-
if col_name == None:
|
36 |
return "Aucun pdf actif, veuillez en uploader un !"
|
|
|
|
|
|
|
|
|
|
|
37 |
prompt, resp, sources, ids = rag.ask_rag(question, col_name)
|
38 |
-
# print("choice:" + col_name, "RAG:" + resp)
|
39 |
return resp
|
40 |
-
|
41 |
def on_temperature_change(temp):
|
42 |
-
rag.set_temperature(temp)
|
43 |
-
|
44 |
-
|
|
|
|
|
45 |
with gr.Tab("RAG"):
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
# Le button qui permet d'uploader un pdf
|
52 |
-
upload_button = gr.UploadButton("Clique pour
|
53 |
-
|
54 |
# La zone où on pose une question au RAG
|
55 |
ask_input = gr.Text(label="Pose une question à ton pdf")
|
56 |
# La réponse du RAG (Markdown pour afficher les formules .tex)
|
57 |
rag_output = gr.Markdown(label="Réponse")
|
58 |
-
|
59 |
with gr.Tab("Réglages"):
|
60 |
gr.Markdown("## Modèles:")
|
61 |
gr.Markdown("- " + rag.get_llm_name())
|
62 |
gr.Markdown("- " + rag.get_feature_name())
|
63 |
temperature_slider = gr.Slider(minimum=0,
|
64 |
maximum=1.0,
|
65 |
-
value=0.
|
66 |
step=0.1,
|
67 |
label="Température")
|
68 |
-
|
69 |
# Réponses aux évènements
|
70 |
-
upload_button.upload(fn=upload_file, inputs=upload_button, outputs=[
|
71 |
-
ask_input.submit(fn=ask_rag, inputs=[ask_input,
|
72 |
temperature_slider.change(fn=on_temperature_change, inputs=temperature_slider)
|
|
|
|
|
73 |
|
74 |
-
# demo.launch(allowed_paths=["./file/"], share=True)
|
75 |
-
demo.launch(allowed_paths=["./file/"])
|
76 |
|
77 |
if __name__ == "__main__":
|
78 |
main()
|
|
|
1 |
from pathlib import Path
|
|
|
2 |
import gradio as gr
|
3 |
from src.rag import Rag
|
4 |
from src.amodel import ModelType
|
5 |
|
6 |
+
STORE_DIR = "./db/rag_app" # Le répertoire de la base
|
7 |
+
# STORE_DIR = None # Store éphémère
|
8 |
+
MAX_DOCS = 6 # Le nombre max de documents dans la base
|
9 |
|
10 |
def main():
|
|
|
|
|
|
|
|
|
|
|
11 |
# UI
|
12 |
with gr.Blocks() as demo:
|
|
|
13 |
def upload_file(file_path):
|
|
|
|
|
|
|
|
|
|
|
|
|
14 |
name:str = Path(file_path).name
|
15 |
+
names = rag.emb_store.get_collection_names()
|
16 |
+
count = len(names)
|
17 |
+
if name in names:
|
18 |
+
rag.delete_collection(name)
|
19 |
+
if count >= MAX_DOCS:
|
20 |
+
rag.delete_collection(names[0])
|
21 |
rag.add_pdf_to_store(file_name=file_path, collection_name=name)
|
22 |
+
return name
|
|
|
|
|
|
|
23 |
|
24 |
+
def ask_rag(question:str, col_name:str):
|
25 |
+
if col_name == "Aucun fichier":
|
|
|
26 |
return "Aucun pdf actif, veuillez en uploader un !"
|
27 |
+
if question.strip() == "":
|
28 |
+
return "Veuillez poser une question."
|
29 |
+
names = rag.emb_store.get_collection_names()
|
30 |
+
if not col_name in names:
|
31 |
+
return "'{name}' n'est plus sur le serveur, veuillez le recharger".format(name=col_name)
|
32 |
prompt, resp, sources, ids = rag.ask_rag(question, col_name)
|
|
|
33 |
return resp
|
34 |
+
|
35 |
def on_temperature_change(temp):
|
36 |
+
rag.set_temperature(temp)
|
37 |
+
|
38 |
+
# global State https://www.gradio.app/guides/state-in-blocks
|
39 |
+
rag:Rag = Rag(ModelType.MTHUGGINGFACE, store_dir=STORE_DIR) # Création du rag
|
40 |
+
# rag.reset_store() # Reset de la base à chaque démarrage du serveur
|
41 |
with gr.Tab("RAG"):
|
42 |
+
gr.Image("./files/drane.jpg", height=100, show_download_button=False,
|
43 |
+
show_fullscreen_button=False, show_label=False, show_share_button=False,
|
44 |
+
interactive=False, container=False)
|
45 |
+
# Le label qui affiche le nom du pdf courant
|
46 |
+
pdf_name = gr.Markdown("Aucun fichier")
|
47 |
# Le button qui permet d'uploader un pdf
|
48 |
+
upload_button = gr.UploadButton("Clique pour ajouter un pdf", file_types=[".pdf"], file_count="single")
|
|
|
49 |
# La zone où on pose une question au RAG
|
50 |
ask_input = gr.Text(label="Pose une question à ton pdf")
|
51 |
# La réponse du RAG (Markdown pour afficher les formules .tex)
|
52 |
rag_output = gr.Markdown(label="Réponse")
|
|
|
53 |
with gr.Tab("Réglages"):
|
54 |
gr.Markdown("## Modèles:")
|
55 |
gr.Markdown("- " + rag.get_llm_name())
|
56 |
gr.Markdown("- " + rag.get_feature_name())
|
57 |
temperature_slider = gr.Slider(minimum=0,
|
58 |
maximum=1.0,
|
59 |
+
value=0.0,
|
60 |
step=0.1,
|
61 |
label="Température")
|
62 |
+
|
63 |
# Réponses aux évènements
|
64 |
+
upload_button.upload(fn=upload_file, inputs=upload_button, outputs=[pdf_name], show_progress="full")
|
65 |
+
ask_input.submit(fn=ask_rag, inputs=[ask_input, pdf_name], outputs=rag_output, show_progress="full")
|
66 |
temperature_slider.change(fn=on_temperature_change, inputs=temperature_slider)
|
67 |
+
|
68 |
+
demo.launch()
|
69 |
|
|
|
|
|
70 |
|
71 |
if __name__ == "__main__":
|
72 |
main()
|
git-commit-push.bat
ADDED
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
1 |
+
git add .
|
2 |
+
git commit -a
|
3 |
+
git push
|
4 |
+
PAUSE
|
src/model_huggingface.py
CHANGED
@@ -53,7 +53,7 @@ class HuggingFaceModel(AModel):
|
|
53 |
try:
|
54 |
for chunk in chunks:
|
55 |
v = self.create_vector(chunk)
|
56 |
-
if not isinstance(v, np.ndarray):
|
57 |
raise
|
58 |
vectors.append(v.tolist())
|
59 |
return vectors
|
|
|
53 |
try:
|
54 |
for chunk in chunks:
|
55 |
v = self.create_vector(chunk)
|
56 |
+
if not isinstance(v, np.ndarray): # l'api renvoie des array numpy....
|
57 |
raise
|
58 |
vectors.append(v.tolist())
|
59 |
return vectors
|
src/model_mistral.py
DELETED
@@ -1,63 +0,0 @@
|
|
1 |
-
import os
|
2 |
-
import sys
|
3 |
-
from dotenv import load_dotenv
|
4 |
-
from .amodel import AModel
|
5 |
-
from mistralai import Mistral
|
6 |
-
|
7 |
-
class MistralModel(AModel):
|
8 |
-
'''
|
9 |
-
https://docs.mistral.ai/capabilities/completion/
|
10 |
-
https://docs.mistral.ai/capabilities/embeddings/
|
11 |
-
temperature entre 0.0 et 0.7
|
12 |
-
'''
|
13 |
-
|
14 |
-
def __init__(self, llm_name:str, feature_name:str, temperature:float=0.0):
|
15 |
-
self.llm_name:str = llm_name
|
16 |
-
self.feature_name:str = feature_name
|
17 |
-
self.temperature = temperature
|
18 |
-
load_dotenv()
|
19 |
-
try:
|
20 |
-
self.model = Mistral(api_key=os.getenv("MISTRAL_API_KEY"))
|
21 |
-
except:
|
22 |
-
raise
|
23 |
-
|
24 |
-
|
25 |
-
def ask_llm(self, question:str)->str:
|
26 |
-
try:
|
27 |
-
response = self.model.chat.complete(
|
28 |
-
model=self.llm_name,
|
29 |
-
messages = [{ "role": "user", "content": question, },],
|
30 |
-
temperature=self.temperature
|
31 |
-
)
|
32 |
-
return response.choices[0].message.content
|
33 |
-
except:
|
34 |
-
raise
|
35 |
-
|
36 |
-
def create_vector(self, chunk:str)->list[float]:
|
37 |
-
'''
|
38 |
-
Renvoie un vecteur de taille 1024 à partir de chunk
|
39 |
-
'''
|
40 |
-
try:
|
41 |
-
response = self.model.embeddings.create(
|
42 |
-
model=self.feature_name,
|
43 |
-
# inputs=["Embed this sentence.", "As well as this one."],
|
44 |
-
inputs=[chunk]
|
45 |
-
)
|
46 |
-
return response.data[0].embedding
|
47 |
-
except:
|
48 |
-
raise
|
49 |
-
|
50 |
-
def create_vectors(self, chunks:list[str])->list[list[float]]:
|
51 |
-
'''
|
52 |
-
Renvoie n vecteurs de taille 1024 à partir de la liste chunks
|
53 |
-
'''
|
54 |
-
try:
|
55 |
-
response = self.model.embeddings.create(
|
56 |
-
model=self.feature_name,
|
57 |
-
inputs=chunks,
|
58 |
-
)
|
59 |
-
n:int = len(chunks)
|
60 |
-
result = [response.data[i].embedding for i in range(n)]
|
61 |
-
return result
|
62 |
-
except:
|
63 |
-
raise
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/model_ollama.py
DELETED
@@ -1,49 +0,0 @@
|
|
1 |
-
from .amodel import AModel
|
2 |
-
import ollama
|
3 |
-
import numpy as np
|
4 |
-
|
5 |
-
class OllamaModel(AModel):
|
6 |
-
|
7 |
-
def __init__(self, llm_name:str, feature_name:str, temperature:float=0.0):
|
8 |
-
self.llm_name:str = llm_name
|
9 |
-
self.feature_name:str = feature_name
|
10 |
-
self.temperature = temperature
|
11 |
-
|
12 |
-
def ask_llm(self, question:str)->str:
|
13 |
-
try:
|
14 |
-
resp = ollama.chat(
|
15 |
-
model=self.llm_name,
|
16 |
-
messages=[{'role':'user', 'content':question}],
|
17 |
-
stream=False,
|
18 |
-
options={"temperature":self.temperature})
|
19 |
-
return resp.message.content
|
20 |
-
except:
|
21 |
-
raise
|
22 |
-
|
23 |
-
def create_vector(self, chunk:str)->list[float]:
|
24 |
-
'''
|
25 |
-
TODO: Vérifier s'il ne faut pas utiliser 'embed' plutôt que 'embeddings'
|
26 |
-
'''
|
27 |
-
try:
|
28 |
-
resp = ollama.embeddings(
|
29 |
-
model=self.feature_name,
|
30 |
-
prompt=chunk)
|
31 |
-
return self.normalize(resp.embedding).tolist()
|
32 |
-
except:
|
33 |
-
raise
|
34 |
-
|
35 |
-
def normalize(self, v:list[float]):
|
36 |
-
norm = np.linalg.norm(v)
|
37 |
-
if norm == 0:
|
38 |
-
return v
|
39 |
-
return v / norm
|
40 |
-
|
41 |
-
def create_vectors(self, chunks:list[str])->list[list[float]]:
|
42 |
-
try:
|
43 |
-
resp = ollama.embed(
|
44 |
-
model=self.feature_name,
|
45 |
-
input=chunks)
|
46 |
-
# print(resp.embeddings)
|
47 |
-
return resp.embeddings
|
48 |
-
except:
|
49 |
-
raise
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/model_openai.py
DELETED
@@ -1,65 +0,0 @@
|
|
1 |
-
import os
|
2 |
-
import sys
|
3 |
-
from dotenv import load_dotenv
|
4 |
-
from .amodel import AModel
|
5 |
-
from openai import OpenAI
|
6 |
-
|
7 |
-
class OpenAIModel(AModel):
|
8 |
-
'''
|
9 |
-
https://platform.openai.com/docs/guides/text-generation
|
10 |
-
|
11 |
-
'''
|
12 |
-
|
13 |
-
def __init__(self, llm_name:str, feature_name:str, temperature:float=0.0):
|
14 |
-
self.llm_name:str = llm_name
|
15 |
-
self.feature_name:str = feature_name
|
16 |
-
self.temperature = temperature
|
17 |
-
load_dotenv()
|
18 |
-
try:
|
19 |
-
self.model = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
|
20 |
-
except:
|
21 |
-
raise
|
22 |
-
|
23 |
-
def ask_llm(self, question:str)->str:
|
24 |
-
try:
|
25 |
-
response = self.model.chat.completions.create(
|
26 |
-
# model="gpt-4o-mini",
|
27 |
-
model=self.llm_name,
|
28 |
-
messages=[
|
29 |
-
{"role":"system", "content":""},
|
30 |
-
{"role":"user", "content":question},
|
31 |
-
],
|
32 |
-
temperature=self.temperature
|
33 |
-
)
|
34 |
-
return response.choices[0].message.content
|
35 |
-
except:
|
36 |
-
raise
|
37 |
-
|
38 |
-
def create_vector(self, chunk:str)->list[float]:
|
39 |
-
'''
|
40 |
-
8192 tokens max
|
41 |
-
'''
|
42 |
-
# les embeddings d'OpenAI sont normalisés à 1
|
43 |
-
try:
|
44 |
-
response = self.model.embeddings.create(
|
45 |
-
input=chunk,
|
46 |
-
model=self.feature_name
|
47 |
-
)
|
48 |
-
return response.data[0].embedding
|
49 |
-
except:
|
50 |
-
raise
|
51 |
-
|
52 |
-
def create_vectors(self, chunks:list[str])->list[list[float]]:
|
53 |
-
'''
|
54 |
-
Pas plus de 2048 chunks
|
55 |
-
'''
|
56 |
-
try:
|
57 |
-
response = self.model.embeddings.create(
|
58 |
-
input=chunks,
|
59 |
-
model=self.feature_name
|
60 |
-
)
|
61 |
-
n:int = len(chunks)
|
62 |
-
result = [response.data[i].embedding for i in range(n)]
|
63 |
-
return result
|
64 |
-
except:
|
65 |
-
raise
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/rag.py
CHANGED
@@ -1,7 +1,6 @@
|
|
1 |
import sys
|
2 |
|
3 |
from pypdf import PdfReader
|
4 |
-
|
5 |
from .chunker import Chunker
|
6 |
from .amodel import ModelType
|
7 |
from .model_huggingface import HuggingFaceModel
|
|
|
1 |
import sys
|
2 |
|
3 |
from pypdf import PdfReader
|
|
|
4 |
from .chunker import Chunker
|
5 |
from .amodel import ModelType
|
6 |
from .model_huggingface import HuggingFaceModel
|
src/store.py
CHANGED
@@ -230,9 +230,10 @@ class Store(AStore):
|
|
230 |
Si on ne peut pas créer le 'persist_dir'
|
231 |
'''
|
232 |
# Vérifier si le persist_dir existe, sinon le créer
|
233 |
-
print("Persist_dir:" + self.persist_dir)
|
234 |
try:
|
235 |
if not os.path.exists(self.persist_dir):
|
|
|
236 |
os.mkdir(self.persist_dir)
|
237 |
except:
|
238 |
raise Exception("Unable to create the persit directory: {dir}".format(dir=self.persist_dir))
|
|
|
230 |
Si on ne peut pas créer le 'persist_dir'
|
231 |
'''
|
232 |
# Vérifier si le persist_dir existe, sinon le créer
|
233 |
+
# print("Persist_dir:" + self.persist_dir)
|
234 |
try:
|
235 |
if not os.path.exists(self.persist_dir):
|
236 |
+
print("Trying to recreate persist_dir", self.persist_dir)
|
237 |
os.mkdir(self.persist_dir)
|
238 |
except:
|
239 |
raise Exception("Unable to create the persit directory: {dir}".format(dir=self.persist_dir))
|