momenaca commited on
Commit
579d749
·
1 Parent(s): a925f26

add azure search vectorstore link for presse

Browse files
app.py CHANGED
@@ -4,24 +4,27 @@ import logging
4
  import gradio as gr
5
  from langchain.prompts.chat import ChatPromptTemplate
6
  from huggingface_hub import hf_hub_download, whoami
7
- from app.source.backend.llm_utils import get_llm
8
- from app.source.backend.document_store import pickle_to_document_store
9
- from app.source.backend.get_prompts import get_qa_prompts
10
- from app.source.frontend.utils import (
11
  make_html_source,
12
  make_html_presse_source,
13
  init_env,
14
  )
15
- from app.source.backend.prompt_utils import to_chat_instruction, SpecialTokens
 
 
 
16
 
17
  init_env()
18
 
19
- with open("./app/config.yaml") as f:
20
  config = yaml.full_load(f)
21
 
22
  prompts = {}
23
  for source in config["prompt_naming"]:
24
- with open(f"./app/prompt_{source}.yaml") as f:
25
  prompts[source] = yaml.full_load(f)
26
 
27
  ## Building LLM
@@ -40,8 +43,11 @@ qdrants = {
40
  )
41
  )
42
  for tab in config["prompt_naming"]
 
43
  }
44
 
 
 
45
  ## Load Prompts
46
  print("Loading Prompts")
47
  chat_qa_prompts, chat_reformulation_prompts, chat_summarize_memory_prompts = {}, {}, {}
@@ -51,6 +57,7 @@ for source, prompt in prompts.items():
51
  chat_reformulation_prompts[source] = chat_reformulation_prompt
52
  # chat_summarize_memory_prompts[source] = chat_summarize_memory_prompt
53
 
 
54
  with open("./assets/style.css", "r") as f:
55
  css = f.read()
56
 
@@ -277,18 +284,29 @@ def get_html_sources(buttons, cards):
277
  """
278
 
279
 
280
- def get_sources(outils, question, tab, qdrants=qdrants, config=config):
 
 
281
  k = config["num_document_retrieved"]
282
  min_similarity = config["min_similarity"]
283
  if tab in outils:
284
- sources = qdrants[
285
- config["source_mapping"][tab]
286
- ].similarity_search_with_relevance_scores(
287
- config["query_preprompt"]
288
- + question.replace("<p>", "").replace("</p>\n", ""),
289
- k=k,
290
- # filter=get_qdrant_filters(filters),
 
 
 
 
 
 
 
 
291
  )
 
292
  sources = [(doc, score) for doc, score in sources if score >= min_similarity]
293
 
294
  buttons_ids = list(range(len(sources)))
@@ -323,9 +341,11 @@ def get_sources(outils, question, tab, qdrants=qdrants, config=config):
323
  return "", ""
324
 
325
 
326
- def retrieve_sources(outils, *questions, qdrants=qdrants, config=config):
 
 
327
  results = [
328
- get_sources(outils, question, tab, qdrants, config)
329
  for question, tab in zip(questions, config["tabs"])
330
  ]
331
  formated_sources = [source[0] for source in results]
 
4
  import gradio as gr
5
  from langchain.prompts.chat import ChatPromptTemplate
6
  from huggingface_hub import hf_hub_download, whoami
7
+ from spinoza_project.source.backend.llm_utils import get_llm, get_vectorstore
8
+ from spinoza_project.source.backend.document_store import pickle_to_document_store
9
+ from spinoza_project.source.backend.get_prompts import get_qa_prompts
10
+ from spinoza_project.source.frontend.utils import (
11
  make_html_source,
12
  make_html_presse_source,
13
  init_env,
14
  )
15
+ from spinoza_project.source.backend.prompt_utils import (
16
+ to_chat_instruction,
17
+ SpecialTokens,
18
+ )
19
 
20
  init_env()
21
 
22
+ with open("./spinoza_project/config.yaml") as f:
23
  config = yaml.full_load(f)
24
 
25
  prompts = {}
26
  for source in config["prompt_naming"]:
27
+ with open(f"./spinoza_project/prompt_{source}.yaml") as f:
28
  prompts[source] = yaml.full_load(f)
29
 
30
  ## Building LLM
 
43
  )
44
  )
45
  for tab in config["prompt_naming"]
46
+ if tab != "Presse"
47
  }
48
 
49
+ bdd_presse = get_vectorstore("presse")
50
+
51
  ## Load Prompts
52
  print("Loading Prompts")
53
  chat_qa_prompts, chat_reformulation_prompts, chat_summarize_memory_prompts = {}, {}, {}
 
57
  chat_reformulation_prompts[source] = chat_reformulation_prompt
58
  # chat_summarize_memory_prompts[source] = chat_summarize_memory_prompt
59
 
60
+
61
  with open("./assets/style.css", "r") as f:
62
  css = f.read()
63
 
 
284
  """
285
 
286
 
287
+ def get_sources(
288
+ outils, question, tab, qdrants=qdrants, bdd_presse=bdd_presse, config=config
289
+ ):
290
  k = config["num_document_retrieved"]
291
  min_similarity = config["min_similarity"]
292
  if tab in outils:
293
+ sources = (
294
+ (
295
+ bdd_presse.similarity_search_with_relevance_scores(
296
+ question.replace("<p>", "").replace("</p>\n", ""),
297
+ k=k,
298
+ )
299
+ )
300
+ if tab == "Presse"
301
+ else qdrants[
302
+ config["source_mapping"][tab]
303
+ ].similarity_search_with_relevance_scores(
304
+ config["query_preprompt"]
305
+ + question.replace("<p>", "").replace("</p>\n", ""),
306
+ k=k,
307
+ )
308
  )
309
+
310
  sources = [(doc, score) for doc, score in sources if score >= min_similarity]
311
 
312
  buttons_ids = list(range(len(sources)))
 
341
  return "", ""
342
 
343
 
344
+ def retrieve_sources(
345
+ outils, *questions, qdrants=qdrants, bdd_presse=bdd_presse, config=config
346
+ ):
347
  results = [
348
+ get_sources(outils, question, tab, qdrants, bdd_presse, config)
349
  for question, tab in zip(questions, config["tabs"])
350
  ]
