Commit
·
f6033ca
1
Parent(s):
7f91951
Update space
Browse files- README.md +113 -4
- app.py +46 -69
- assets/logo.png +0 -0
- config.yaml +38 -0
- model/ModelIntegrations.py +42 -0
- model/ModelStrategy.py +6 -0
- model/__init__.py +0 -0
- model/selector.py +42 -0
- pages/chatbot.py +82 -0
- pages/documents.py +35 -0
- pages/form.py +14 -0
- pages/persistent_documents.py +35 -0
- pages/prompt_system.py +12 -0
- rag.py +78 -36
- requirements.txt +19 -7
- util.py +7 -0
- vectore_store/ConnectorStrategy.py +14 -0
- vectore_store/PineconeConnector.py +100 -0
- vectore_store/VectoreStoreManager.py +15 -0
- vectore_store/__init__.py +0 -0
README.md
CHANGED
@@ -1,13 +1,122 @@
|
|
1 |
---
|
2 |
title: Viti
|
3 |
emoji: 💻
|
4 |
-
colorFrom:
|
5 |
-
colorTo:
|
6 |
sdk: streamlit
|
7 |
-
sdk_version: 1.
|
8 |
app_file: app.py
|
9 |
pinned: false
|
10 |
license: mit
|
11 |
---
|
12 |
|
13 |
-
Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
---
|
2 |
title: Viti
|
3 |
emoji: 💻
|
4 |
+
colorFrom: purple
|
5 |
+
colorTo: indigo
|
6 |
sdk: streamlit
|
7 |
+
sdk_version: 1.39.0
|
8 |
app_file: app.py
|
9 |
pinned: false
|
10 |
license: mit
|
11 |
---
|
12 |
|
13 |
+
<!-- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference -->
|
14 |
+
|
15 |
+
# Application Template - README
|
16 |
+
|
17 |
+
## Introduction
|
18 |
+
|
19 |
+
Cette application sert de base/template pour en déployer d'autres sur le même modèle via Huggingface. Elle est facilement duplicable en dupliquant l'espace. Elle est décomposée en plusieurs sections pour offrir une gestion complète des documents et des dialogues avec une Intelligence Artificielle (IA).
|
20 |
+
|
21 |
+
### Structure de l'application
|
22 |
+
|
23 |
+
L'application est structurée en trois parties principales :
|
24 |
+
|
25 |
+
- **Documents**
|
26 |
+
- *Communs*
|
27 |
+
- *Vos Documents*
|
28 |
+
- **Configurations**
|
29 |
+
- *Prompt système*
|
30 |
+
- *Paramètres*
|
31 |
+
- **Dialogue**
|
32 |
+
- *Chatbot*
|
33 |
+
|
34 |
+
### Fonctionnement des sections
|
35 |
+
|
36 |
+
#### 1. Documents
|
37 |
+
|
38 |
+
- **Documents Communs** :
|
39 |
+
Cette section permet de déposer des documents accessibles à tous les utilisateurs de l'application. Ces documents sont vectorisés et stockés dans une base de données vectorielle (voir section dédiée). Ils seront explorés lors des interactions avec l'IA.
|
40 |
+
|
41 |
+
- **Vos Documents** :
|
42 |
+
Chaque utilisateur peut uploader ses propres documents, qui seront pris en compte pendant sa session. Ces documents sont temporaires et ne sont accessibles que durant la session active de l'utilisateur.
|
43 |
+
|
44 |
+
#### 2. Configurations
|
45 |
+
|
46 |
+
- **Prompt système** :
|
47 |
+
Cette section permet de configurer le *prompt système* utilisé lors des conversations avec l'IA. Ce prompt influence le comportement de l'IA pendant le dialogue.
|
48 |
+
|
49 |
+
- **Paramètres** :
|
50 |
+
Ici, vous pouvez ajuster les paramètres dynamiques de l'application, tels que les préférences utilisateurs ou les options spécifiques à votre usage.
|
51 |
+
|
52 |
+
#### 3. Dialogue
|
53 |
+
|
54 |
+
- **Chatbot** :
|
55 |
+
Une interface de discussion avec l'IA, où il est possible de choisir le modèle d'IA avec lequel interagir. Vous pouvez aussi commencer la conversation à partir d'un *prompt* pré-défini.
|
56 |
+
|
57 |
+
## Base de Données Vectorielle
|
58 |
+
|
59 |
+
La base de données vectorielle permet de stocker de manière permanente les différents vecteurs de documents, afin de faciliter leur recherche et leur utilisation dans les conversations avec l'IA.
|
60 |
+
|
61 |
+
### Pinecone
|
62 |
+
|
63 |
+
L'application utilise **Pinecone**, une solution cloud pour la gestion de bases de données vectorielles. Pinecone simplifie la gestion des vecteurs et permet une intégration efficace avec l'application.
|
64 |
+
|
65 |
+
Pour que l'intégration fonctionne correctement, vous devez renseigner les variables d'environnement suivantes :
|
66 |
+
|
67 |
+
- **PINECONE_API_KEY** : Clé d'API fournie par Pinecone pour l'accès à votre compte.
|
68 |
+
- **PINECONE_INDEX_NAME** : Le nom de l'index Pinecone dans lequel les vecteurs seront stockés.
|
69 |
+
- **PINECONE_NAMESPACE** : Un namespace unique propre à chaque application, utilisé pour organiser les vecteurs.
|
70 |
+
|
71 |
+
Ces informations sont disponibles directement dans votre compte Pinecone, et doivent être correctement configurées pour permettre le fonctionnement de la base de données vectorielle.
|
72 |
+
|
73 |
+
|
74 |
+
## Configuration de l'application
|
75 |
+
|
76 |
+
Vous pouvez configurer votre application plus finement en la personalisant en fonction de vos besoins. Ces configurations se font dans le fichier *config.yaml* accessible dans la partie *Files* de votre espace Huggingface. La modification se fait ensuite via le bouton *'edit'*.
|
77 |
+
Une fois, vos modifications effectuées, cliquez sur *'Commit changes to main'* pour les enregistrer et relancer automatiquement l'application.
|
78 |
+
|
79 |
+
#### Paramètres Dynamiques
|
80 |
+
|
81 |
+
Les paramètres peuvent être ajustés dans la section **variables**, en mettant la liste des variables souhaitées.
|
82 |
+
Pour chacune d'entre elles, un *label*, une *key* et optionnelement une valeur par défaut *value* sont nécessaires.
|
83 |
+
Pour être prise en compte, ces variables doivent être implémenté dans le prompt template via leur *'key'* sous la forme **{ma_variable}**
|
84 |
+
|
85 |
+
#### Prompt template
|
86 |
+
|
87 |
+
Vous pouvez directement spécifier votre prompt template dans la section **prompt_template** du fichier de configuration
|
88 |
+
|
89 |
+
#### Prompt system
|
90 |
+
|
91 |
+
Egalement, vous pouvez renseigner un prompt système par défaut, dans la section **prompt_system** du fichier de configuration
|
92 |
+
|
93 |
+
#### Prompts par Défaut
|
94 |
+
|
95 |
+
Des *prompts* par défaut peuvent être définis pour démarrer les conversations avec l'IA. Ces *prompts* sont personnalisables dans la section **prompts**.
|
96 |
+
La première tabulation correspond à une catégorie, permettant de faire des regroupements.
|
97 |
+
Chaque '-' représente ensuite un prompt qui sera proposé.
|
98 |
+
|
99 |
+
## Déploiement
|
100 |
+
|
101 |
+
Pour déployer cette application sur Huggingface :
|
102 |
+
|
103 |
+
1. Dupliquez l'espace Huggingface existant.
|
104 |
+
2. Renseignez les variables d'environnements. Il vous sera demandé de rentrer toutes les variables d'environnements. Vous les variables qui seront propres à votre application :
|
105 |
+
- **APP_NAME** : Nom de votre application
|
106 |
+
- **PINECONE_NAMESPACE** : Espace de stockage permanent de votre application
|
107 |
+
3. Ajustez votre configuration dans le fichier *config.yaml* (voir section **Configuration de l'application**)
|
108 |
+
|
109 |
+
|
110 |
+
## Variables d'environnements
|
111 |
+
| Variable | Description
|
112 |
+
|----------|----------
|
113 |
+
**APP_NAME**|Nom de l'application
|
114 |
+
**ANTHROPIC_API_KEY**| Clé API Anthropic
|
115 |
+
**MISTRAL_API_KEY**|Clé API Mistral
|
116 |
+
**OPENAI_API_KEY**|Clé API OpenAI
|
117 |
+
**LLAMA_API_KEY**|Clé Llama API
|
118 |
+
**PINECONE_API_KEY**|Clé API Pinecone
|
119 |
+
**PINECONE_INDEX_NAME**|Index/BDD Pinecone
|
120 |
+
**PINECONE_NAMESPACE**|Espace de stockage propre à l'application
|
121 |
+
|
122 |
+
|
app.py
CHANGED
@@ -1,89 +1,66 @@
|
|
1 |
-
import os
|
2 |
-
import tempfile
|
3 |
import streamlit as st
|
4 |
-
|
5 |
-
from rag import ChatPDF
|
6 |
-
|
7 |
-
title = "Simulateur IA Viti"
|
8 |
-
subtitle = "Poser vos questions"
|
9 |
-
description = "Demonstrateur Viti"
|
10 |
-
LOGO = "images/agir.png"
|
11 |
-
form_help ="Vous pouvez compléter les informations ci-dessous pour personnaliser votre expérience"
|
12 |
-
placeholder = (
|
13 |
-
"Vous pouvez me posez une question sur vos attentes, appuyer sur Entrée pour valider"
|
14 |
-
)
|
15 |
-
placeholder_doc = (
|
16 |
-
"Vous pouvez charger un rapport de cout de production"
|
17 |
-
)
|
18 |
-
placeholder_url = "Récupérer les données de ce lien."
|
19 |
|
20 |
-
|
|
|
|
|
|
|
21 |
|
22 |
-
|
23 |
|
24 |
-
|
25 |
-
st.subheader(subtitle)
|
26 |
-
for i, (msg, is_user) in enumerate(st.session_state["messages"]):
|
27 |
-
message(msg, is_user=is_user, key=str(i))
|
28 |
-
st.session_state["thinking_spinner"] = st.empty()
|
29 |
|
|
|
|
|
30 |
|
31 |
-
def
|
32 |
-
if st.session_state["user_input"] and len(st.session_state["user_input"].strip()) > 0:
|
33 |
-
user_text = st.session_state["user_input"].strip()
|
34 |
-
with st.session_state["thinking_spinner"], st.spinner(f"Je réfléchis"):
|
35 |
-
agent_text = st.session_state["assistant"].ask(user_text)
|
36 |
|
37 |
-
|
38 |
-
st.session_state["messages"].append((agent_text, False))
|
39 |
|
|
|
|
|
|
|
|
|
40 |
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
|
46 |
-
for file in st.session_state["file_uploader"]:
|
47 |
-
with tempfile.NamedTemporaryFile(delete=False) as tf:
|
48 |
-
tf.write(file.getbuffer())
|
49 |
-
file_path = tf.name
|
50 |
|
51 |
-
|
52 |
-
st.session_state["assistant"].ingest(file_path)
|
53 |
-
os.remove(file_path)
|
54 |
|
|
|
55 |
|
56 |
-
|
57 |
-
if len(st.session_state) == 0:
|
58 |
-
st.session_state["messages"] = []
|
59 |
-
st.session_state["assistant"] = ChatPDF()
|
60 |
|
61 |
-
|
62 |
st.logo(LOGO)
|
63 |
-
st.
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
st.
|
69 |
-
|
70 |
-
|
71 |
-
st.
|
72 |
-
|
73 |
-
|
74 |
-
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
79 |
)
|
80 |
|
81 |
-
|
82 |
-
|
83 |
-
display_messages()
|
84 |
-
st.caption(placeholder)
|
85 |
-
st.text_input("Message", key="user_input", on_change=process_input)
|
86 |
|
87 |
|
88 |
if __name__ == "__main__":
|
89 |
-
|
|
|
|
|
|
|
1 |
import streamlit as st
|
2 |
+
import os
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3 |
|
4 |
+
from dotenv import load_dotenv
|
5 |
+
from rag import Rag
|
6 |
+
from vectore_store.PineconeConnector import PineconeConnector
|
7 |
+
from vectore_store.VectoreStoreManager import VectoreStoreManager
|
8 |
|
9 |
+
from util import getYamlConfig
|
10 |
|
11 |
+
load_dotenv()
|
|
|
|
|
|
|
|
|
12 |
|
13 |
+
GROUP_NAME = os.environ.get("APP_NAME")
|
14 |
+
LOGO = "assets/logo.png"
|
15 |
|
16 |
+
def init_app():
|
|
|
|
|
|
|
|
|
17 |
|
18 |
+
config = getYamlConfig()
|
|
|
19 |
|
20 |
+
if len(st.session_state) == 0:
|
21 |
+
# Define Vectore store strategy
|
22 |
+
pinecone_connector = PineconeConnector()
|
23 |
+
vs_manager = VectoreStoreManager(pinecone_connector)
|
24 |
|
25 |
+
st.session_state["messages"] = []
|
26 |
+
st.session_state["assistant"] = Rag(vectore_store=vs_manager)
|
27 |
+
st.session_state["data_dict"] = config['variables']
|
28 |
+
st.session_state["prompt_system"] = config['prompt_system']
|
29 |
|
|
|
|
|
|
|
|
|
30 |
|
31 |
+
def main():
|
|
|
|
|
32 |
|
33 |
+
init_app()
|
34 |
|
35 |
+
st.set_page_config(page_title=GROUP_NAME)
|
|
|
|
|
|
|
36 |
|
|
|
37 |
st.logo(LOGO)
|
38 |
+
st.title(GROUP_NAME)
|
39 |
+
|
40 |
+
saved_documents = st.Page("pages/persistent_documents.py", title="Communs", icon="🗃️")
|
41 |
+
documents = st.Page("pages/documents.py", title="Vos documents", icon="📂")
|
42 |
+
prompt_system = st.Page("pages/prompt_system.py", title="Prompt système", icon="🖊️", default=True)
|
43 |
+
form = st.Page("pages/form.py", title="Paramètres", icon="📋")
|
44 |
+
chatbot = st.Page("pages/chatbot.py", title="Chatbot", icon="🤖")
|
45 |
+
|
46 |
+
pg = st.navigation(
|
47 |
+
{
|
48 |
+
"Documents": [
|
49 |
+
saved_documents,
|
50 |
+
documents,
|
51 |
+
],
|
52 |
+
"Configurations": [
|
53 |
+
prompt_system,
|
54 |
+
form,
|
55 |
+
],
|
56 |
+
"Dialogue": [
|
57 |
+
chatbot
|
58 |
+
],
|
59 |
+
}
|
60 |
)
|
61 |
|
62 |
+
pg.run()
|
|
|
|
|
|
|
|
|
63 |
|
64 |
|
65 |
if __name__ == "__main__":
|
66 |
+
main()
|
assets/logo.png
ADDED
![]() |
config.yaml
ADDED
@@ -0,0 +1,38 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
prompts:
|
2 |
+
general:
|
3 |
+
- "Quels sont les principaux défis auxquels les agriculteurs sont confrontés aujourd'hui ?"
|
4 |
+
- "Explique-moi les différences entre l'agriculture biologique et l'agriculture conventionnelle."
|
5 |
+
|
6 |
+
techniques:
|
7 |
+
- "Comment fonctionne la rotation des cultures et pourquoi est-elle importante ?"
|
8 |
+
- "Explique les avantages de l'irrigation goutte à goutte pour les cultures."
|
9 |
+
|
10 |
+
variables:
|
11 |
+
- label : Indicateur 1
|
12 |
+
key : param1
|
13 |
+
value :
|
14 |
+
- label : Indicateur 2
|
15 |
+
key : param2
|
16 |
+
value :
|
17 |
+
|
18 |
+
prompt_system: "
|
19 |
+
Tu es un système expert sur le pilotage des données d'une exploitation viticole sur la filière viticole du Bordelais-Libournais.
|
20 |
+
Tu réponds toujours par rapport aux données du contexte. Si tu ne connais pas la réponse, ne réponds pas.
|
21 |
+
Si cela est possible, réponds sous forme d'indicateurs et de tableaux. Tu répondras en Français.
|
22 |
+
"
|
23 |
+
|
24 |
+
prompt_template: "
|
25 |
+
{prompt_system}
|
26 |
+
|
27 |
+
Dans un premier temps, tu analysera l'indicateur suivant : {param1}
|
28 |
+
de l’exploitation en les comparant avec les indicateurs équivalents dans les données de référence.
|
29 |
+
|
30 |
+
Dans un second temps, tu analysera l'indicateur suivant : {param2}
|
31 |
+
|
32 |
+
Données de référence : {commonContext}
|
33 |
+
|
34 |
+
Document de l'utilisateur : {documentContext}
|
35 |
+
|
36 |
+
Voici l'historique des messages : {messages}
|
37 |
+
Les attentes de l'utilisateur sont : {query}
|
38 |
+
"
|
model/ModelIntegrations.py
ADDED
@@ -0,0 +1,42 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import os
|
2 |
+
|
3 |
+
from .ModelStrategy import ModelStrategy
|
4 |
+
|
5 |
+
from langchain_openai import ChatOpenAI
|
6 |
+
from langchain_mistralai.chat_models import ChatMistralAI
|
7 |
+
from langchain_anthropic import ChatAnthropic
|
8 |
+
|
9 |
+
from llamaapi import LlamaAPI
|
10 |
+
from langchain_experimental.llms import ChatLlamaAPI
|
11 |
+
|
12 |
+
class MistralModel(ModelStrategy):
|
13 |
+
def get_model(self, model_name):
|
14 |
+
return ChatMistralAI(model=model_name)
|
15 |
+
|
16 |
+
|
17 |
+
class OpenAIModel(ModelStrategy):
|
18 |
+
def get_model(self, model_name):
|
19 |
+
return ChatOpenAI(model=model_name)
|
20 |
+
|
21 |
+
|
22 |
+
class AnthropicModel(ModelStrategy):
|
23 |
+
def get_model(self, model_name):
|
24 |
+
return ChatAnthropic(model=model_name)
|
25 |
+
|
26 |
+
|
27 |
+
class LlamaAPIModel(ModelStrategy):
|
28 |
+
def get_model(self, model_name):
|
29 |
+
llama = LlamaAPI(os.environ.get("LLAMA_API_KEY"))
|
30 |
+
return ChatLlamaAPI(client=llama, model=model_name)
|
31 |
+
|
32 |
+
class ModelManager():
|
33 |
+
def __init__(self):
|
34 |
+
self.models = {
|
35 |
+
"mistral": MistralModel(),
|
36 |
+
"openai": OpenAIModel(),
|
37 |
+
"anthropic": AnthropicModel(),
|
38 |
+
"llama": LlamaAPIModel()
|
39 |
+
}
|
40 |
+
|
41 |
+
def get_model(self, provider, model_name):
|
42 |
+
return self.models[provider].get_model(model_name)
|
model/ModelStrategy.py
ADDED
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from abc import ABC, abstractmethod
|
2 |
+
|
3 |
+
class ModelStrategy(ABC):
|
4 |
+
@abstractmethod
|
5 |
+
def get_model(self, model_name):
|
6 |
+
pass
|
model/__init__.py
ADDED
File without changes
|
model/selector.py
ADDED
@@ -0,0 +1,42 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import streamlit as st
|
2 |
+
from .ModelIntegrations import ModelManager
|
3 |
+
|
4 |
+
def ModelSelector():
|
5 |
+
# Dictionnaire des modèles par fournisseur
|
6 |
+
model_providers = {
|
7 |
+
"Mistral": {
|
8 |
+
"mistral-large-latest": "mistral.mistral-large-latest",
|
9 |
+
"open-mixtral-8x7b": "mistral.open-mixtral-8x7b",
|
10 |
+
},
|
11 |
+
"OpenAI": {
|
12 |
+
"gpt-4o": "openai.gpt-4o",
|
13 |
+
},
|
14 |
+
"Anthropic": {
|
15 |
+
"claude-3-5-sonnet-20240620": "anthropic.claude-3-5-sonnet-20240620",
|
16 |
+
"claude-3-opus-20240229": "anthropic.claude-3-opus-20240229",
|
17 |
+
"claude-3-sonnet-20240229": "anthropic.claude-3-sonnet-20240229",
|
18 |
+
},
|
19 |
+
# "llama": {
|
20 |
+
# "llama3.2-11b-vision": "llama.llama3.2-11b-vision",
|
21 |
+
# "llama3.2-1b": "llama.llama3.2-1b",
|
22 |
+
# "llama3.2-3b": "llama.llama3.2-3b"
|
23 |
+
# }
|
24 |
+
}
|
25 |
+
|
26 |
+
# Créer une liste avec les noms de modèle, groupés par fournisseur (fournisseur - modèle)
|
27 |
+
model_options = []
|
28 |
+
model_mapping = {}
|
29 |
+
|
30 |
+
for provider, models in model_providers.items():
|
31 |
+
for model_name, model_instance in models.items():
|
32 |
+
option_name = f"{provider} - {model_name}"
|
33 |
+
model_options.append(option_name)
|
34 |
+
model_mapping[option_name] = model_instance
|
35 |
+
|
36 |
+
# Sélection d'un modèle via un seul sélecteur
|
37 |
+
selected_model_option = st.selectbox("Choisissez votre modèle", options=model_options)
|
38 |
+
|
39 |
+
if(st.session_state["assistant"]):
|
40 |
+
splitter = model_mapping[selected_model_option].split(".")
|
41 |
+
st.session_state["assistant"].setModel(ModelManager().get_model(splitter[0], splitter[1]), splitter[1])
|
42 |
+
|
pages/chatbot.py
ADDED
@@ -0,0 +1,82 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import streamlit as st
|
2 |
+
from langchain_core.messages import AIMessage, HumanMessage
|
3 |
+
from model import selector
|
4 |
+
from util import getYamlConfig
|
5 |
+
from st_copy_to_clipboard import st_copy_to_clipboard
|
6 |
+
|
7 |
+
def display_messages():
|
8 |
+
|
9 |
+
for i, message in enumerate(st.session_state.chat_history):
|
10 |
+
if isinstance(message, AIMessage):
|
11 |
+
with st.chat_message("AI"):
|
12 |
+
# Display the model from the kwargs
|
13 |
+
model = message.kwargs.get("model", "Unknown Model") # Get the model, default to "Unknown Model"
|
14 |
+
st.write(f"**Model :** {model}")
|
15 |
+
st.markdown(message.content)
|
16 |
+
st_copy_to_clipboard(message.content,key=f"message_{i}")
|
17 |
+
|
18 |
+
elif isinstance(message, HumanMessage):
|
19 |
+
with st.chat_message("Moi"):
|
20 |
+
st.write(message.content)
|
21 |
+
|
22 |
+
|
23 |
+
def launchQuery(query: str = None):
|
24 |
+
|
25 |
+
# Initialize the assistant's response
|
26 |
+
full_response = st.write_stream(
|
27 |
+
st.session_state["assistant"].ask(
|
28 |
+
query,
|
29 |
+
prompt_system=st.session_state.prompt_system,
|
30 |
+
messages=st.session_state["chat_history"] if "chat_history" in st.session_state else [],
|
31 |
+
variables=st.session_state["data_dict"]
|
32 |
+
))
|
33 |
+
|
34 |
+
# Temporary placeholder AI message in chat history
|
35 |
+
st.session_state["chat_history"].append(AIMessage(content=full_response, kwargs={"model": st.session_state["assistant"].getReadableModel()}))
|
36 |
+
st.rerun()
|
37 |
+
|
38 |
+
|
39 |
+
def show_prompts():
|
40 |
+
yaml_data = getYamlConfig()["prompts"]
|
41 |
+
|
42 |
+
expander = st.expander("Prompts pré-définis")
|
43 |
+
|
44 |
+
for categroy in yaml_data:
|
45 |
+
expander.write(categroy.capitalize())
|
46 |
+
|
47 |
+
for item in yaml_data[categroy]:
|
48 |
+
if expander.button(item, key=f"button_{item}"):
|
49 |
+
launchQuery(item)
|
50 |
+
|
51 |
+
|
52 |
+
def page():
|
53 |
+
st.subheader("Posez vos questions")
|
54 |
+
|
55 |
+
if "assistant" not in st.session_state:
|
56 |
+
st.text("Assistant non initialisé")
|
57 |
+
|
58 |
+
if "chat_history" not in st.session_state:
|
59 |
+
st.session_state["chat_history"] = []
|
60 |
+
|
61 |
+
st.markdown("<style>iframe{height:50px;}</style>", unsafe_allow_html=True)
|
62 |
+
|
63 |
+
# Collpase for default prompts
|
64 |
+
show_prompts()
|
65 |
+
|
66 |
+
# Models selector
|
67 |
+
selector.ModelSelector()
|
68 |
+
|
69 |
+
# Displaying messages
|
70 |
+
display_messages()
|
71 |
+
|
72 |
+
|
73 |
+
user_query = st.chat_input("")
|
74 |
+
if user_query is not None and user_query != "":
|
75 |
+
|
76 |
+
st.session_state["chat_history"].append(HumanMessage(content=user_query))
|
77 |
+
|
78 |
+
# Stream and display response
|
79 |
+
launchQuery(user_query)
|
80 |
+
|
81 |
+
|
82 |
+
page()
|
pages/documents.py
ADDED
@@ -0,0 +1,35 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import os
|
2 |
+
import tempfile
|
3 |
+
import streamlit as st
|
4 |
+
|
5 |
+
def read_and_save_file():
|
6 |
+
st.session_state["messages"] = []
|
7 |
+
st.session_state["user_input"] = ""
|
8 |
+
|
9 |
+
for file in st.session_state["file_uploader"]:
|
10 |
+
with tempfile.NamedTemporaryFile(delete=False) as tf:
|
11 |
+
tf.write(file.getbuffer())
|
12 |
+
file_path = tf.name
|
13 |
+
|
14 |
+
with st.session_state["ingestion_spinner"], st.spinner(f"Chargement {file.name}"):
|
15 |
+
st.session_state["assistant"].ingest(file_path)
|
16 |
+
os.remove(file_path)
|
17 |
+
|
18 |
+
|
19 |
+
|
20 |
+
def page():
|
21 |
+
st.subheader("Charger vos documents")
|
22 |
+
|
23 |
+
# File uploader
|
24 |
+
uploaded_file = st.file_uploader(
|
25 |
+
"Télécharger un ou plusieurs documents",
|
26 |
+
type=["pdf"],
|
27 |
+
key="file_uploader",
|
28 |
+
accept_multiple_files=True,
|
29 |
+
on_change=read_and_save_file,
|
30 |
+
)
|
31 |
+
|
32 |
+
|
33 |
+
st.session_state["ingestion_spinner"] = st.empty()
|
34 |
+
|
35 |
+
page()
|
pages/form.py
ADDED
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import streamlit as st
|
2 |
+
|
3 |
+
def page():
|
4 |
+
st.subheader("Définissez vos paramètres")
|
5 |
+
|
6 |
+
# Boucle pour créer des inputs basés sur data_dict (qui est une liste ici)
|
7 |
+
for param in st.session_state.data_dict:
|
8 |
+
# Utilisation de la clé 'label' et 'value' pour afficher et récupérer les valeurs
|
9 |
+
value = st.text_input(label=param['label'], value=param['value'] if param['value'] else "")
|
10 |
+
|
11 |
+
# Mettre à jour la valeur dans le dictionnaire après la saisie utilisateur
|
12 |
+
param['value'] = value
|
13 |
+
|
14 |
+
page()
|
pages/persistent_documents.py
ADDED
@@ -0,0 +1,35 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import os
|
2 |
+
import tempfile
|
3 |
+
import streamlit as st
|
4 |
+
|
5 |
+
def uploadToDb():
|
6 |
+
|
7 |
+
for file in st.session_state["file_uploader_commun"]:
|
8 |
+
with tempfile.NamedTemporaryFile(delete=False) as tf:
|
9 |
+
tf.write(file.getbuffer())
|
10 |
+
file_path = tf.name
|
11 |
+
|
12 |
+
with st.session_state["ingestion_spinner"], st.spinner(f"Chargement {file.name}"):
|
13 |
+
st.session_state["assistant"].ingestToDb(file_path, filename=file.name)
|
14 |
+
os.remove(file_path)
|
15 |
+
|
16 |
+
def page():
|
17 |
+
st.subheader("Montez des documents communs")
|
18 |
+
|
19 |
+
st.file_uploader(
|
20 |
+
"Télécharger un documents",
|
21 |
+
type=["pdf"],
|
22 |
+
key="file_uploader_commun",
|
23 |
+
accept_multiple_files=True,
|
24 |
+
on_change=uploadToDb,
|
25 |
+
)
|
26 |
+
|
27 |
+
st.session_state["ingestion_spinner"] = st.empty()
|
28 |
+
|
29 |
+
st.divider()
|
30 |
+
st.write("Documents dans la base de données")
|
31 |
+
|
32 |
+
for doc in st.session_state["assistant"].vector_store.getDocs():
|
33 |
+
st.write(" - "+doc)
|
34 |
+
|
35 |
+
page()
|
pages/prompt_system.py
ADDED
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import streamlit as st
|
2 |
+
|
3 |
+
def page():
|
4 |
+
|
5 |
+
st.subheader("Renseignez votre prompt système")
|
6 |
+
|
7 |
+
prompt = st.text_area("Prompt system", st.session_state.prompt_system if 'prompt_system' in st.session_state else "")
|
8 |
+
|
9 |
+
# Session State also supports attribute based syntax
|
10 |
+
st.session_state['prompt_system'] = prompt
|
11 |
+
|
12 |
+
page()
|
rag.py
CHANGED
@@ -1,7 +1,5 @@
|
|
1 |
import os
|
2 |
-
|
3 |
-
# import sys
|
4 |
-
# sys.modules['sqlite3'] = sys.modules.pop('pysqlite3')
|
5 |
from dotenv import load_dotenv
|
6 |
from langchain_community.vectorstores import FAISS
|
7 |
from langchain_mistralai.chat_models import ChatMistralAI
|
@@ -12,47 +10,66 @@ from langchain.text_splitter import RecursiveCharacterTextSplitter
|
|
12 |
from langchain.schema.runnable import RunnablePassthrough
|
13 |
from langchain.prompts import PromptTemplate
|
14 |
from langchain_community.vectorstores.utils import filter_complex_metadata
|
15 |
-
#add new import
|
16 |
from langchain_community.document_loaders.csv_loader import CSVLoader
|
17 |
|
|
|
|
|
|
|
18 |
# load .env in local dev
|
19 |
load_dotenv()
|
20 |
env_api_key = os.environ.get("MISTRAL_API_KEY")
|
21 |
-
llm_model = "open-mixtral-8x7b"
|
22 |
|
23 |
-
class
|
24 |
-
|
25 |
retriever = None
|
26 |
chain = None
|
|
|
27 |
|
28 |
-
def __init__(self):
|
29 |
-
|
30 |
-
self.model = ChatMistralAI(model=llm_model)
|
31 |
-
self.
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
43 |
|
44 |
def ingest(self, pdf_file_path: str):
|
45 |
docs = PyPDFLoader(file_path=pdf_file_path).load()
|
46 |
|
47 |
-
|
48 |
chunks = self.text_splitter.split_documents(docs)
|
49 |
chunks = filter_complex_metadata(chunks)
|
50 |
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
# vector_store = Chroma.from_documents(documents=chunks, embedding=embeddings)
|
55 |
-
self.retriever = vector_store.as_retriever(
|
56 |
search_type="similarity_score_threshold",
|
57 |
search_kwargs={
|
58 |
"k": 3,
|
@@ -60,18 +77,43 @@ class ChatPDF:
|
|
60 |
},
|
61 |
)
|
62 |
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
67 |
|
68 |
-
|
69 |
-
if not
|
70 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
71 |
|
72 |
-
return self.chain.
|
73 |
|
74 |
def clear(self):
|
|
|
75 |
self.vector_store = None
|
76 |
self.retriever = None
|
77 |
self.chain = None
|
|
|
1 |
import os
|
2 |
+
|
|
|
|
|
3 |
from dotenv import load_dotenv
|
4 |
from langchain_community.vectorstores import FAISS
|
5 |
from langchain_mistralai.chat_models import ChatMistralAI
|
|
|
10 |
from langchain.schema.runnable import RunnablePassthrough
|
11 |
from langchain.prompts import PromptTemplate
|
12 |
from langchain_community.vectorstores.utils import filter_complex_metadata
|
|
|
13 |
from langchain_community.document_loaders.csv_loader import CSVLoader
|
14 |
|
15 |
+
from util import getYamlConfig
|
16 |
+
|
17 |
+
|
18 |
# load .env in local dev
|
19 |
load_dotenv()
|
20 |
env_api_key = os.environ.get("MISTRAL_API_KEY")
|
|
|
21 |
|
22 |
+
class Rag:
|
23 |
+
document_vector_store = None
|
24 |
retriever = None
|
25 |
chain = None
|
26 |
+
readableModelName = ""
|
27 |
|
28 |
+
def __init__(self, vectore_store=None):
|
29 |
+
|
30 |
+
# self.model = ChatMistralAI(model=llm_model)
|
31 |
+
self.embedding = MistralAIEmbeddings(model="mistral-embed", mistral_api_key=env_api_key)
|
32 |
+
|
33 |
+
self.text_splitter = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=100, length_function=len)
|
34 |
+
|
35 |
+
base_template = getYamlConfig()['prompt_template']
|
36 |
+
self.prompt = PromptTemplate.from_template(base_template)
|
37 |
+
|
38 |
+
self.vector_store = vectore_store
|
39 |
+
|
40 |
+
def setModel(self, model, readableModelName = ""):
|
41 |
+
self.model = model
|
42 |
+
self.readableModelName = readableModelName
|
43 |
+
|
44 |
+
def getReadableModel(self):
|
45 |
+
return self.readableModelName
|
46 |
+
|
47 |
+
def ingestToDb(self, file_path: str, filename: str):
|
48 |
+
|
49 |
+
docs = PyPDFLoader(file_path=file_path).load()
|
50 |
+
|
51 |
+
# Extract all text from the document
|
52 |
+
text = ""
|
53 |
+
for page in docs:
|
54 |
+
text += page.page_content
|
55 |
+
|
56 |
+
# Split the text into chunks
|
57 |
+
chunks = self.text_splitter.split_text(text)
|
58 |
+
|
59 |
+
return self.vector_store.addDoc(filename=filename, text_chunks=chunks, embedding=self.embedding)
|
60 |
+
|
61 |
+
def getDbFiles(self):
|
62 |
+
return self.vector_store.getDocs()
|
63 |
|
64 |
def ingest(self, pdf_file_path: str):
|
65 |
docs = PyPDFLoader(file_path=pdf_file_path).load()
|
66 |
|
|
|
67 |
chunks = self.text_splitter.split_documents(docs)
|
68 |
chunks = filter_complex_metadata(chunks)
|
69 |
|
70 |
+
document_vector_store = FAISS.from_documents(chunks, self.embedding)
|
71 |
+
|
72 |
+
self.retriever = document_vector_store.as_retriever(
|
|
|
|
|
73 |
search_type="similarity_score_threshold",
|
74 |
search_kwargs={
|
75 |
"k": 3,
|
|
|
77 |
},
|
78 |
)
|
79 |
|
80 |
+
def ask(self, query: str, prompt_system: str, messages: list, variables: list = None):
|
81 |
+
self.chain = self.prompt | self.model | StrOutputParser()
|
82 |
+
|
83 |
+
# Retrieve the context document
|
84 |
+
if self.retriever is None:
|
85 |
+
documentContext = ''
|
86 |
+
else:
|
87 |
+
documentContext = self.retriever.invoke(query)
|
88 |
+
|
89 |
+
# Retrieve the VectoreStore
|
90 |
+
contextCommon = self.vector_store.retriever(query, self.embedding)
|
91 |
+
|
92 |
+
# Dictionnaire de base avec les variables principales
|
93 |
+
chain_input = {
|
94 |
+
"query": query,
|
95 |
+
"documentContext": documentContext,
|
96 |
+
"commonContext": contextCommon,
|
97 |
+
"prompt_system": prompt_system,
|
98 |
+
"messages": messages
|
99 |
+
}
|
100 |
|
101 |
+
# Suppression des valeurs nulles (facultatif)
|
102 |
+
chain_input = {k: v for k, v in chain_input.items() if v is not None}
|
103 |
+
|
104 |
+
# Si des variables sous forme de liste sont fournies
|
105 |
+
if variables:
|
106 |
+
# Convertir la liste en dictionnaire avec 'key' comme clé et 'value' comme valeur
|
107 |
+
extra_vars = {item['key']: item['value'] for item in variables if 'key' in item and 'value' in item}
|
108 |
+
|
109 |
+
# Fusionner avec chain_input
|
110 |
+
chain_input.update(extra_vars)
|
111 |
+
|
112 |
|
113 |
+
return self.chain.stream(chain_input)
|
114 |
|
115 |
def clear(self):
|
116 |
+
self.document_vector_store = None
|
117 |
self.vector_store = None
|
118 |
self.retriever = None
|
119 |
self.chain = None
|
requirements.txt
CHANGED
@@ -1,9 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
langchain
|
2 |
-
|
3 |
langchain-community
|
4 |
-
|
5 |
-
|
6 |
-
|
7 |
-
|
8 |
-
|
9 |
-
|
|
|
|
1 |
+
streamlit==1.37.0
|
2 |
+
streamlit_chat
|
3 |
+
python-dotenv
|
4 |
+
pymupdf
|
5 |
+
pypdf
|
6 |
+
python-multipart
|
7 |
+
pydantic
|
8 |
+
pinecone-notebooks
|
9 |
+
pinecone-client[grpc]
|
10 |
+
async-timeout
|
11 |
+
typing-extensions
|
12 |
langchain
|
13 |
+
langchain-openai
|
14 |
langchain-community
|
15 |
+
langchain-experimental
|
16 |
+
langchain-pinecone
|
17 |
+
langchain_mistralai
|
18 |
+
langchain_anthropic
|
19 |
+
llamaapi
|
20 |
+
pyyaml
|
21 |
+
st_copy_to_clipboard
|
util.py
ADDED
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import os
|
2 |
+
import yaml
|
3 |
+
|
4 |
+
def getYamlConfig():
|
5 |
+
file_path = os.path.join(os.path.dirname(__file__), 'config.yaml')
|
6 |
+
with open(file_path, 'r', encoding='utf-8') as file:
|
7 |
+
return yaml.safe_load(file)
|
vectore_store/ConnectorStrategy.py
ADDED
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from abc import ABC, abstractmethod
|
2 |
+
|
3 |
+
class ConnectorStrategy(ABC):
|
4 |
+
@abstractmethod
|
5 |
+
def getDocs(self):
|
6 |
+
pass
|
7 |
+
|
8 |
+
@abstractmethod
|
9 |
+
def addDoc(self, filename, text_chunks, embedding):
|
10 |
+
pass
|
11 |
+
|
12 |
+
@abstractmethod
|
13 |
+
def retriever(self, query, embedding):
|
14 |
+
pass
|
vectore_store/PineconeConnector.py
ADDED
@@ -0,0 +1,100 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import os
|
2 |
+
from dotenv import load_dotenv
|
3 |
+
|
4 |
+
from .ConnectorStrategy import ConnectorStrategy
|
5 |
+
|
6 |
+
from pinecone import Pinecone, ServerlessSpec
|
7 |
+
from langchain_openai import OpenAIEmbeddings
|
8 |
+
from langchain_pinecone import PineconeVectorStore
|
9 |
+
from langchain_core.documents import Document
|
10 |
+
|
11 |
+
import unicodedata
|
12 |
+
import time
|
13 |
+
|
14 |
+
class PineconeConnector(ConnectorStrategy):
|
15 |
+
def __init__(self):
|
16 |
+
|
17 |
+
load_dotenv()
|
18 |
+
|
19 |
+
pinecone_api_key = os.environ.get("PINECONE_API_KEY")
|
20 |
+
|
21 |
+
self.index_name = os.environ.get("PINECONE_INDEX_NAME")
|
22 |
+
self.namespace = os.environ.get("PINECONE_NAMESPACE")
|
23 |
+
|
24 |
+
|
25 |
+
pc = Pinecone(api_key=pinecone_api_key)
|
26 |
+
|
27 |
+
existing_indexes = [index_info["name"] for index_info in pc.list_indexes()]
|
28 |
+
|
29 |
+
if self.index_name not in existing_indexes:
|
30 |
+
pc.create_index(
|
31 |
+
name=self.index_name,
|
32 |
+
dimension=3072,
|
33 |
+
metric="cosine",
|
34 |
+
spec=ServerlessSpec(cloud="aws", region="us-east-1"),
|
35 |
+
)
|
36 |
+
while not pc.describe_index(self.index_name).status["ready"]:
|
37 |
+
time.sleep(1)
|
38 |
+
|
39 |
+
self.index = pc.Index(self.index_name)
|
40 |
+
|
41 |
+
|
42 |
+
def getDocs(self):
|
43 |
+
# Simulate getting docs from Pinecone
|
44 |
+
|
45 |
+
docs_names = []
|
46 |
+
for ids in self.index.list(namespace=self.namespace):
|
47 |
+
for id in ids:
|
48 |
+
name_doc = "_".join(id.split("_")[:-1])
|
49 |
+
if name_doc not in docs_names:
|
50 |
+
docs_names.append(name_doc)
|
51 |
+
|
52 |
+
return docs_names
|
53 |
+
|
54 |
+
|
55 |
+
def addDoc(self, filename, text_chunks, embedding):
|
56 |
+
try:
|
57 |
+
vector_store = PineconeVectorStore(index=self.index, embedding=embedding,namespace=self.namespace)
|
58 |
+
|
59 |
+
file_name = filename.split(".")[0].replace(" ","_").replace("-","_").replace(".","_").replace("/","_").replace("\\","_").strip()
|
60 |
+
|
61 |
+
documents = []
|
62 |
+
uuids = []
|
63 |
+
|
64 |
+
for i, chunk in enumerate(text_chunks):
|
65 |
+
clean_filename = remove_non_standard_ascii(file_name)
|
66 |
+
uuid = f"{clean_filename}_{i}"
|
67 |
+
|
68 |
+
document = Document(
|
69 |
+
page_content=chunk,
|
70 |
+
metadata={ "filename":filename, "chunk_id":uuid },
|
71 |
+
)
|
72 |
+
|
73 |
+
uuids.append(uuid)
|
74 |
+
documents.append(document)
|
75 |
+
|
76 |
+
|
77 |
+
vector_store.add_documents(documents=documents, ids=uuids)
|
78 |
+
|
79 |
+
return {"filename_id":clean_filename}
|
80 |
+
|
81 |
+
except Exception as e:
|
82 |
+
print(e)
|
83 |
+
return False
|
84 |
+
|
85 |
+
def retriever(self, query, embedding):
|
86 |
+
|
87 |
+
vector_store = PineconeVectorStore(index=self.index, embedding=embedding,namespace=self.namespace)
|
88 |
+
|
89 |
+
retriever = vector_store.as_retriever(
|
90 |
+
search_type="similarity_score_threshold",
|
91 |
+
search_kwargs={"k": 3, "score_threshold": 0.6},
|
92 |
+
)
|
93 |
+
|
94 |
+
return retriever.invoke(query)
|
95 |
+
|
96 |
+
|
97 |
+
def remove_non_standard_ascii(input_string: str) -> str:
|
98 |
+
normalized_string = unicodedata.normalize('NFKD', input_string)
|
99 |
+
return ''.join(char for char in normalized_string if 'a' <= char <= 'z' or 'A' <= char <= 'Z' or char.isdigit() or char in ' .,!?')
|
100 |
+
|
vectore_store/VectoreStoreManager.py
ADDED
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from vectore_store import ConnectorStrategy
|
2 |
+
|
3 |
+
|
4 |
+
class VectoreStoreManager:
|
5 |
+
def __init__(self, strategy: ConnectorStrategy):
|
6 |
+
self.strategy = strategy
|
7 |
+
|
8 |
+
def getDocs(self):
|
9 |
+
return self.strategy.getDocs()
|
10 |
+
|
11 |
+
def addDoc(self, filename, text_chunks, embedding):
|
12 |
+
self.strategy.addDoc(filename, text_chunks, embedding)
|
13 |
+
|
14 |
+
def retriever(self, query, embedding):
|
15 |
+
return self.strategy.retriever(query, embedding)
|
vectore_store/__init__.py
ADDED
File without changes
|