Eric Marchand
commited on
Commit
·
3afd61a
1
Parent(s):
590d088
Refactoring
Browse files- .gitignore +2 -6
- README.md +1 -0
- app.py +118 -57
- db/readme.txt +0 -1
- files/drane.jpg +0 -0
- files/drane.png +0 -0
- files/viking.png +0 -0
- git-commit-push.bat +1 -0
- requirements.txt +3 -1
- src/amodel.py +16 -17
- src/astore.py +0 -39
- src/model_huggingface.py +2 -9
- src/rag.py +28 -75
.gitignore
CHANGED
@@ -1,6 +1,2 @@
|
|
1 |
-
|
2 |
-
|
3 |
-
__pycache__/
|
4 |
-
.vscode/
|
5 |
-
.gradio/
|
6 |
-
.env
|
|
|
1 |
+
git-commit-push.bat
|
2 |
+
|
|
|
|
|
|
|
|
README.md
CHANGED
@@ -9,6 +9,7 @@ app_file: app.py
|
|
9 |
pinned: false
|
10 |
license: cc0-1.0
|
11 |
short_description: RAG app with minimal depencies
|
|
|
12 |
---
|
13 |
|
14 |
Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
|
|
|
9 |
pinned: false
|
10 |
license: cc0-1.0
|
11 |
short_description: RAG app with minimal depencies
|
12 |
+
python_version: 3.11
|
13 |
---
|
14 |
|
15 |
Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
|
app.py
CHANGED
@@ -1,72 +1,133 @@
|
|
1 |
from pathlib import Path
|
2 |
import gradio as gr
|
3 |
from src.rag import Rag
|
4 |
-
from src.
|
|
|
|
|
|
|
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 |
-
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
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 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
34 |
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
47 |
# Le button qui permet d'uploader un pdf
|
48 |
-
upload_button = gr.UploadButton(
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
|
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 |
-
#
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
69 |
|
70 |
|
71 |
if __name__ == "__main__":
|
72 |
-
|
|
|
1 |
from pathlib import Path
|
2 |
import gradio as gr
|
3 |
from src.rag import Rag
|
4 |
+
from src.model_huggingface import HuggingFaceModel
|
5 |
+
|
6 |
+
from src.amodel import AModel
|
7 |
+
AModel.load_env_variables()
|
8 |
|
9 |
STORE_DIR = "./db/rag_app" # Le répertoire de la base
|
10 |
# STORE_DIR = None # Store éphémère
|
11 |
MAX_DOCS = 6 # Le nombre max de documents dans la base
|
12 |
|
13 |
+
# global State https://www.gradio.app/guides/state-in-blocks
|
14 |
+
rag:Rag = Rag(
|
15 |
+
HuggingFaceModel("meta-llama/Meta-Llama-3-8B-Instruct", None, 0),
|
16 |
+
HuggingFaceModel(None, "sentence-transformers/all-MiniLM-l6-v2", 0),
|
17 |
+
STORE_DIR
|
18 |
+
)
|
19 |
+
rag.reset_store() # Reset de la base à chaque démarrage du serveur
|
20 |
+
print("rag created, store reseted")
|
|
|
|
|
|
|
|
|
|
|
21 |
|
22 |
+
# UI
|
23 |
+
with gr.Blocks(title="RAGnar",
|
24 |
+
# theme="Yntec/HaleyCH_Theme_Orange_Green",
|
25 |
+
fill_height=True,
|
26 |
+
analytics_enabled=False,
|
27 |
+
css="footer {visibility: hidden}",
|
28 |
+
) as demo:
|
29 |
+
def upload_file(file_path):
|
30 |
+
name:str = Path(file_path).name
|
31 |
+
names = rag.emb_store.get_collection_names()
|
32 |
+
count = len(names)
|
33 |
+
if name in names:
|
34 |
+
rag.delete_collection(name)
|
35 |
+
print("collection", name, "deleted because already exists")
|
36 |
+
names = rag.emb_store.get_collection_names()
|
37 |
+
if count >= MAX_DOCS:
|
38 |
+
print("collection", names[0], "deleted because too many collections")
|
39 |
+
rag.delete_collection(names[0])
|
40 |
+
rag.add_pdf_to_store(file_name=file_path, collection_name=name)
|
41 |
+
return gr.Dropdown(
|
42 |
+
choices=rag.emb_store.get_collection_names(),
|
43 |
+
value=rag.emb_store.collections[-1].name,
|
44 |
+
show_label=False,
|
45 |
+
container=False,
|
46 |
+
interactive=True
|
47 |
+
)
|
48 |
|
49 |
+
def ask_rag(question:str, col_name:str):
|
50 |
+
if col_name == "Aucun fichier":
|
51 |
+
return "Aucun pdf actif, veuillez en uploader un !"
|
52 |
+
if question.strip() == "":
|
53 |
+
return "Veuillez poser une question."
|
54 |
+
names = rag.emb_store.get_collection_names()
|
55 |
+
if not col_name in names:
|
56 |
+
return "'{name}' n'est plus sur le serveur, veuillez le recharger".format(name=col_name)
|
57 |
+
prompt, resp, sources, ids = rag.ask_rag(question, col_name)
|
58 |
+
return resp
|
59 |
+
|
60 |
+
def on_temperature_change(temp):
|
61 |
+
rag.set_temperature(temp)
|
62 |
+
|
63 |
+
def on_refresh():
|
64 |
+
print("on_refresh")
|
65 |
+
choices=rag.emb_store.get_collection_names() if len(rag.emb_store.collections) > 0 else ["Aucun fichier"]
|
66 |
+
value = rag.emb_store.collections[-1].name if len(rag.emb_store.collections) > 0 else "Aucun fichier"
|
67 |
+
return gr.Dropdown(
|
68 |
+
choices=choices,
|
69 |
+
value=value,
|
70 |
+
show_label=False,
|
71 |
+
container=False,
|
72 |
+
interactive=True
|
73 |
+
)
|
74 |
+
|
75 |
+
|
76 |
+
with gr.Row():
|
77 |
+
gr.Image("./files/drane.png", show_download_button=False,
|
78 |
+
show_fullscreen_button=False, show_label=False, show_share_button=False,
|
79 |
+
interactive=False, container=False)
|
80 |
+
# https://www.svgrepo.com/svg/483648/viking-face
|
81 |
+
gr.Image("./files/viking.png", show_download_button=False,
|
82 |
+
show_fullscreen_button=False, show_label=False, show_share_button=False,
|
83 |
+
interactive=False, container=False)
|
84 |
+
with gr.Tab("RAG naïf"):
|
85 |
+
with gr.Row():
|
86 |
+
# Le button qui permet d'updater le combo des collections
|
87 |
+
refresh = gr.Button("Refresh", scale=1)
|
88 |
+
# Le combo qui affiche toutes les collections du store
|
89 |
+
choices=rag.emb_store.get_collection_names() if len(rag.emb_store.collections) > 0 else ["Aucun fichier"]
|
90 |
+
value = rag.emb_store.collections[-1].name if len(rag.emb_store.collections) > 0 else "Aucun fichier"
|
91 |
+
cols = gr.Dropdown(
|
92 |
+
choices=choices,
|
93 |
+
value=value,
|
94 |
+
show_label=False,
|
95 |
+
container=False,
|
96 |
+
interactive=True,
|
97 |
+
scale=10
|
98 |
+
)
|
99 |
# Le button qui permet d'uploader un pdf
|
100 |
+
upload_button = gr.UploadButton(
|
101 |
+
"Clique pour ajouter un pdf",
|
102 |
+
file_types=[".pdf"],
|
103 |
+
file_count="single",
|
104 |
+
scale=10)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
105 |
|
106 |
+
# La zone où on pose une question au RAG
|
107 |
+
ask_input = gr.Text(placeholder="Pose une question à ton pdf", show_label=False, container=False)
|
108 |
+
# La réponse du RAG (Markdown pour afficher les formules .tex)
|
109 |
+
rag_output = gr.Textbox("", show_copy_button=False,
|
110 |
+
show_label=False,
|
111 |
+
container=False,
|
112 |
+
max_lines=15)
|
113 |
+
|
114 |
+
with gr.Tab("Réglages"):
|
115 |
+
gr.Markdown("## Modèles:")
|
116 |
+
gr.Markdown("- " + rag.get_llm_name())
|
117 |
+
gr.Markdown("- " + rag.get_feature_name())
|
118 |
+
temperature_slider = gr.Slider(minimum=0,
|
119 |
+
maximum=1.0,
|
120 |
+
value=0.0,
|
121 |
+
step=0.1,
|
122 |
+
label="Température")
|
123 |
+
|
124 |
+
# Réponses aux évènements
|
125 |
+
refresh.click(fn=on_refresh, inputs=[], outputs=[cols])
|
126 |
+
upload_button.upload(fn=upload_file, inputs=upload_button, outputs=[cols], show_progress=True)
|
127 |
+
ask_input.submit(fn=ask_rag, inputs=[ask_input, cols], outputs=rag_output, show_progress=True)
|
128 |
+
temperature_slider.change(fn=on_temperature_change, inputs=temperature_slider)
|
129 |
+
demo.load(fn=on_refresh, inputs=[], outputs=[cols])
|
130 |
|
131 |
|
132 |
if __name__ == "__main__":
|
133 |
+
demo.queue().launch()
|
db/readme.txt
DELETED
@@ -1 +0,0 @@
|
|
1 |
-
Chaque collection dans un répertoire
|
|
|
|
files/drane.jpg
DELETED
Binary file (41.6 kB)
|
|
files/drane.png
ADDED
![]() |
files/viking.png
ADDED
![]() |
git-commit-push.bat
CHANGED
@@ -2,3 +2,4 @@ git add .
|
|
2 |
git commit -a
|
3 |
git push
|
4 |
PAUSE
|
|
|
|
2 |
git commit -a
|
3 |
git push
|
4 |
PAUSE
|
5 |
+
|
requirements.txt
CHANGED
@@ -1,2 +1,4 @@
|
|
|
|
|
|
1 |
pypdf
|
2 |
-
|
|
|
1 |
+
gradio
|
2 |
+
numpy
|
3 |
pypdf
|
4 |
+
huggingface_hub
|
src/amodel.py
CHANGED
@@ -1,21 +1,5 @@
|
|
1 |
from abc import ABC, abstractmethod
|
2 |
-
|
3 |
-
|
4 |
-
class ModelType(Enum):
|
5 |
-
''' Les différentes technos de models '''
|
6 |
-
MTOPENAI = 1
|
7 |
-
MTOLLAMA = 2
|
8 |
-
MTHUGGINGFACE = 3
|
9 |
-
MTMISTRAL = 4
|
10 |
-
|
11 |
-
@classmethod
|
12 |
-
def to_str(self, mt:int)->str:
|
13 |
-
match mt:
|
14 |
-
case 1: return "MTOPENAI"
|
15 |
-
case 2: return "MTOLLAMA"
|
16 |
-
case 3: return "MTHUGGINGFACE"
|
17 |
-
case 4: return "MTMISTRAL"
|
18 |
-
case _: return "UNKNOWN"
|
19 |
|
20 |
class AModel(ABC):
|
21 |
'''
|
@@ -27,6 +11,21 @@ class AModel(ABC):
|
|
27 |
...
|
28 |
'''
|
29 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
30 |
@abstractmethod
|
31 |
def ask_llm(self, question:str)->str:
|
32 |
pass
|
|
|
1 |
from abc import ABC, abstractmethod
|
2 |
+
import os
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3 |
|
4 |
class AModel(ABC):
|
5 |
'''
|
|
|
11 |
...
|
12 |
'''
|
13 |
|
14 |
+
@classmethod
|
15 |
+
def load_env_variables(cls):
|
16 |
+
'''
|
17 |
+
Gestion des tokens par variables d'environnement
|
18 |
+
On utilise dotenv, sauf si la platforme est un space HuggingFace
|
19 |
+
Dans ce cas les variables d'env sont déjà chargées
|
20 |
+
'''
|
21 |
+
# HF_ACTIVE est une variable d'environnement créée dans les spaces HuggingFace
|
22 |
+
# Elle sert à savoir que l'appli tourne dans un space
|
23 |
+
if not os.getenv("HF_ACTIVE"): # Utilisation ailleurs que dans un space
|
24 |
+
# load_dotenv ne passe pas dans un space HuggingFace
|
25 |
+
from dotenv import load_dotenv
|
26 |
+
load_dotenv()
|
27 |
+
|
28 |
+
|
29 |
@abstractmethod
|
30 |
def ask_llm(self, question:str)->str:
|
31 |
pass
|
src/astore.py
DELETED
@@ -1,39 +0,0 @@
|
|
1 |
-
from abc import ABC, abstractmethod
|
2 |
-
|
3 |
-
|
4 |
-
class AStore(ABC):
|
5 |
-
'''
|
6 |
-
Classe abstraite de base pour tous les stores :
|
7 |
-
Chroma
|
8 |
-
Perso
|
9 |
-
...
|
10 |
-
'''
|
11 |
-
|
12 |
-
@abstractmethod
|
13 |
-
def reset(self)->None:
|
14 |
-
pass
|
15 |
-
|
16 |
-
@abstractmethod
|
17 |
-
def print_infos(self)->None:
|
18 |
-
pass
|
19 |
-
|
20 |
-
@abstractmethod
|
21 |
-
def add_to_collection(self, collection_name:str, source:str, vectors:list[list[float]], chunks:list[str])->None:
|
22 |
-
pass
|
23 |
-
|
24 |
-
@abstractmethod
|
25 |
-
def delete_collection(self, name:str)->None:
|
26 |
-
pass
|
27 |
-
|
28 |
-
@abstractmethod
|
29 |
-
def get_similar_vector(self, vector:list[float], collection_name:str)->list[float]:
|
30 |
-
pass
|
31 |
-
|
32 |
-
@abstractmethod
|
33 |
-
def get_similar_chunk(self, query_vector:list[float], collection_name:str)->tuple[str, str]:
|
34 |
-
pass
|
35 |
-
|
36 |
-
@abstractmethod
|
37 |
-
def get_similar_chunks(self, query_vector:list[float], count:int, collection_name:str):
|
38 |
-
pass
|
39 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/model_huggingface.py
CHANGED
@@ -10,14 +10,7 @@ class HuggingFaceModel(AModel):
|
|
10 |
self.llm_name:str = llm_name
|
11 |
self.feature_name:str = feature_name
|
12 |
self.temperature = temperature
|
13 |
-
|
14 |
-
if (os.getenv("HF_ACTIVE")): # Lancement depuis l'app sur HuggingFace
|
15 |
-
api_token = os.getenv("HF_TOKEN")
|
16 |
-
else: # Lancement depuis mon ordi
|
17 |
-
# print("Launch Rag in HuggingFace local")
|
18 |
-
from dotenv import load_dotenv # Trick: ne passe pas dans une app sur HuggingFace
|
19 |
-
load_dotenv()
|
20 |
-
api_token = os.getenv("HUGGINGFACEHUB_API_TOKEN")
|
21 |
try:
|
22 |
self.model = InferenceClient(api_key=api_token)
|
23 |
except:
|
@@ -29,7 +22,7 @@ class HuggingFaceModel(AModel):
|
|
29 |
resp = self.model.chat.completions.create(
|
30 |
model=self.llm_name,
|
31 |
messages=messages,
|
32 |
-
max_tokens=
|
33 |
temperature=self.temperature,
|
34 |
# stream=True
|
35 |
)
|
|
|
10 |
self.llm_name:str = llm_name
|
11 |
self.feature_name:str = feature_name
|
12 |
self.temperature = temperature
|
13 |
+
api_token = os.getenv("HUGGINGFACEHUB_API_TOKEN")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
14 |
try:
|
15 |
self.model = InferenceClient(api_key=api_token)
|
16 |
except:
|
|
|
22 |
resp = self.model.chat.completions.create(
|
23 |
model=self.llm_name,
|
24 |
messages=messages,
|
25 |
+
max_tokens=2048,
|
26 |
temperature=self.temperature,
|
27 |
# stream=True
|
28 |
)
|
src/rag.py
CHANGED
@@ -1,14 +1,14 @@
|
|
1 |
-
import sys
|
2 |
-
|
3 |
from pypdf import PdfReader
|
4 |
from .chunker import Chunker
|
5 |
-
from .amodel import
|
6 |
-
from .model_huggingface import HuggingFaceModel
|
7 |
from .store import Store
|
8 |
|
|
|
|
|
9 |
|
10 |
class Rag:
|
11 |
'''
|
|
|
12 |
Classe qui s'occupe de toute la chaine du RAG.
|
13 |
Elle permet :
|
14 |
d'interroger un llm directement (sans RAG) avec ask_llm()
|
@@ -29,39 +29,36 @@ class Rag:
|
|
29 |
Question : {question}
|
30 |
"""
|
31 |
|
32 |
-
def __init__(self,
|
33 |
-
'''
|
34 |
Constructeur du Rag
|
35 |
Args:
|
36 |
-
|
37 |
-
|
|
|
38 |
Exception:
|
39 |
-
Si le
|
40 |
-
Si le type de model est inconnu
|
41 |
'''
|
42 |
-
self.
|
|
|
|
|
43 |
try:
|
44 |
-
match model_type:
|
45 |
-
case ModelType.MTHUGGINGFACE:
|
46 |
-
self.model = HuggingFaceModel("meta-llama/Meta-Llama-3-8B-Instruct", "sentence-transformers/all-MiniLM-l6-v2", 0)
|
47 |
-
case _:
|
48 |
-
raise Exception("Rag.__init__: Unknown model type: {mt} : {v}".format(mt=ModelType.to_str(model_type), v=model_type))
|
49 |
self.emb_store = Store(store_dir) # persistant
|
50 |
# self.emb_store = Store(None) # éphémère
|
51 |
-
except
|
52 |
raise
|
53 |
|
54 |
def get_llm_name(self):
|
55 |
-
return self.
|
56 |
|
57 |
def get_feature_name(self):
|
58 |
-
return self.
|
59 |
|
60 |
def get_temperature(self):
|
61 |
-
return self.
|
62 |
|
63 |
def set_temperature(self, temperature:float):
|
64 |
-
self.
|
65 |
|
66 |
def reset_store(self):
|
67 |
self.emb_store.reset()
|
@@ -71,7 +68,7 @@ class Rag:
|
|
71 |
|
72 |
def create_vectors(self, chunks:list[str])->list[list[float]]:
|
73 |
'''
|
74 |
-
Renvoie les vecteurs correspondant à 'chunks', calculés par '
|
75 |
Args:
|
76 |
chunks: les extraits de texte à calculer
|
77 |
Return:
|
@@ -80,7 +77,7 @@ class Rag:
|
|
80 |
vectors:list = []
|
81 |
tokens:int = 0
|
82 |
try:
|
83 |
-
vectors:list[list[float]] = self.
|
84 |
return vectors
|
85 |
except:
|
86 |
raise
|
@@ -101,17 +98,8 @@ class Rag:
|
|
101 |
Return:
|
102 |
La liste des chunks
|
103 |
'''
|
104 |
-
# splitter = RecursiveCharacterTextSplitter(
|
105 |
-
# # separator="\n",
|
106 |
-
# chunk_size=1000,
|
107 |
-
# chunk_overlap=200,
|
108 |
-
# length_function=len,
|
109 |
-
# is_separator_regex=False
|
110 |
-
# )
|
111 |
-
# chunks = splitter.split_text(text)
|
112 |
-
# print("get_chunks: " + str(len(chunks)))
|
113 |
chunker = Chunker()
|
114 |
-
chunks = chunker.split_basic(text=text, char_count=
|
115 |
return chunks
|
116 |
|
117 |
def add_pdf_to_store(self, file_name:str, collection_name:str)->None:
|
@@ -157,14 +145,14 @@ class Rag:
|
|
157 |
|
158 |
def ask_llm(self, question:str)->str:
|
159 |
'''
|
160 |
-
Pose une question au
|
161 |
Args:
|
162 |
question: La question qu'on veut lui poser
|
163 |
Returns:
|
164 |
-
La réponse du
|
165 |
'''
|
166 |
try:
|
167 |
-
return self.
|
168 |
except:
|
169 |
return "Error while comminicating with model !"
|
170 |
|
@@ -175,8 +163,8 @@ class Rag:
|
|
175 |
question: La question qu'on veut lui poser
|
176 |
collection_name: le nom de la collection que l'on veut interroger
|
177 |
Returns:
|
178 |
-
Le prompt effectivement donné au
|
179 |
-
La réponse du
|
180 |
Les sources du RAG utilisées
|
181 |
Les ids des documents du RAG
|
182 |
'''
|
@@ -188,7 +176,7 @@ class Rag:
|
|
188 |
return "", "Error: {name} is no more in the database !".format(name=collection_name), [], []
|
189 |
try:
|
190 |
# Transformer la 'question' en vecteur avec emb_model
|
191 |
-
query_vector:list[float] = self.
|
192 |
# Récupérer les chunks du store similaires à la question
|
193 |
chunks, sources, ids = self.emb_store.get_similar_chunks(
|
194 |
query_vector=query_vector,
|
@@ -207,42 +195,7 @@ class Rag:
|
|
207 |
except:
|
208 |
return "", "Error with communicating with model !", [], []
|
209 |
|
210 |
-
|
211 |
-
# Test placé ici pendant la mise au point
|
212 |
-
STORE_DIR = "./db/chroma_vectors"
|
213 |
-
# rag = Rag(ModelType.MTOPENAI, store_dir=STORE_DIR)
|
214 |
-
rag = Rag(ModelType.MTHUGGINGFACE, store_dir=STORE_DIR)
|
215 |
-
# rag = Rag(llm_type=ModelType.MTHUGGINGFACE, emb_type=ModelType.MTHUGGINGFACE, store_dir=STORE_DIR)
|
216 |
-
|
217 |
-
rag.reset_store()
|
218 |
-
rag.add_pdf_to_store("chap-1-Statique.pdf", "T_SPCL")
|
219 |
-
# rag.add_pdf_to_store("chap-2-Regulation.pdf", "T_SPCL")
|
220 |
-
# rag.add_pdf_to_store("chap-3-Dynamique.pdf", "T_SPCL")
|
221 |
-
# rag.add_pdf_to_store("chap-4-Echangeurs.pdf", "T_SPCL")
|
222 |
-
|
223 |
-
rag.emb_store.print_infos()
|
224 |
-
|
225 |
-
prompt, resp, sources, ids = rag.ask_rag(
|
226 |
-
question="Quelle est la différence entre une pression relative et une pression absolue?",
|
227 |
-
# question="Qu'est-ce qu'un échangeur à contre-courant?",
|
228 |
-
# question="Quelle est la formule de la résistance thermique? Réponds brièvement",
|
229 |
-
# question="Quelle est l'équation de Bernouilli avec les termes de pompe et pertes de charges? Réponds brièvement",
|
230 |
-
# question="Que signifie le terme de vitesse dans l'équation de Bernouilli ?",
|
231 |
-
# question="Transforme 1 bar en mètre de colonne d'eau",
|
232 |
-
# question="A quoi correspond HMT d'une pompe?",
|
233 |
-
collection_name="T_SPCL"
|
234 |
-
)
|
235 |
-
print(prompt)
|
236 |
-
print("---------------------------")
|
237 |
-
print(resp)
|
238 |
-
print("---------------------------")
|
239 |
-
print("sources:", sources)
|
240 |
-
print("ids=", ids)
|
241 |
-
|
242 |
-
# print(rag.ask_llm("Quelle est l'équation de Bernouilli avec les termes de pompe et pertes de charges? Réponds brièvement"))
|
243 |
-
|
244 |
-
if __name__ == "__main__":
|
245 |
-
test_cours_TSTL()
|
246 |
|
247 |
|
248 |
|
|
|
|
|
|
|
1 |
from pypdf import PdfReader
|
2 |
from .chunker import Chunker
|
3 |
+
from .amodel import AModel
|
|
|
4 |
from .store import Store
|
5 |
|
6 |
+
CHUNK_CHAR_COUNT = 1000
|
7 |
+
CHUNK_OVERLAP = 200
|
8 |
|
9 |
class Rag:
|
10 |
'''
|
11 |
+
RAG naïf
|
12 |
Classe qui s'occupe de toute la chaine du RAG.
|
13 |
Elle permet :
|
14 |
d'interroger un llm directement (sans RAG) avec ask_llm()
|
|
|
29 |
Question : {question}
|
30 |
"""
|
31 |
|
32 |
+
def __init__(self, llm:AModel, emb:AModel, store_dir:str)->None:
|
33 |
+
'''
|
34 |
Constructeur du Rag
|
35 |
Args:
|
36 |
+
llm: le model de langage
|
37 |
+
emb: le model d'embeddings
|
38 |
+
store_dir: le répertoire de persistance de la base de données ou None pour éphémère
|
39 |
Exception:
|
40 |
+
Si le store ne peut pas se créer (répertoire inaccessible par ex.)
|
|
|
41 |
'''
|
42 |
+
self.llm:AModel = llm
|
43 |
+
self.emb:AModel = emb
|
44 |
+
self.store_dir:str = store_dir
|
45 |
try:
|
|
|
|
|
|
|
|
|
|
|
46 |
self.emb_store = Store(store_dir) # persistant
|
47 |
# self.emb_store = Store(None) # éphémère
|
48 |
+
except:
|
49 |
raise
|
50 |
|
51 |
def get_llm_name(self):
|
52 |
+
return self.llm.get_llm_name()
|
53 |
|
54 |
def get_feature_name(self):
|
55 |
+
return self.emb.get_feature_name()
|
56 |
|
57 |
def get_temperature(self):
|
58 |
+
return self.llm.get_temperature()
|
59 |
|
60 |
def set_temperature(self, temperature:float):
|
61 |
+
self.llm.set_temperature(temperature)
|
62 |
|
63 |
def reset_store(self):
|
64 |
self.emb_store.reset()
|
|
|
68 |
|
69 |
def create_vectors(self, chunks:list[str])->list[list[float]]:
|
70 |
'''
|
71 |
+
Renvoie les vecteurs correspondant à 'chunks', calculés par 'emb'
|
72 |
Args:
|
73 |
chunks: les extraits de texte à calculer
|
74 |
Return:
|
|
|
77 |
vectors:list = []
|
78 |
tokens:int = 0
|
79 |
try:
|
80 |
+
vectors:list[list[float]] = self.emb.create_vectors(chunks) # batch si le model le permet
|
81 |
return vectors
|
82 |
except:
|
83 |
raise
|
|
|
98 |
Return:
|
99 |
La liste des chunks
|
100 |
'''
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
101 |
chunker = Chunker()
|
102 |
+
chunks = chunker.split_basic(text=text, char_count=CHUNK_CHAR_COUNT, overlap=CHUNK_OVERLAP)
|
103 |
return chunks
|
104 |
|
105 |
def add_pdf_to_store(self, file_name:str, collection_name:str)->None:
|
|
|
145 |
|
146 |
def ask_llm(self, question:str)->str:
|
147 |
'''
|
148 |
+
Pose une question au llm, attend sa réponse et la renvoie.
|
149 |
Args:
|
150 |
question: La question qu'on veut lui poser
|
151 |
Returns:
|
152 |
+
La réponse du llm
|
153 |
'''
|
154 |
try:
|
155 |
+
return self.llm.ask_llm(question=question)
|
156 |
except:
|
157 |
return "Error while comminicating with model !"
|
158 |
|
|
|
163 |
question: La question qu'on veut lui poser
|
164 |
collection_name: le nom de la collection que l'on veut interroger
|
165 |
Returns:
|
166 |
+
Le prompt effectivement donné au llm
|
167 |
+
La réponse du llm
|
168 |
Les sources du RAG utilisées
|
169 |
Les ids des documents du RAG
|
170 |
'''
|
|
|
176 |
return "", "Error: {name} is no more in the database !".format(name=collection_name), [], []
|
177 |
try:
|
178 |
# Transformer la 'question' en vecteur avec emb_model
|
179 |
+
query_vector:list[float] = self.emb.create_vector(question)
|
180 |
# Récupérer les chunks du store similaires à la question
|
181 |
chunks, sources, ids = self.emb_store.get_similar_chunks(
|
182 |
query_vector=query_vector,
|
|
|
195 |
except:
|
196 |
return "", "Error with communicating with model !", [], []
|
197 |
|
198 |
+
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
199 |
|
200 |
|
201 |
|