351
  formated_sources = [source[0] for source in results]
poetry.lock CHANGED
@@ -223,6 +223,70 @@ files = [
223
  [package.dependencies]
224
  cryptography = "*"
225
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
226
  [[package]]
227
  name = "certifi"
228
  version = "2024.6.2"
@@ -1419,6 +1483,20 @@ files = [
1419
  {file = "intel_openmp-2021.4.0-py2.py3-none-win_amd64.whl", hash = "sha256:eef4c8bcc8acefd7f5cd3b9384dbf73d59e2c99fc56545712ded913f43c4a94f"},
1420
  ]
1421
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1422
  [[package]]
1423
  name = "itsdangerous"
1424
  version = "2.2.0"
@@ -1998,6 +2076,25 @@ requests = ">=2.0.0,<3"
1998
  [package.extras]
1999
  broker = ["pymsalruntime (>=0.13.2,<0.17)"]
2000
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2001
  [[package]]
2002
  name = "multidict"
2003
  version = "6.0.5"
@@ -4859,4 +4956,4 @@ multidict = ">=4.0"
4859
  [metadata]
4860
  lock-version = "2.0"
4861
  python-versions = "^3.10"
4862
- content-hash = "4b2da1198ef4ee6995118810fcfe20d43f21d1eac07b71d7b71a2bab98b633e0"
 
223
  [package.dependencies]
224
  cryptography = "*"
225
 
226
+ [[package]]
227
+ name = "azure-common"
228
+ version = "1.1.28"
229
+ description = "Microsoft Azure Client Library for Python (Common)"
230
+ optional = false
231
+ python-versions = "*"
232
+ files = [
233
+ {file = "azure-common-1.1.28.zip", hash = "sha256:4ac0cd3214e36b6a1b6a442686722a5d8cc449603aa833f3f0f40bda836704a3"},
234
+ {file = "azure_common-1.1.28-py2.py3-none-any.whl", hash = "sha256:5c12d3dcf4ec20599ca6b0d3e09e86e146353d443e7fcc050c9a19c1f9df20ad"},
235
+ ]
236
+
237
+ [[package]]
238
+ name = "azure-core"
239
+ version = "1.30.2"
240
+ description = "Microsoft Azure Core Library for Python"
241
+ optional = false
242
+ python-versions = ">=3.8"
243
+ files = [
244
+ {file = "azure-core-1.30.2.tar.gz", hash = "sha256:a14dc210efcd608821aa472d9fb8e8d035d29b68993819147bc290a8ac224472"},
245
+ {file = "azure_core-1.30.2-py3-none-any.whl", hash = "sha256:cf019c1ca832e96274ae85abd3d9f752397194d9fea3b41487290562ac8abe4a"},
246
+ ]
247
+
248
+ [package.dependencies]
249
+ requests = ">=2.21.0"
250
+ six = ">=1.11.0"
251
+ typing-extensions = ">=4.6.0"
252
+
253
+ [package.extras]
254
+ aio = ["aiohttp (>=3.0)"]
255
+
256
+ [[package]]
257
+ name = "azure-identity"
258
+ version = "1.17.1"
259
+ description = "Microsoft Azure Identity Library for Python"
260
+ optional = false
261
+ python-versions = ">=3.8"
262
+ files = [
263
+ {file = "azure-identity-1.17.1.tar.gz", hash = "sha256:32ecc67cc73f4bd0595e4f64b1ca65cd05186f4fe6f98ed2ae9f1aa32646efea"},
264
+ {file = "azure_identity-1.17.1-py3-none-any.whl", hash = "sha256:db8d59c183b680e763722bfe8ebc45930e6c57df510620985939f7f3191e0382"},
265
+ ]
266
+
267
+ [package.dependencies]
268
+ azure-core = ">=1.23.0"
269
+ cryptography = ">=2.5"
270
+ msal = ">=1.24.0"
271
+ msal-extensions = ">=0.3.0"
272
+ typing-extensions = ">=4.0.0"
273
+
274
+ [[package]]
275
+ name = "azure-search-documents"
276
+ version = "11.4.0"
277
+ description = "Microsoft Azure Cognitive Search Client Library for Python"
278
+ optional = false
279
+ python-versions = ">=3.7"
280
+ files = [
281
+ {file = "azure-search-documents-11.4.0.tar.gz", hash = "sha256:599f269f106fb51e646ff426a218c21811575598e6a769b23fa4a0127c0f57e0"},
282
+ {file = "azure_search_documents-11.4.0-py3-none-any.whl", hash = "sha256:e435266dc992a3450dc475309c9475f89a4bb0e9dac838140e609d9f1c7608ac"},
283
+ ]
284
+
285
+ [package.dependencies]
286
+ azure-common = ">=1.1,<2.0"
287
+ azure-core = ">=1.28.0,<2.0.0"
288
+ isodate = ">=0.6.0"
289
+
290
  [[package]]
291
  name = "certifi"
292
  version = "2024.6.2"
 
1483
  {file = "intel_openmp-2021.4.0-py2.py3-none-win_amd64.whl", hash = "sha256:eef4c8bcc8acefd7f5cd3b9384dbf73d59e2c99fc56545712ded913f43c4a94f"},
1484
  ]
1485
 
1486
+ [[package]]
1487
+ name = "isodate"
1488
+ version = "0.6.1"
1489
+ description = "An ISO 8601 date/time/duration parser and formatter"
1490
+ optional = false
1491
+ python-versions = "*"
1492
+ files = [
1493
+ {file = "isodate-0.6.1-py2.py3-none-any.whl", hash = "sha256:0751eece944162659049d35f4f549ed815792b38793f07cf73381c1c87cbed96"},
1494
+ {file = "isodate-0.6.1.tar.gz", hash = "sha256:48c5881de7e8b0a0d648cb024c8062dc84e7b840ed81e864c7614fd3c127bde9"},
1495
+ ]
1496
+
1497
+ [package.dependencies]
1498
+ six = "*"
1499
+
1500
  [[package]]
