Votre Nom commited on
Commit
44a12cc
·
1 Parent(s): 1defa68

Initial deployment with RAG model

Browse files
Files changed (5) hide show
  1. .DS_Store +0 -0
  2. RAG.py +117 -0
  3. app.py +28 -0
  4. requirements.txt +7 -2
  5. src/{streamlit_app.py → template.py} +0 -0
.DS_Store ADDED
Binary file (6.15 kB). View file
 
RAG.py ADDED
@@ -0,0 +1,117 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import faiss
3
+ import pickle
4
+ import numpy as np
5
+ import openai
6
+ import tiktoken
7
+ from dotenv import load_dotenv
8
+ from openai import OpenAI
9
+ from pathlib import Path
10
+ from huggingface_hub import hf_hub_download
11
+
12
+
13
+ # —— CONFIG ——
14
+ load_dotenv()
15
+ # Use the new OpenAI client
16
+ client = OpenAI()
17
+ EMBED_MODEL = "text-embedding-3-small"
18
+ CHAT_MODEL = "o4-mini-2025-04-16"
19
+ FAISS_INDEX_FILE = "tindle_index.faiss"
20
+ IDS_PKL = "tindle_ids.pkl"
21
+ CHUNKS_PKL = "tindle_chunks.pkl"
22
+ TOP_K = 10
23
+ MAX_TOKENS_CONTEXT = 4000
24
+ SYSTEM_PROMPT = (
25
+ "Tu es un assistant expert en droit fiscal. "
26
+ "Fais d’abord appel aux passages fournis pour répondre. "
27
+ "Si ces passages sont insuffisants, utilise tes connaissances générales en le précisant clairement."
28
+ )
29
+
30
+ # —— CHARGEMENT DE L'INDEX ——
31
+
32
+ # Télécharger les fichiers depuis le repo Hugging Face
33
+ index_path = hf_hub_download(repo_id="Jordanche/fiscarag", filename=FAISS_INDEX_FILE)
34
+ ids_path = hf_hub_download(repo_id="Jordanche/fiscarag", filename=IDS_PKL)
35
+ chunks_path = hf_hub_download(repo_id="Jordanche/fiscarag", filename=CHUNKS_PKL)
36
+
37
+ # Charger les fichiers
38
+ index = faiss.read_index(index_path)
39
+ with open(ids_path, "rb") as f:
40
+ ids = pickle.load(f)
41
+ with open(chunks_path, "rb") as f:
42
+ chunks_dict = pickle.load(f)
43
+
44
+ # —— TOKEN COUNTER ——
45
+ enc = tiktoken.get_encoding("cl100k_base")
46
+ def num_tokens(s: str) -> int:
47
+ return len(enc.encode(s))
48
+
49
+
50
+ # —— FONCTIONS RAG ——
51
+
52
+ def embed_question(question: str) -> list[float]:
53
+ resp = client.embeddings.create(
54
+ model=EMBED_MODEL,
55
+ input=[question]
56
+ )
57
+ # on récupère l'attribut .data, puis .embedding
58
+ return resp.data[0].embedding
59
+
60
+
61
+ def retrieve_chunks(q_emb: list[float], k: int = TOP_K):
62
+ xq = np.array([q_emb], dtype="float32")
63
+ distances, indices = index.search(xq, k)
64
+ out = []
65
+ for dist, idx in zip(distances[0], indices[0]):
66
+ cid = ids[idx]
67
+ meta = chunks_dict[cid]
68
+ out.append({
69
+ "score": float(dist),
70
+ "id": cid,
71
+ "offset": meta["offset"],
72
+ "text": meta["text"]
73
+ })
74
+ return out
75
+
76
+
77
+ def build_context(chunks, max_tokens=MAX_TOKENS_CONTEXT):
78
+ parts, tokens = [], 0
79
+ for c in sorted(chunks, key=lambda x: x["score"]):
80
+ piece = f"(Source: {c['id']}@{c['offset']}) {c['text']}"
81
+ nt = num_tokens(piece)
82
+ if tokens + nt > max_tokens:
83
+ break
84
+ parts.append(piece)
85
+ tokens += nt
86
+ return "\n\n".join(parts)
87
+
88
+
89
+ def make_prompt(question: str, context: str):
90
+ return [
91
+ {"role": "system", "content": SYSTEM_PROMPT},
92
+ {"role": "user", "content": f"Question: {question}\n\nContexte:\n{context}"}
93
+ ]
94
+
95
+
96
+ def answer_question(question: str, k: int = TOP_K) -> str:
97
+ # 1) Embed
98
+ q_emb = embed_question(question)
99
+
100
+ # 2) Retrieve
101
+ top_chunks = retrieve_chunks(q_emb, k)
102
+
103
+ # 3) Assemble
104
+ context = build_context(top_chunks)
105
+
106
+ # 4) Prompt
107
+ messages = make_prompt(question, context)
108
+ # 5) Call LLM
109
+ resp = client.chat.completions.create(
110
+ model=CHAT_MODEL,
111
+ messages=messages )
112
+ return resp.choices[0].message.content
113
+
114
+ # —— EXEMPLE ——
115
+ if __name__ == "__main__":
116
+ question = "Quels sont les délais pour la réhabilitation d'hôtels en outre-mer ?"
117
+ print(answer_question(question, k=10))
app.py ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ from RAG import answer_question
3
+
4
+ st.set_page_config(page_title="Tindle RAG Chat", page_icon="💬", layout="centered")
5
+
6
+ st.title("💬 Tindle RAG Chat")
7
+ st.markdown("Posez une question sur le droit fiscal, l'assistant RAG vous répondra en s'appuyant sur les sources indexées.")
8
+
9
+ if "messages" not in st.session_state:
10
+ st.session_state["messages"] = []
11
+
12
+ # Affichage de l'historique du chat
13
+ for msg in st.session_state["messages"]:
14
+ if msg["role"] == "user":
15
+ st.chat_message("user").markdown(msg["content"])
16
+ else:
17
+ st.chat_message("assistant").markdown(msg["content"])
18
+
19
+ # Saisie utilisateur
20
+ if prompt := st.chat_input("Votre question..."):
21
+ st.session_state["messages"].append({"role": "user", "content": prompt})
22
+ with st.spinner("Recherche de la réponse..."):
23
+ try:
24
+ response = answer_question(prompt)
25
+ except Exception as e:
26
+ response = f"Erreur lors de l'appel au RAG : {e}"
27
+ st.session_state["messages"].append({"role": "assistant", "content": response})
28
+ st.rerun()
requirements.txt CHANGED
@@ -1,3 +1,8 @@
1
  altair
2
- pandas
3
- streamlit
 
 
 
 
 
 
1
  altair
2
+ streamlit>=1.28.0
3
+ openai>=1.0.0
4
+ faiss-cpu>=1.7.4
5
+ numpy>=1.24.0
6
+ tiktoken>=0.5.0
7
+ python-dotenv>=1.0.0
8
+ pyarrow>=12.0.0
src/{streamlit_app.py → template.py} RENAMED
File without changes