Eric Marchand commited on
Commit
f4f9c98
·
1 Parent(s): 941f36a

Meilleur gestion du multiclient

Browse files
.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 = "./files/rag_app" # Le répertoire de la base
8
- MAX_DOCS = 4 # Le nombre max de documents dans la base
 
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
- # Mettre à jour la liste des noms de collections
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, choice:str):
34
- col_name:str = choice
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
- # with gr.Row():
47
- gr.Image("./files/drane.jpg", height=100, show_download_button=False, show_fullscreen_button=False, show_label=False)
48
- # Le combo qui permet le choix du pdf
49
- names:list[str] = rag.emb_store.get_collection_names()
50
- combo = gr.Dropdown(names, label="PDFs", multiselect=False)
51
  # Le button qui permet d'uploader un pdf
52
- upload_button = gr.UploadButton("Clique pour uploader un pdf", file_types=[".pdf"], file_count="single")
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.5,
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=[combo, rag_output], show_progress="full")
71
- ask_input.submit(fn=ask_rag, inputs=[ask_input, combo], outputs=rag_output)
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))