1501
  name = "itsdangerous"
1502
  version = "2.2.0"
 
2076
  [package.extras]
2077
  broker = ["pymsalruntime (>=0.13.2,<0.17)"]
2078
 
2079
+ [[package]]
2080
+ name = "msal-extensions"
2081
+ version = "1.1.0"
2082
+ description = "Microsoft Authentication Library extensions (MSAL EX) provides a persistence API that can save your data on disk, encrypted on Windows, macOS and Linux. Concurrent data access will be coordinated by a file lock mechanism."
2083
+ optional = false
2084
+ python-versions = ">=3.7"
2085
+ files = [
2086
+ {file = "msal-extensions-1.1.0.tar.gz", hash = "sha256:6ab357867062db7b253d0bd2df6d411c7891a0ee7308d54d1e4317c1d1c54252"},
2087
+ {file = "msal_extensions-1.1.0-py3-none-any.whl", hash = "sha256:01be9711b4c0b1a151450068eeb2c4f0997df3bba085ac299de3a66f585e382f"},
2088
+ ]
2089
+
2090
+ [package.dependencies]
2091
+ msal = ">=0.4.1,<2.0.0"
2092
+ packaging = "*"
2093
+ portalocker = [
2094
+ {version = ">=1.0,<3", markers = "platform_system != \"Windows\""},
2095
+ {version = ">=1.6,<3", markers = "platform_system == \"Windows\""},
2096
+ ]
2097
+
2098
  [[package]]
2099
  name = "multidict"
2100
  version = "6.0.5"
 
4956
  [metadata]
4957
  lock-version = "2.0"
4958
  python-versions = "^3.10"
4959
+ content-hash = "adbf6715ae5e4d0cd93b90c6754be4996023e0ad1296f700b738887a06123a73"
pyproject.toml CHANGED
@@ -1,10 +1,10 @@
1
  [tool.poetry]
2
- name = "spinoza-project"
3
  version = "0.1.0"
4
  description = ""
5
  authors = ["Miguel Omenaca Muro <[email protected]>"]
6
  readme = "README.md"
7
- package-mode = false
8
 
9
  [tool.poetry.dependencies]
10
  python = "^3.10"
@@ -18,6 +18,8 @@ loadenv = "^0.1.1"
18
  datasets = "^2.20.0"
19
  langchain-community = "^0.2.5"
20
  transformers = "4.39.0"
 
 
21
 
22
 
23
  [build-system]
 
1
  [tool.poetry]
2
+ name = "spinoza_project"
3
  version = "0.1.0"
4
  description = ""
5
  authors = ["Miguel Omenaca Muro <[email protected]>"]
6
  readme = "README.md"
7
+ package-mode = true
8
 
9
  [tool.poetry.dependencies]
10
  python = "^3.10"
 
18
  datasets = "^2.20.0"
19
  langchain-community = "^0.2.5"
20
  transformers = "4.39.0"
21
+ azure-search-documents = "^11.4.0"
22
+ azure-identity = "^1.17.1"
23
 
24
 
25
  [build-system]
requirements.txt CHANGED
@@ -99,6 +99,18 @@ attrs==23.2.0 ; python_version >= "3.10" and python_version < "4.0" \
99
  authlib==1.3.1 ; python_version >= "3.10" and python_version < "4.0" \
100
  --hash=sha256:7ae843f03c06c5c0debd63c9db91f9fda64fa62a42a77419fa15fbb7e7a58917 \
101
  --hash=sha256:d35800b973099bbadc49b42b256ecb80041ad56b7fe1216a362c7943c088f377
 
 
 
 
 
 
 
 
 
 
 
 
102
  certifi==2024.6.2 ; python_version >= "3.10" and python_version < "4.0" \
103
  --hash=sha256:3cd43f1c6fa7dedc5899d69d3ad0398fd018ad1a17fba83ddaf78aa46c747516 \
104
  --hash=sha256:ddc6c8ce995e6987e7faf5e3f1b02b302836a0e5d98ece18392cb1a36c72ad56
@@ -724,6 +736,9 @@ intel-openmp==2021.4.0 ; python_version >= "3.10" and python_version < "4.0" and
724
  --hash=sha256:6e863d8fd3d7e8ef389d52cf97a50fe2afe1a19247e8c0d168ce021546f96fc9 \
725
  --hash=sha256:e2240ab8d01472fed04f3544a878cda5da16c26232b7ea1b59132dbfb48b186e \
726
  --hash=sha256:eef4c8bcc8acefd7f5cd3b9384dbf73d59e2c99fc56545712ded913f43c4a94f
 
 
 
727
  itsdangerous==2.2.0 ; python_version >= "3.10" and python_version < "4.0" \
728
  --hash=sha256:c6242fc49e35958c8b15141343aa660db5fc54d4f13a1db01a3f5891b98700ef \
729
  --hash=sha256:e0050c0b7da1eea53ffaf149c0cfbb5c6e2e2b69c4bef22c81fa6eb73e5f6173
@@ -980,6 +995,9 @@ mkl==2021.4.0 ; python_version >= "3.10" and python_version < "4.0" and platform
980
  mpmath==1.3.0 ; python_version >= "3.10" and python_version < "4.0" \
981
  --hash=sha256:7a28eb2a9774d00c7bc92411c19a89209d5da7c4c9a9e227be8330a23a25b91f \
982
  --hash=sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c
 
 
 
983
  msal==1.28.1 ; python_version >= "3.10" and python_version < "4.0" \
984
  --hash=sha256:563c2d70de77a2ca9786aab84cb4e133a38a6897e6676774edc23d610bfc9e7b \
985
  --hash=sha256:d72bbfe2d5c2f2555f4bc6205be4450ddfd12976610dd9a16a9ab0f05c68b64d
 
99
  authlib==1.3.1 ; python_version >= "3.10" and python_version < "4.0" \
100
  --hash=sha256:7ae843f03c06c5c0debd63c9db91f9fda64fa62a42a77419fa15fbb7e7a58917 \
