Fallou Diagne commited on
Commit
c4f2e45
·
1 Parent(s): e93d9f3

add all files

Browse files
Files changed (2) hide show
  1. app.py +247 -0
  2. requirements.txt +116 -0
app.py ADDED
@@ -0,0 +1,247 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import asyncio
2
+ from minio import Minio
3
+ from minio.error import S3Error
4
+ from datetime import timedelta
5
+ from agno.agent import Agent
6
+ from agno.knowledge.pdf import PDFKnowledgeBase, PDFReader
7
+ from agno.vectordb.qdrant import Qdrant
8
+ from agno.storage.redis import RedisStorage
9
+ from agno.memory.v2.db.redis import RedisMemoryDb
10
+ from agno.memory.v2.memory import Memory
11
+ from agno.document.chunking.agentic import AgenticChunking
12
+ from agno.models.openai import OpenAIChat
13
+ from agno.tools.reasoning import ReasoningTools
14
+ import gradio as gr
15
+ import json
16
+ import pandas as pd
17
+ from docx import Document
18
+ import tempfile
19
+ from weasyprint import HTML
20
+ import tempfile
21
+ import markdown as md
22
+ import os
23
+ import tempfile
24
+ from PIL import Image
25
+ import io
26
+
27
+ from dotenv import load_dotenv
28
+ load_dotenv()
29
+
30
+
31
+ """
32
+ # Configuration MinIO
33
+ client = Minio(
34
+ os.getenv("ENDPOINT"),
35
+ access_key=os.getenv("ACCESS_KEY"),
36
+ secret_key=os.getenv("SECRET_KEY"),
37
+ secure=False
38
+ )
39
+
40
+ bucket_name = "iachatbotbocs"
41
+ """
42
+ # Initialisation Qdrant
43
+ vector_db = Qdrant(
44
+ collection="directives_16J",
45
+ url=os.getenv("QDRANT_URL"),
46
+ api_key=os.getenv("QDRANT_API_KEY")
47
+ )
48
+
49
+ # Initialize Redis storage with default local connection
50
+ storage = RedisStorage(
51
+ prefix="agno_test_directive", # Prefix for Redis keys to namespace the sessions
52
+ host="localhost", # Redis host address
53
+ port=6379, # Redis port number
54
+ )
55
+ # Create memory storage
56
+ memory_db = RedisMemoryDb(
57
+ prefix="agno_test_directive",
58
+ host="localhost",
59
+ port=6379,
60
+ )
61
+ memory = Memory(db=memory_db)
62
+
63
+
64
+ # Charger tous les PDF du dossier dans la base de connaissance
65
+ knowledge_base = PDFKnowledgeBase(
66
+ #path="/home/fallou/PROJECT/Repo-IA/AGNO/directive",
67
+ reader=PDFReader(chunk=True),
68
+ vector_db=vector_db,
69
+ chunking_strategy=AgenticChunking(),
70
+ )
71
+
72
+ agent = Agent(
73
+ model=OpenAIChat(id="gpt-4o"),
74
+ knowledge=knowledge_base,
75
+ search_knowledge=True,
76
+ debug_mode=False,
77
+ description=(
78
+ "Agent polyvalent spécialisé dans l'analyse, la génération et la vérification des directives de suivi des projets et programmes de l'État du Sénégal. "
79
+ "Il combine les rôles suivants : analyse des requêtes, recherche contextuelle, extraction des rôles, rédaction structurée et contrôle de conformité."
80
+ ),
81
+ instructions=[
82
+ # === CONTEXTE GLOBAL ===
83
+ "### Contexte Global\n"
84
+ "- Les directives proviennent de toutes les sources officielles : Conseils Présidentiels, Conseils des Ministres, Conseils Interministériels et réunions gouvernementales.\n"
85
+ "- Extraire **toutes les directives** présentes, même implicites ou partiellement mentionnées.\n"
86
+ "- L’objectif est de fournir une base exhaustive et directement exploitable pour le suivi des projets et programmes de l’État.\n",
87
+
88
+ # === TÂCHE 1 : RECHERCHE ET EXTRACTION ===
89
+ "### Tâche : Extraction exhaustive des directives\n"
90
+ "- Identifier toutes les directives liées à `{query}` ou aux projets/programmes mentionnés dans les documents.\n"
91
+ "- Ne pas omettre les directives secondaires ou implicites (ex : relances, rappels, suivis techniques).\n"
92
+ "- Lister chaque directive de manière indépendante.\n",
93
+
94
+
95
+ # === FORMAT DE SORTIE OBLIGATOIRE ===
96
+ "### Format de sortie obligatoire\n"
97
+ "- La sortie doit être **en Markdown structuré** avec deux sections principales :\n"
98
+ "1. **Résumé Global** : courte synthèse expliquant le contexte et nombre de directives trouvées.\n"
99
+ "2. **Détails des Directives** sous forme de tableau Markdown avec colonnes suivantes :\n"
100
+ "| Autorité émettrice | Source | Date de publication | Description détaillée de la directive | Action(s) à entreprendre | Structures responsables | Parties prenantes | Statut | Priorité | Date limite | Actions spécifiques du BOCS |\n"
101
+ "- Chaque ligne représente une directive.\n"
102
+ "- Les dates doivent être formatées `JJ Mois AAAA` .\n"
103
+ "- Ne pas inclure de JSON ou code brut, uniquement résumé + tableau Markdown.\n",
104
+
105
+ # === TÂCHE 2 : MÉTADONNÉES ET STRUCTURATION ===
106
+ "### Tâche : Structuration des directives\n"
107
+ "- Pour chaque directive, fournir :\n"
108
+ " 1. **Autorité émettrice** : Auteur de la directive (PR, PM, SGG, Ministère, etc.)\n"
109
+ " 2. **Source** (type de conseil ou réunion)\n"
110
+ " 3. **Date de publication du document** (ou 'Date du conseil')\n"
111
+ " 4. **Description détaillée** de la directive\n"
112
+ " 5. **Actions à entreprendre** (liste détaillée)\n"
113
+ " 6. **Structures responsables** (entités principales)\n"
114
+ " 7. **Parties prenantes** (internes/externes)\n"
115
+ " 8. **Statut** (En cours, Démarré, Non démarré)\n"
116
+ " 9. **Priorité** (Très Haute, Haute, Moyenne, Basse)\n"
117
+ " 10. **Date limite** (ou 'Meilleurs delais')\n"
118
+ " 11. **Actions spécifiques du BOCS** (toujours présentes, détaillées)\n",
119
+
120
+
121
+
122
+ # === TÂCHE 4 : VÉRIFICATION ===
123
+ "### Vérification\n"
124
+ "- Vérifier que chaque directive contient **tous les champs**.\n"
125
+ "- Si une information manque au niveau de la date limite, préciser 'Meilleurs delais'.\n"
126
+ "- Garantir que la structure est prête à être utilisée sans retravail manuel."
127
+ ],
128
+ tools=[ReasoningTools(add_instructions=True)],
129
+ storage=storage,
130
+ add_history_to_messages=False,
131
+
132
+ )
133
+
134
+
135
+ # Vérifier si la collection Qdrant contient déjà des points
136
+ def is_collection_indexed(vector_db):
137
+ try:
138
+ info = vector_db.client.count(collection_name=vector_db.collection)
139
+ return info.count > 0
140
+ except Exception:
141
+ return False
142
+
143
+ # Charger la base de connaissance seulement si elle n'est pas déjà indexée
144
+ if not is_collection_indexed(vector_db):
145
+ agent.knowledge.load(recreate=False)
146
+
147
+
148
+ # -------------------------------
149
+ # Fonction principale : appel agent avec agent.run()
150
+ # -------------------------------
151
+ def generate_directives(query: str):
152
+ try:
153
+ prompt = f"Analyse toutes les directives en details en occurence avec la question du user : {query}"
154
+
155
+ result = agent.run(prompt)
156
+ content = getattr(result, "content", None)
157
+
158
+ if hasattr(content, "dict") and callable(content.dict):
159
+ data = content.dict()
160
+ elif isinstance(content, dict):
161
+ data = content
162
+ elif isinstance(content, str):
163
+ return f"Réponse brute :\n\n{content}"
164
+ else:
165
+ return f"Type inattendu pour content : {type(content)}\nValeur : {content}"
166
+
167
+ resume = data.get("resume")
168
+ directives = data.get("directives")
169
+ if not resume or not directives:
170
+ return "Le JSON ne contient pas 'resume' ou 'directives'."
171
+
172
+ # Générer seulement du Markdown
173
+ markdown_output = f"## Résumé\n- **Nombre de directives** : {resume.get('nombre_directives', 'Inconnu')}\n\n"
174
+ markdown_output += f"> {resume.get('contexte', '')}\n\n"
175
+ markdown_output += "## Directives détaillées\n"
176
+ for idx, directive in enumerate(directives, start=1):
177
+ markdown_output += f"### Directive {idx}\n"
178
+ for key, value in directive.items():
179
+ if isinstance(value, list):
180
+ value = ", ".join(value)
181
+ markdown_output += f"- **{key}** : {value}\n"
182
+ markdown_output += "\n"
183
+
184
+ return markdown_output
185
+
186
+ except Exception as e:
187
+ return f"Erreur inattendue : {str(e)}"
188
+
189
+
190
+ # --- Convertir Markdown en PDF ---
191
+ def export_to_pdf(markdown_text):
192
+ if not markdown_text:
193
+ return None
194
+ # Convertir Markdown en HTML
195
+ html_content = md.markdown(markdown_text)
196
+ tmp = tempfile.NamedTemporaryFile(delete=False, suffix=".pdf")
197
+ HTML(string=html_content).write_pdf(tmp.name)
198
+ return tmp.name
199
+
200
+
201
+ def export_to_html(markdown_text):
202
+ if not markdown_text:
203
+ return None
204
+ html_content = md.markdown(markdown_text)
205
+ tmp = tempfile.NamedTemporaryFile(delete=False, suffix=".html")
206
+ with open(tmp.name, "w", encoding="utf-8") as f:
207
+ f.write(f"<html><body>{html_content}</body></html>")
208
+ return tmp.name
209
+
210
+ # -------------------------------
211
+ # Interface Gradio avec deux onglets
212
+ # -------------------------------
213
+ with gr.Blocks() as demo:
214
+ gr.Markdown("# 🏛️ Assistant Suivi des Directives")
215
+
216
+ # Onglet Chat
217
+ with gr.Tab("Chat"):
218
+ with gr.Row():
219
+ query_input = gr.Textbox(
220
+ label="Votre requête",
221
+ placeholder="Ex: Détails spécifiques sur des directives individuelles. ",
222
+ lines=3
223
+ )
224
+ submit_btn = gr.Button("Générer Directives")
225
+
226
+ output_md = gr.Markdown(label="Résultat")
227
+
228
+ # Ici on relie directement la fonction
229
+ submit_btn.click(
230
+ fn=generate_directives,
231
+ inputs=query_input,
232
+ outputs=output_md
233
+ )
234
+
235
+ # Onglet Export (toujours à l’intérieur du même Blocks)
236
+ with gr.Tab("Export"):
237
+ gr.Markdown("### Exportez les directives générées")
238
+ export_pdf_btn = gr.Button("Télécharger PDF (.pdf)")
239
+ export_html_btn = gr.Button("Télécharger HTML (.html)")
240
+
241
+ pdf_file = gr.File(label="Fichier PDF", interactive=False)
242
+ html_file = gr.File(label="Fichier HTML", interactive=False)
243
+
244
+ export_pdf_btn.click(fn=export_to_pdf, inputs=output_md, outputs=pdf_file)
245
+ export_html_btn.click(fn=export_to_html, inputs=output_md, outputs=html_file)
246
+
247
+ demo.launch(server_name="0.0.0.0", server_port=7860)
requirements.txt ADDED
@@ -0,0 +1,116 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ agno==1.7.5
2
+ aiofiles==24.1.0
3
+ annotated-types==0.7.0
4
+ anyio==4.9.0
5
+ argon2-cffi==25.1.0
6
+ argon2-cffi-bindings==21.2.0
7
+ beautifulsoup4==4.13.4
8
+ bidict==0.23.1
9
+ Brotli==1.1.0
10
+ bs4==0.0.2
11
+ certifi==2025.7.14
12
+ cffi==1.17.1
13
+ charset-normalizer==3.4.2
14
+ click==8.2.1
15
+ cssselect2==0.8.0
16
+ defusedxml==0.7.1
17
+ deprecation==2.1.0
18
+ distro==1.9.0
19
+ docstring_parser==0.17.0
20
+ fastapi==0.116.1
21
+ ffmpy==0.6.1
22
+ filelock==3.18.0
23
+ fonttools==4.59.0
24
+ fsspec==2025.7.0
25
+ gitdb==4.0.12
26
+ GitPython==3.1.44
27
+ gradio==5.38.2
28
+ gradio_client==1.11.0
29
+ groovy==0.1.2
30
+ grpcio==1.73.1
31
+ h11==0.16.0
32
+ h2==4.2.0
33
+ hf-xet==1.1.5
34
+ hpack==4.1.0
35
+ httpcore==1.0.9
36
+ httptools==0.6.4
37
+ httpx==0.28.1
38
+ huggingface-hub==0.34.1
39
+ hyperframe==6.1.0
40
+ idna==3.10
41
+ Jinja2==3.1.6
42
+ jiter==0.10.0
43
+ lancedb==0.24.1
44
+ lxml==6.0.0
45
+ Markdown==3.8.2
46
+ markdown-it-py==3.0.0
47
+ MarkupSafe==3.0.2
48
+ mdurl==0.1.2
49
+ minio==7.2.16
50
+ numpy==2.3.1
51
+ openai==1.97.1
52
+ orjson==3.11.1
53
+ overrides==7.7.0
54
+ packaging==25.0
55
+ panda==0.3.1
56
+ pandas==2.3.1
57
+ pillow==11.3.0
58
+ portalocker==3.2.0
59
+ protobuf==6.31.1
60
+ pyarrow==21.0.0
61
+ pycparser==2.22
62
+ pycryptodome==3.23.0
63
+ pydantic==2.11.7
64
+ pydantic-settings==2.10.1
65
+ pydantic_core==2.33.2
66
+ pydub==0.25.1
67
+ pydyf==0.11.0
68
+ Pygments==2.19.2
69
+ pylance==0.31.1
70
+ pypdf==5.8.0
71
+ pyphen==0.17.2
72
+ python-dateutil==2.9.0.post0
73
+ python-docx==1.2.0
74
+ python-dotenv==1.1.1
75
+ python-engineio==4.12.2
76
+ python-multipart==0.0.20
77
+ python-socketio==5.13.0
78
+ pytz==2025.2
79
+ PyYAML==6.0.2
80
+ qdrant-client==1.15.0
81
+ redis==6.2.0
82
+ requests==2.32.4
83
+ rich==14.0.0
84
+ ruff==0.12.5
85
+ safehttpx==0.1.6
86
+ semantic-version==2.10.0
87
+ setuptools==80.9.0
88
+ shellingham==1.5.4
89
+ simple-websocket==1.1.0
90
+ six==1.17.0
91
+ smmap==5.0.2
92
+ sniffio==1.3.1
93
+ soupsieve==2.7
94
+ starlette==0.47.2
95
+ tantivy==0.24.0
96
+ tinycss2==1.4.0
97
+ tinyhtml5==2.0.0
98
+ tomli==2.2.1
99
+ tomlkit==0.13.3
100
+ tqdm==4.67.1
101
+ typer==0.16.0
102
+ typing-inspection==0.4.1
103
+ typing_extensions==4.14.1
104
+ tzdata==2025.2
105
+ unicorn==2.1.3
106
+ urllib3==2.5.0
107
+ uvicorn==0.35.0
108
+ uvloop==0.21.0
109
+ watchfiles==1.1.0
110
+ weasyprint==66.0
111
+ webencodings==0.5.1
112
+ websockets==15.0.1
113
+ wsproto==1.2.0
114
+ youtube-search-python==1.6.6
115
+ youtube-transcript-api==0.6.2
116
+ zopfli==0.2.3.post1