101
  --hash=sha256:d35800b973099bbadc49b42b256ecb80041ad56b7fe1216a362c7943c088f377
102
+ azure-common==1.1.28 ; python_version >= "3.10" and python_version < "4.0" \
103
+ --hash=sha256:4ac0cd3214e36b6a1b6a442686722a5d8cc449603aa833f3f0f40bda836704a3 \
104
+ --hash=sha256:5c12d3dcf4ec20599ca6b0d3e09e86e146353d443e7fcc050c9a19c1f9df20ad
105
+ azure-core==1.30.2 ; python_version >= "3.10" and python_version < "4.0" \
106
+ --hash=sha256:a14dc210efcd608821aa472d9fb8e8d035d29b68993819147bc290a8ac224472 \
107
+ --hash=sha256:cf019c1ca832e96274ae85abd3d9f752397194d9fea3b41487290562ac8abe4a
108
+ azure-identity==1.17.1 ; python_version >= "3.10" and python_version < "4.0" \
109
+ --hash=sha256:32ecc67cc73f4bd0595e4f64b1ca65cd05186f4fe6f98ed2ae9f1aa32646efea \
110
+ --hash=sha256:db8d59c183b680e763722bfe8ebc45930e6c57df510620985939f7f3191e0382
111
+ azure-search-documents==11.4.0 ; python_version >= "3.10" and python_version < "4.0" \
112
+ --hash=sha256:599f269f106fb51e646ff426a218c21811575598e6a769b23fa4a0127c0f57e0 \
113
+ --hash=sha256:e435266dc992a3450dc475309c9475f89a4bb0e9dac838140e609d9f1c7608ac
114
  certifi==2024.6.2 ; python_version >= "3.10" and python_version < "4.0" \
115
  --hash=sha256:3cd43f1c6fa7dedc5899d69d3ad0398fd018ad1a17fba83ddaf78aa46c747516 \
116
  --hash=sha256:ddc6c8ce995e6987e7faf5e3f1b02b302836a0e5d98ece18392cb1a36c72ad56
 
736
  --hash=sha256:6e863d8fd3d7e8ef389d52cf97a50fe2afe1a19247e8c0d168ce021546f96fc9 \
737
  --hash=sha256:e2240ab8d01472fed04f3544a878cda5da16c26232b7ea1b59132dbfb48b186e \
738
  --hash=sha256:eef4c8bcc8acefd7f5cd3b9384dbf73d59e2c99fc56545712ded913f43c4a94f
739
+ isodate==0.6.1 ; python_version >= "3.10" and python_version < "4.0" \
740
+ --hash=sha256:0751eece944162659049d35f4f549ed815792b38793f07cf73381c1c87cbed96 \
741
+ --hash=sha256:48c5881de7e8b0a0d648cb024c8062dc84e7b840ed81e864c7614fd3c127bde9
742
  itsdangerous==2.2.0 ; python_version >= "3.10" and python_version < "4.0" \
743
  --hash=sha256:c6242fc49e35958c8b15141343aa660db5fc54d4f13a1db01a3f5891b98700ef \
744
  --hash=sha256:e0050c0b7da1eea53ffaf149c0cfbb5c6e2e2b69c4bef22c81fa6eb73e5f6173
 
995
  mpmath==1.3.0 ; python_version >= "3.10" and python_version < "4.0" \
996
  --hash=sha256:7a28eb2a9774d00c7bc92411c19a89209d5da7c4c9a9e227be8330a23a25b91f \
997
  --hash=sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c
998
+ msal-extensions==1.1.0 ; python_version >= "3.10" and python_version < "4.0" \
999
+ --hash=sha256:01be9711b4c0b1a151450068eeb2c4f0997df3bba085ac299de3a66f585e382f \
1000
+ --hash=sha256:6ab357867062db7b253d0bd2df6d411c7891a0ee7308d54d1e4317c1d1c54252
1001
  msal==1.28.1 ; python_version >= "3.10" and python_version < "4.0" \
1002
  --hash=sha256:563c2d70de77a2ca9786aab84cb4e133a38a6897e6676774edc23d610bfc9e7b \
1003
  --hash=sha256:d72bbfe2d5c2f2555f4bc6205be4450ddfd12976610dd9a16a9ab0f05c68b64d
spinoza_project/config.yaml ADDED
@@ -0,0 +1,44 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ demo_name: Spinoza Q&A
2
+ tabs:
3
+ GIEC et IPBES: "*Outil dédié aux rapports du GIEC et de l'IPBES.*"
4
+ Textes Juridiques: "*Outil dédié aux codes Français modifiés par la loi climat (21/73).*"
5
+ Documents Stratégiques: "*Outil dédié aux données centrées sur le plan politique (SNBC).*"
6
+ ADEME:
7
+ "*Outil dédié aux données issues de l'ADEME et nous avons sélectionnés notamment différentes catégories de rapports:*\n
8
+ * *Les guides mis à disposition de la population*\n
9
+ * *Les rapport d'expériences sur des nouvelles technologies*\n
10
+ * *Des études et recherches sur des impacts locaux*\n
11
+ * *Des documents institutionnels (analyses demandées par la France & rapports d'activité)*\n
12
+ * *Les plans de transition sectoriels pour les secteurs industriels les plus émetteurs : (verre, papier, ciment, acier, aluminium, chimie, sucre)*"
13
+ Presse: "*Outil dédié aux données fournies par Aday concernant la presse.*"
14
+
15
+ logo_rsf: "data:image/png;base64,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"
16
+
17
+ logo_ap: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQoAAAC+CAMAAAD6ObEsAAAAolBMVEX///8xL014x8kuLEuAysxvxMYdGkAvLUwrKUkoJkf6+vslIkXj8vMfHEEbGD8hHkIYFD1VVGrz8/XFxMvl5efW1tvy+vqSkZ2O0NE5OFS8u8IPCjkVETwMBjhEQlx6eYmamaSEg5K1tL2trLTR0dYAADNOTGXr6+7d3eFXVmuIh5ViYXVvbn82NFJqaXufn6kAACsAADDR6+sAACcAACFHRV3RZPTPAAAX0UlEQVR4nO1daYOiuhLFywwkMCyCXp8CokILikvPvc///9deqrIAbt120z098zifxISQHLJUqiqF9u2v+/im/SF4uaE9FT0VPRU9FT0VDfRUKPRUKPRUKPRUKPRUKDxGxfcr+HOouNa6W1R8/+fvHxf4+1c3oStca9s/329R8eNXV/ez8aOnQqKnQqGnQqGnQqGnQqGnQqGnQqGnQqGnQqGnQqGnQqGnQqGnQuGzqQiOu11mfUjR70XnVFgTjuUIL49bvNqOeWruOpQapOyo9p2ieypMG/HEG7/28CqZ4VUR6wMGYn7FfvFWKiyBy4SpCa0d+JyKIcErh1MxsfFqYGTdt+TdeCMV44S/e2d4nnKXig2/GtD0I9ryTrSp+E8Dd6lIKW+TbHGNu1QcvjQVzda/+q7AF0xcNuouFUdDELjqqv6/HEdPUjHwgnbSXSqsKfYme/3ZFf44TPWaimM76S4VWjGMDCeqzuj7jRGq8TEY6NN22n0q2IS7Wn1JqeKNqEhNxcAPW2kvUfFnoYwaTAxI1Up8CxVWMM7L4kWxywpGxa2hhWk3SwiK0e3E92ArBCU+YZj+qFWne1QE2QrRkrDydBr5fhxNU3bLbMHBu9pI5Ie7V4d9HEf+adJ6HKKcbGgUx/H+kF1SZc12UzeKI7o5Xt75Tlhi+dDXnAt720q9R8XoyUE816+orCLKy9FptCkmMYpu8QQTZzy/P9dme5dAwaYu0xTyKrL5U0ziOcs2GdZx4FIdq0S8eNfxNJU5vObDsS8q0Ox796kQckWi7jhGzXmHkI2gV1BhiEctonrNGri75vN2rRIGtt6cu8ZTx6zTTBp3K/GfeK2cTJvzX0ZTYHqMimM8aEO0uE0Fu25lMpbqccG6ncRKiOrqhD45Sz3vUu9CLmrPZCshaemnt1IxiwbXcU7FeYNy+bQ5vZIq5+jcNy8TO+wXO/5wynppIR7l53XyI1QUhqqqSUhjCLxEBZHi6u68T2BZXoGJgSmLpJ5ny99RZ/NFIToFNv9Q0/IWKlRDiL/fVNNGb76kgri+66oMPm/PTI4v3YsJjWVx9ICpqbgm9i5bLU6Cd7LpigqhcuBCphQ7jeItVIxi0Sm8zRj+GA/V1uaCCqfKgyCvZAaPd/O5nFrmqyIIyonsZTEwVYo+aw/5Ipq5/Dpu9OF3gS9MA2+BV3sx4S/eQsVCvDVPzYKpbOo5FZ5YsOWLpnhLGJ8VELridsguxJ+6Fxz50tfsw+/BTDzM4EzLPrJ/CxViKSKNfeqaXKVCZbFkn8chILQmDXl34bFZx/YN+IfvGU2n7rHigV43VIimyaeX4r24SrB+PRWB6AJuQxDI/atUGKp42XrUnokNsltPhIEf7atjDmLWiFfNnGcKQnMUn6ub3gS5/TBk9YWKru6Fr6ei5B3M9JsPsK9R0XizmVdTEfDK6E3tR1hKkUUOFtNTIOfEvgdKkTcqEMFR1N2Xb+b1VAhhtSWVSJXfGRWmSp85NRXivd/QDq6cwQ00Zra3o1bk+RGHXL9sOXM9TEV7cdvSK1To85oKo6ZCjE77ugB5mwq6vXrDY2go8i6oDh6lovTP26kpAe6MinoINKko+AChS+0a5Dyj2+eIr9/wGKZNibANsdI/QIW8iJu7OWq+QEVjgFhccGiuQNpsJqcVMQPrw+PiHB3MFU1F3jnky31gMRWD6+UV5EavEBKWadR6CGvgu/NdNg5gV3B+b5eozvd5TQjF3gNUiOKa8+b86s70FhVLTqaQswFMrhjo1IvmdVkNhWOZd6VfHt3aR/LaHR6lQig+BrZaAg5qFn4VFVLadOTgX4nyUdqU4t9cDsBgGtHhdjbqQLUnFXkkakE83nRHD1IRiJV/4A1z9ocVTtWO+3VU1J1oDXuQfCcLxD3ISOxB6JBPH+WJwtbMSdrK2LdACb1rK2jAClvj+5Gd6UQuSMQfrNe6W0/Kr6QiVDtTxyCeK5m0+S5D7Uy9XZZNhnJfG71/Oyb784UZXKwrpm49SIWlFArsZr2pZnklFfWIGjT1drbQV5CmvkLOc3Y9s7wZJ9kbz+ceucN0Vw9SoeWv02LdpsI6XdFiRXKivKbFIvv3T51SkXcp547ECMEF/jHd5ixpSyr61Z3pbSq04HSh24xrseFSt0lJBxYAIQe2tHcC0lcAtnwParxDu9kUb767tge5Q4VmpS2Nt+ntm/Vra7wHuj8stHejeHYNgD+4TAt/YpIRsWFoUR9/P/ManWK8+intILyQf+v1LEh9D8e0Tg13oW0TzJDwbcLspw+Iarus/Kchi+TDyEYbCSvCpZP2UmktbJfbWUyd+oNOnBlGocC1zb5MCxu/+ZDMxRV/GVYjn0KQHfZuZE/RtDUWGfhGt8g56mde/sNQLioSxZFz2s2ueETN0mkcgV1tGV4kfkFc8+p6sIA7JlPNKkYvm2R79OjRo0ePHr8E1927H8nwx2DpEJLc29fMKaHzO+l/Dpb2wIzvUkEG5P+GioF/lwr9zL7xx6KnQqGnQqGnQisW1XydhmgGkFSMjmm1W5xpMRpUWKv0dNqttBF4ODRcwuBypuXb9bySDrplGIazQNPy44SrFsaLXZU2nHDLRVpttkphF2TLqlpmtWKqyFhdJl05GN3DIvaITqi/AbUbp8LaRR5bOO142OokNRUrx6C6To39Mnacp1rtaMWG4w53kc1KtCOutNomrvtcBsPIi0CrXgxjm5XtRTuhIK8idk1sl6xEdRybXTqxsIxb7BFYl1PnHsznWEqTKQFtIlIR7JWTndN0AlRUZInQLeqgGm04eVhU/glwUSMP9iwjp+xPZwU+A0pfP0WVGGh0ic1S9QSYmsRoJjcHpoNGnmDuiWeRK/rXTpGByls3fGF5QSrAg5T4CbhZ6LShVZdUoAuE6fk+Z+ycCu6DiMQuBBUmWrxYxsBnP2icxCwjBT1v5rIf1TH1TcbFSCsS8BNcTEx2O1IzFHUBs6nXgTb3NizQHdN5WAp7HFABZ8HoeqwFR7fhbdKgAvyTdDsrxxN0TLygwqvyMhwCT0+BsHKyhnruc4hOnd6ONRl++Bk6iaKLZP4z2R9ytI9CHax94lRHNhJ9qEvO69KVF951gG+e0LljlaEa9ZRwZPInrXcdIgHMlibBN4Ra+3MqhE0FTOpOJg2+9v6Y50GQqAaxZLC2M1pNugJVMD4H3F7ollVixHvAmkibPJio3I88kwWrhivGIJjlGBXg9qJv+Llr2rKQCCqg/fJwGbT3nAqXkzdyOStIhWg/eJWRFMsGM4tRoB+Y6fiDNMNZEe2l1PeqIz42gHFT18X9SO02mITkcQeoFaMCHUO4gw9OirWVQVAB/nTyJCr8PqNCOVxSblrDuYLW2dkkCcC5pNTKJ5wVTbak4Dk84elOPB+sQDgr1XVxPvJ8M6OCG4hrKtAfkLoST/XjBRXQiaXvHriJnVMhLbnExOxAhXSfQ9c/T5X9zAjNPVc4rdswZwRDeSRGT8b8tTTqsvhAKqD5rliwwZLMqBizN0EnuURYL+eCCmi+s6rvP6NC9gBwtABaJo0xCLfaWV02jv3Zcp7E0F3QUq3lk40fG7DQVOgHQ5fX6tI9wKdATHMrl0+b4HEhfA2tvKWpEVRArxXzanlt2nT4PLIVvQGokOMJnGWFV2FRi7LsISX0F1GRAKx27FnmwAJ3c2lb7czP6AZgSh84y4KtVrFcTOFNO2DpZYJgZO4uVhB0i7E3pWbNnKuLaXwMtGIJLD2VgoqyLoIf2C6mcTTdasFssk6Q1rUOc2u+ODhP8O5h8WJUAJ/o924dkpgcPpSNJfRMO47EsQugAr2zbHO5A7HLaHiGSirwGBDxo9i5IWJ5ojx0lGlREQL17nxbwbGxaKVZ/9rEpOtsBXIG606Vz1ZPslhNmLwJi3wBZxxsstwZrGTvg8//z5X9X5fS5uoJ53SYveymg7ISOFK/ec85Faa0+FNcm1pUaEtcE7BsA4nycb3wQPI2Am2Mj7Y9xsQAI4WEsMKYFOWV6QcrVq0h3xXY9tqmFDsne+04qZsk3jSfPrWpzT0Btgm2mcZDj9LkbNpc73HboLsn7M8Tg5CknhgmT8IZMeHvOE24g6vueZApTMR6QsVyEboeD5oSrz/+0PusSuLIWQar5Xa7FA5Pi33kx0nVFmkm2+1WuF6P0yiKo8N4DPfUQpiQKyYkipO5WIWz6nCoGru6cuvEfhynkp18aUcxmzgW4ujZgq0nLH0n04PF9EpdPgxXjkIX5Qubn+JKOlIBq09w9+aiPDtCW7bdLouyvWoGL9XlC0JR0aOnQsHyCKE9FQBrPRyuP1Sr0KNHj6+IUXhfwrM+IepR/hkWkDNkaZruWrLM5MlPbh/JCiZmEvnVx24MgnkUzT89zNTSFXsQqwRYWsn2Rrp+q6ljA/ZMjdPaH1MptnP2uozU8bqnSkPh7JntCfbiqHpyQ+YNHNwl3TgE+S5ksLERPQH0yOT9J4AehKIC7VkZHPnUBzfdbBao6LCfOjjTeI7UpbZ8A8d4YMbH+/m7h6KiAtsEvJVQ99a31IlwEMI7Wh/hZY0KV9kZU8f7gI73AiQVFjVVv7/dUFA+ftA80aLic1Fmk2NoKSpAjcv1snhYX4MtN4OlBavFYsbHcBGgSkukQ5Ywy8KL6gd5KEuxZtvtkast8sX22PBXsMLFAhwTMDKAZhUBGGe8MV6xS3y0rOkqW6k7A/70IltA7bvAeJg4tue62ZbbQUBDz22HVhJFCWj0sucoeg4XsWHbho+eAU8Y0kqPo+i/wM34kLiO5yYVlwGK5yT5WWmjKvH/1bT/slLWIdzsJQdLy/cQMCxSPgJHx7dtJ1qP2UOehtrqOUIFqR9FP1MNLqNnrgSyjoPY8JyYCiXP7imK/i1TVnvbp10odmaJPNklnQqWVJjELN3kW240aklPg4gtbUF9ICoCC3Ei/AgIPwgUOGyIpWP419Y00GZO5UHc0+xJhD0RdvFKnlFDw1nVDAoEZoKZMrqUe0MccnR4cGiMNbHnTzY7mL5Rrzqg4NEgNd5r1/OsSypA14mZkrEWuEJBSenPAH0iwPyHXG4FFaSCkDkwnYiTevxQpO7IY3UUl8gdcKTbtoqrMhMh51jR7rJBRYFZqA2aUIIHLUXYDV6a6b9bDjuBg4CRHicmFVRYx+NRhFdtU+FNN5jJ3mpButzDKwH51MLDlMbwuFhDtiTnVAwYbyb1poIKQjdzQaizHoIe2IxZe3IwBtjTyfHgCirydAlmOsKKTrMGFRXoxL30uARtOdLIqXDmG64/f+8JMjhcaRKYiYKh8rqRaFOBRi94vAooIExk4G7h4tI/cbgSK+An/mxymGScCjJk/SzDvg+K7RF0KrCLgz2dW9lDsHhwYQpXEPGSJRXAtz5FnyAwK8FhY6TCCUXV3x0kGaUpzmdh3KWC1xImCR49S1FROLUTCMiGSSCocMU5QKAiKsQ9wh0BvCUcTpM5qKvSoKJoU4E+c3xSBusknNOEbPy85rgLPxRYt6SP0YHco0IY9E8gTlhNKiCki3R8APMwqzBSoUTyurloq8cpLzQwTggEtpGRWeD3bSoqUseBYHUAu3SqvD/gBZH3HsVmzTdlgPolvUeFaO36ggow/EvzMDgksIxIRSSljFgdr8UnFDIjowJEGOlwYEX3qGCjUOmPGS0wSCFbjNkC2gEVUJz09qru9grjFhUQ3UaGpkUvFN4r6kjHsQrXckEFePjIQQ7l3KaCvTNVIns0ZOyYChizwpkIA/bcoMK5Q0VQOyHgZCDmino7eYcKDdZwERlgSZtUyIpIKtAVixMO98q5okMqMDoYtnI01d9GBUZixBAkVipmPhSx1DR2jwq4g26Ai8wfSCqkZ4ZV1FSAA4xJYWYYg2QRjTunQtvAiuQP04qH3HjDAOEOU/Zgt0PfVbAUv56KMkFp6pDOjYGiAvUk3nwIMWKUXAFBLUxW0Q3EbMBwFV1TMQKfUiYAkVravELF3V7BA9CaFC3i0UJ7hAruVMCerw9qKgIUfXVCpg0qAhSxCYpTFMNVdE2FlhtCfvX25tkpIYvJyooKuYKw6YtT0TgydIzFHoRyLUtgNKmITEkFGw6moMKXy3MqPJ3xZXBhXFuJABish8GWhAs+xUnsQXTjhGUAFZGgQm9GTHozitT2HSPez7YeIVGLCkoJBdEy8wmJORUn9hdSMWU/1CJRVoZvOL4hnAcKlt9WVCTsQlABT+BUxIS4XFKZnXzDM9yqZH/ZYq6dTdke1PDXjAr2r/zExHEP3vTxXii1oDSu7ArAQb4DKkDXsAItAMb9aSeIfzDFav1V/+Ao8tUqL85vPLuofzVKZETOMgiH1rqpXGWz0XlGrQyzWXlZ7nllevTo0aNHjx49virKGcPqvq2lXL2cR2AMOX9TMejkO87PF8z561fkEdgmLGsnYcc/HbBhekmuB10efaXFv3ks5DcDKBNfcrUH3evglZ3+96Ui/9ePX/r+SP7sx8lrW/f7UjEal+VLJ7WKV+RR+D2osMZhfukPcLnrDUZnHvhXd8YyMpA1Gjf9+dtUBNfODfxyjFISu76/GW/m87n8LMZyGkfOesFbOjoNh+tjsIQvoq2V9X60nDqRcRJ5Cjj1sdC043CPCp1iOYj9aLC0NkN2s9aiIlisvSiepl+tj8xi1EmaxB7oOuXapgm3ZhNHR+NTmRBCqylqHEkkTrxMfDzOo3vctWEUE+Itg7lBUM0XupjbtKd7QoihNakI+YcsdDv6dM+7u8gjk9vquZoVqYCwAVyhquMnevA7FUSeCOOTKcZ64HkwbjREpSXpUJw45qVC6GZuR9YaVMzAS0WH02Cm/6W4ACcI3dtNUlQlIxWrCELuV8uNYXKVLv9kh+66GB8YrVn4FSh3s6xcUE1bIkCvDm0HQYMfr9NPIkZvk4oAoht482UK/hIdRN/uDDMfvFBhGSjQq2ENHxvQ2XCBOkIEBXBa5L1ingcjcHKA45/8qClGqvW4eU3EKiaGS3bce8BjAnmwdc+pAI8v9EktwN73hb6MCxUTFo5S2CtAzPS4jnnHbbZIBTq6WHsTXY7AVmVzxTNrItzFqbDXYcBWEPS4mKkHtKjwlVkxNwbmL3LEuwZwJJCRCqb8LS0wCgliCuM94OZ+LoPvuBkGDCMyD5BjcSpk9Gm7dnjGr1g0qEBT2pTfan5wGIbH0AzacOJULCk3TTHopqmzhgMVIga3MMPgh2ZUHjMacSpiLp7CB0GJNIPQNhUYe8DEOwm780PDMDyGLcbkwZ8Qzl3Fm5gqeIKKBWZqUNHIY5dIhbL6N2zrmtGmAr81VN9JPt1n+SZCjDoAUhI6NAEVaCDNNRlQUqwg4kN1ggow3zlhK4/RcIeCSTUWIb/dNhUQ14duta8YrhJqTcg2mwyoWEwxZgSfGY7bFRyOvaQCvm4pHCgykadJBTSb20wDdFVoTpvoj4U0lbvsUt7/hUBvQXEeXsgVsGJ6uzIYbV3beUqvUYE+FfaB5ZnIPEbD+wq9NuxhNjsScr6YQkwjMg2DYEWoFxtfqW/MIiFoGpKKEoQg6hJwU9Xtq72CB1MQeXBFbFHBQ0EQzxHhvVrS5hocMF3bgw9SRp/8efH/NPDjMrk8RK7hJ8N8Lo3/M/UZBh6h7AoVWljnwS93tKnQNvIDPNjbWlTUsdj0T9iE/Gi2Xvv2l8L3K1SwVxzCJt1yTTnvj0+RzXZgdjTE1bF8NgwRQiFNDOMZB3g+jSnmWWOe0ZNjRA0v0knsUEq96Gj6hptoGn4N4hm3Y1aaYIQ+3+zkEw738eN73fpvL1FRiEANGJ1Lvtfx5LBJJ2IXHdRfcYBvOsgPM4TLaqMiHgar2WzV3FEEWXrYHQtNfiOirD8koRXH3eawvfKBh+7xABXBxPDM0GK1A1/L+OsIfx3h9VQEEIVJN8y5DTMcHV4t7nfGA70iRLcn/lEom3ylZb4bPDJX5FMfVFYQKvTw5zHx2LSpzXZT255Wk84+Nf6V8BgVfzR6KhR6KhR6KhR6KhR6KhR6KhR6KhR6KhR6KhR6KhR6KhRuU/HP31fwq6vbFa617Z9bVPz1/Qq+/eomdIVv11r31y0qruHPoeLFhvZU9FT0VPRU9FQ00FOh0FOh0FOh0FOh0FOh8GJD/wfTvkokTVDvpwAAAABJRU5ErkJggg=="
18
+
19
+ source_mapping:
20
+ GIEC et IPBES: "Science"
21
+ Textes Juridiques: "Loi"
22
+ Documents Stratégiques: "Politique"
23
+ ADEME: "ADEME"
24
+ Presse: "Presse"
25
+
26
+ prompt_naming:
27
+ Science: "Science"
28
+ Loi: "Loi"
29
+ Politique: "Politique"
30
+ ADEME: "ADEME"
31
+ Presse: "Presse"
32
+
33
+ database_index_path: "./app/data/database_tab_placeholder.pickle"
34
+ query_preprompt: "query: "
35
+ passage_preprompt: "passage: "
36
+ embedding_model: "intfloat/multilingual-e5-base"
37
+ num_document_retrieved: 5
38
+ min_similarity: 0.05
39
+
40
+ ## Chat API
41
+ user_token: "user"
42
+ assistant_token: "assistant"
43
+ system_token: "system"
44
+ stop_token: "" ## useless in chat mode
spinoza_project/source/backend/llm_utils.py ADDED
@@ -0,0 +1,83 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from langchain_openai import AzureChatOpenAI
2
+ from msal import ConfidentialClientApplication
3
+ from langchain_openai import AzureOpenAIEmbeddings
4
+ from langchain.vectorstores.azuresearch import AzureSearch
5
+ import os
6
+
7
+
8
+ class LLM:
9
+ def __init__(self, llm):
10
+ self.llm = llm
11
+ self.callbacks = []
12
+
13
+ def stream(self, prompt, prompt_arguments):
14
+ self.llm.streaming = True
15
+ streamed_content = self.llm.stream(prompt.format_messages(**prompt_arguments))
16
+ output = ""
17
+ for op in streamed_content:
18
+ output += op.content
19
+ yield output
20
+
21
+ def get_prediction(self, prompt, prompt_arguments):
22
+ self.llm.callbacks = self.callbacks
23
+ return self.llm.predict_messages(
24
+ prompt.format_messages(**prompt_arguments)
25
+ ).content
26
+
27
+ async def get_aprediction(self, prompt, prompt_arguments):
28
+ self.llm.callbacks = self.callbacks
29
+ prediction = await self.llm.apredict_messages(
30
+ prompt.format_messages(**prompt_arguments)
31
+ )
32
+ return prediction
33
+
34
+ async def get_apredictions(self, prompts, prompts_arguments):
35
+ self.llm.callbacks = self.callbacks
36
+ predictions = []
37
+ for prompt_, prompt_args_ in zip(prompts.keys(), prompts_arguments):
38
+ prediction = await self.llm.apredict_messages(
39
+ prompts[prompt_].format_messages(**prompt_args_)
40
+ )
41
+ predictions.append(prediction.content)
42
+ return predictions
43
+
44
+
45
+ def get_token() -> str | None:
46
+ app = ConfidentialClientApplication(
47
+ client_id=os.getenv("CLIENT_ID"),
48
+ client_credential=os.getenv("CLIENT_SECRET"),
49
+ authority=f"https://login.microsoftonline.com/{os.getenv('TENANT_ID')}",
50
+ )
51
+ result = app.acquire_token_for_client(scopes=[os.getenv("SCOPE")])
52
+ if result is not None:
53
+ return result["access_token"]
54
+
55
+
56
+ def get_llm():
57
+ os.environ["OPENAI_API_KEY"] = get_token()
58
+ os.environ["AZURE_OPENAI_ENDPOINT"] = (
59
+ f"{os.getenv('OPENAI_API_ENDPOINT')}{os.getenv('DEPLOYMENT_ID')}/chat/completions?api-version={os.getenv('OPENAI_API_VERSION')}"
60
+ )
61
+
62
+ return LLM(AzureChatOpenAI())
63
+
64
+
65
+ def get_vectorstore(index_name, model="text-embedding-ada-002"):
66
+ os.environ["AZURE_OPENAI_ENDPOINT"] = (
67
+ f"{os.getenv('OPENAI_API_ENDPOINT')}{os.getenv('DEPLOYMENT_EMB_ID')}/embeddings?api-version={os.getenv('OPENAI_API_VERSION')}"
68
+ )
69
+ os.environ["AZURE_OPENAI_API_KEY"] = get_token()
70
+
71
+ aoai_embeddings = AzureOpenAIEmbeddings(
72
+ azure_deployment=model,
73
+ openai_api_version=os.getenv("OPENAI_API_VERSION"),
74
+ )
75
+
76
+ vector_store: AzureSearch = AzureSearch(
77
+ azure_search_endpoint=os.getenv("VECTOR_STORE_ADDRESS"),
78
+ azure_search_key=os.getenv("VECTOR_STORE_PASSWORD"),
79
+ index_name=index_name,
80
+ embedding_function=aoai_embeddings.embed_query,
81
+ )
82
+
83
+ return vector_store