bertrend 0.3.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (181) hide show
  1. bertrend/BERTopicModel.py +320 -0
  2. bertrend/BERTrend.py +1211 -0
  3. bertrend/__init__.py +59 -0
  4. bertrend/config/bertopic_default_config.toml +48 -0
  5. bertrend/config/bertrend_default_config.toml +16 -0
  6. bertrend/config/parameters.py +92 -0
  7. bertrend/config/services_default_config.toml +32 -0
  8. bertrend/demos/__init__.py +4 -0
  9. bertrend/demos/demos_utils/__init__.py +13 -0
  10. bertrend/demos/demos_utils/data_loading_component.py +267 -0
  11. bertrend/demos/demos_utils/embed_documents_component.py +52 -0
  12. bertrend/demos/demos_utils/icons.py +41 -0
  13. bertrend/demos/demos_utils/messages.py +11 -0
  14. bertrend/demos/demos_utils/parameters_component.py +162 -0
  15. bertrend/demos/demos_utils/state_utils.py +99 -0
  16. bertrend/demos/start_summarizer_app.sh +9 -0
  17. bertrend/demos/start_topic_analysis_app.sh +9 -0
  18. bertrend/demos/start_weak_signals_app.sh +9 -0
  19. bertrend/demos/summarization/__init__.py +4 -0
  20. bertrend/demos/summarization/summarizer_app.py +98 -0
  21. bertrend/demos/topic_analysis/__init__.py +4 -0
  22. bertrend/demos/topic_analysis/app.py +87 -0
  23. bertrend/demos/topic_analysis/app_utils.py +69 -0
  24. bertrend/demos/topic_analysis/demo_pages/__init__.py +4 -0
  25. bertrend/demos/topic_analysis/demo_pages/explore_topics.py +455 -0
  26. bertrend/demos/topic_analysis/demo_pages/newsletters_generation.py +178 -0
  27. bertrend/demos/topic_analysis/demo_pages/temporal_visualizations.py +573 -0
  28. bertrend/demos/topic_analysis/demo_pages/topic_visualizations.py +280 -0
  29. bertrend/demos/topic_analysis/demo_pages/training_page.py +219 -0
  30. bertrend/demos/topic_analysis/messages.py +13 -0
  31. bertrend/demos/weak_signals/__init__.py +4 -0
  32. bertrend/demos/weak_signals/app.py +510 -0
  33. bertrend/demos/weak_signals/messages.py +32 -0
  34. bertrend/demos/weak_signals/visualizations_utils.py +403 -0
  35. bertrend/llm_utils/__init__.py +4 -0
  36. bertrend/llm_utils/newsletter.css +131 -0
  37. bertrend/llm_utils/newsletter_features.py +272 -0
  38. bertrend/llm_utils/openai_client.py +235 -0
  39. bertrend/llm_utils/prompts.py +94 -0
  40. bertrend/metrics/__init__.py +4 -0
  41. bertrend/metrics/temporal_metrics_embedding.py +861 -0
  42. bertrend/metrics/topic_metrics.py +199 -0
  43. bertrend/resources/common_ngrams.json +23 -0
  44. bertrend/resources/stopwords-en.json +3 -0
  45. bertrend/resources/stopwords-fr.json +1 -0
  46. bertrend/resources/stopwords-rte.json +15 -0
  47. bertrend/services/__init__.py +4 -0
  48. bertrend/services/authentication.py +66 -0
  49. bertrend/services/embedding_client.py +154 -0
  50. bertrend/services/embedding_service.py +380 -0
  51. bertrend/services/summarizer.py +45 -0
  52. bertrend/services/summary/__init__.py +4 -0
  53. bertrend/services/summary/abstractive_summarizer.py +74 -0
  54. bertrend/services/summary/chatgpt_summarizer.py +67 -0
  55. bertrend/services/summary/extractive_summarizer.py +368 -0
  56. bertrend/services/summary/lexrank.py +126 -0
  57. bertrend/services/summary/prompts.py +43 -0
  58. bertrend/tests/__init__.py +4 -0
  59. bertrend/tests/test_abstractive_summarizer.py +140 -0
  60. bertrend/tests/test_bertrend.py +1091 -0
  61. bertrend/tests/test_cache_utils.py +111 -0
  62. bertrend/tests/test_chatgpt_summarizer.py +150 -0
  63. bertrend/tests/test_config_utils.py +54 -0
  64. bertrend/tests/test_data_loading.py +326 -0
  65. bertrend/tests/test_data_structure.py +53 -0
  66. bertrend/tests/test_embedding_client.py +546 -0
  67. bertrend/tests/test_extractive_summarizer.py +250 -0
  68. bertrend/tests/test_installation.py +183 -0
  69. bertrend/tests/test_newsletter_features.py +354 -0
  70. bertrend/tests/test_openai_client.py +210 -0
  71. bertrend/tests/test_representative_docs.py +125 -0
  72. bertrend/tests/test_summarizer.py +115 -0
  73. bertrend/tests/test_temporal_metrics_embedding.py +349 -0
  74. bertrend/tests/test_topic_description.py +159 -0
  75. bertrend/tests/test_topic_metrics.py +233 -0
  76. bertrend/tests/test_topic_model.py +285 -0
  77. bertrend/tests/test_trend_analysis_data_structure.py +174 -0
  78. bertrend/tests/test_trend_analysis_prompts.py +225 -0
  79. bertrend/tests/test_trend_analysis_visualizations.py +307 -0
  80. bertrend/tests/test_trend_analysis_weak_signals.py +598 -0
  81. bertrend/tests/test_utils_init.py +70 -0
  82. bertrend/tests/test_visualizations.py +306 -0
  83. bertrend/topic_analysis/__init__.py +4 -0
  84. bertrend/topic_analysis/data_structure.py +13 -0
  85. bertrend/topic_analysis/prompts.py +42 -0
  86. bertrend/topic_analysis/representative_docs.py +57 -0
  87. bertrend/topic_analysis/topic_description.py +66 -0
  88. bertrend/topic_analysis/visualizations.py +315 -0
  89. bertrend/trend_analysis/__init__.py +4 -0
  90. bertrend/trend_analysis/data_structure.py +78 -0
  91. bertrend/trend_analysis/prompts.py +229 -0
  92. bertrend/trend_analysis/signal_llm_template_en.html +181 -0
  93. bertrend/trend_analysis/signal_llm_template_fr.html +181 -0
  94. bertrend/trend_analysis/visualizations.py +605 -0
  95. bertrend/trend_analysis/weak_signals.py +425 -0
  96. bertrend/utils/__init__.py +31 -0
  97. bertrend/utils/cache_utils.py +31 -0
  98. bertrend/utils/config_utils.py +41 -0
  99. bertrend/utils/data_loading.py +329 -0
  100. bertrend-0.3.1.dist-info/AUTHORS.txt +15 -0
  101. bertrend-0.3.1.dist-info/LICENSE.md +373 -0
  102. bertrend-0.3.1.dist-info/METADATA +400 -0
  103. bertrend-0.3.1.dist-info/RECORD +181 -0
  104. bertrend-0.3.1.dist-info/WHEEL +4 -0
  105. bertrend_apps/__init__.py +4 -0
  106. bertrend_apps/common/__init__.py +4 -0
  107. bertrend_apps/common/crontab_utils.py +113 -0
  108. bertrend_apps/common/mail_utils.py +91 -0
  109. bertrend_apps/config/feeds/arxiv_feed.toml +15 -0
  110. bertrend_apps/config/feeds/decarbonation_feed.toml +15 -0
  111. bertrend_apps/config/feeds/llm_en_feed.toml +15 -0
  112. bertrend_apps/config/feeds/llm_feed.toml +16 -0
  113. bertrend_apps/config/feeds/nlp_feed.toml +15 -0
  114. bertrend_apps/config/feeds/rte_feed.toml +15 -0
  115. bertrend_apps/config/gmail_credentials.json +1 -0
  116. bertrend_apps/config/newsletters/arxiv_newsletter_from_last.toml +39 -0
  117. bertrend_apps/config/newsletters/arxiv_newsletter_from_scratch.toml +39 -0
  118. bertrend_apps/config/newsletters/decarbonation_newsletter_from_last.toml +38 -0
  119. bertrend_apps/config/newsletters/decarbonation_newsletter_from_scratch.toml +38 -0
  120. bertrend_apps/config/newsletters/llm_en_newsletter_from_last.toml +38 -0
  121. bertrend_apps/config/newsletters/llm_newsletter_from_last.toml +39 -0
  122. bertrend_apps/config/newsletters/nlp_newsletter_from_last.toml +38 -0
  123. bertrend_apps/config/newsletters/rte_newsletter_from_last.toml +38 -0
  124. bertrend_apps/config/newsletters/rte_newsletter_from_scratch.toml +38 -0
  125. bertrend_apps/data_provider/__init__.py +9 -0
  126. bertrend_apps/data_provider/__main__.py +237 -0
  127. bertrend_apps/data_provider/arxiv_provider.py +161 -0
  128. bertrend_apps/data_provider/bing_news_provider.py +82 -0
  129. bertrend_apps/data_provider/curebot_provider.py +105 -0
  130. bertrend_apps/data_provider/data_provider.py +194 -0
  131. bertrend_apps/data_provider/google_news_provider.py +94 -0
  132. bertrend_apps/data_provider/newscatcher_provider.py +74 -0
  133. bertrend_apps/data_provider/utils.py +165 -0
  134. bertrend_apps/exploration/__init__.py +4 -0
  135. bertrend_apps/exploration/curebot/__init__.py +4 -0
  136. bertrend_apps/exploration/curebot/app.py +45 -0
  137. bertrend_apps/exploration/curebot/app_utils.py +317 -0
  138. bertrend_apps/exploration/curebot/config.toml +33 -0
  139. bertrend_apps/exploration/curebot/import_extraction_article.ipynb +108 -0
  140. bertrend_apps/exploration/curebot/old_veille_analyse.py +298 -0
  141. bertrend_apps/exploration/curebot/prompts.py +24 -0
  142. bertrend_apps/exploration/curebot/start_newsletter_generator.sh +8 -0
  143. bertrend_apps/exploration/curebot/tabs/tab1.py +150 -0
  144. bertrend_apps/exploration/curebot/tabs/tab2.py +95 -0
  145. bertrend_apps/exploration/curebot/tabs/tab3.py +125 -0
  146. bertrend_apps/exploration/dataviz/Main_page.py +10 -0
  147. bertrend_apps/exploration/dataviz/__init__.py +4 -0
  148. bertrend_apps/exploration/dataviz/pages/1_Data_feeds.py +105 -0
  149. bertrend_apps/exploration/geolocalization/__init__.py +4 -0
  150. bertrend_apps/exploration/geolocalization/entity_utils.py +118 -0
  151. bertrend_apps/exploration/geolocalization/spacy/__init__.py +4 -0
  152. bertrend_apps/exploration/geolocalization/spacy/common_factory.py +142 -0
  153. bertrend_apps/exploration/geolocalization/spacy/dbpedia.py +206 -0
  154. bertrend_apps/exploration/geolocalization/spacy/nlp.config +37 -0
  155. bertrend_apps/exploration/geolocalization/spacy/utils.py +39 -0
  156. bertrend_apps/install_feeds_and_newsletters.py +29 -0
  157. bertrend_apps/newsletters/__init__.py +4 -0
  158. bertrend_apps/newsletters/__main__.py +277 -0
  159. bertrend_apps/prospective_demo/.streamlit/config.toml +3 -0
  160. bertrend_apps/prospective_demo/.streamlit/secrets.toml +9 -0
  161. bertrend_apps/prospective_demo/__init__.py +70 -0
  162. bertrend_apps/prospective_demo/app.py +111 -0
  163. bertrend_apps/prospective_demo/authentication.py +44 -0
  164. bertrend_apps/prospective_demo/dashboard_analysis.py +132 -0
  165. bertrend_apps/prospective_demo/dashboard_common.py +79 -0
  166. bertrend_apps/prospective_demo/dashboard_signals.py +226 -0
  167. bertrend_apps/prospective_demo/feeds_common.py +38 -0
  168. bertrend_apps/prospective_demo/feeds_config.py +261 -0
  169. bertrend_apps/prospective_demo/feeds_data.py +95 -0
  170. bertrend_apps/prospective_demo/i18n.py +136 -0
  171. bertrend_apps/prospective_demo/llm_utils.py +34 -0
  172. bertrend_apps/prospective_demo/models_info.py +361 -0
  173. bertrend_apps/prospective_demo/perf_utils.py +23 -0
  174. bertrend_apps/prospective_demo/process_new_data.py +348 -0
  175. bertrend_apps/prospective_demo/report_generation.py +214 -0
  176. bertrend_apps/prospective_demo/report_template.html +148 -0
  177. bertrend_apps/prospective_demo/report_utils.py +132 -0
  178. bertrend_apps/prospective_demo/start_demo.sh +9 -0
  179. bertrend_apps/prospective_demo/streamlit_utils.py +37 -0
  180. bertrend_apps/start_apps.sh +15 -0
  181. bertrend_apps/stop_apps.sh +11 -0
bertrend/__init__.py ADDED
@@ -0,0 +1,59 @@
1
+ # Copyright (c) 2024, RTE (https://www.rte-france.com)
2
+ # See AUTHORS.txt
3
+ # SPDX-License-Identifier: MPL-2.0
4
+ # This file is part of BERTrend.
5
+ import os
6
+ from pathlib import Path
7
+
8
+ from bertrend.utils.config_utils import load_toml_config
9
+
10
+ # Default config files path
11
+ BERTOPIC_DEFAULT_CONFIG_PATH = (
12
+ Path(__file__).parent / "config" / "bertopic_default_config.toml"
13
+ )
14
+ BERTREND_DEFAULT_CONFIG_PATH = (
15
+ Path(__file__).parent / "config" / "bertrend_default_config.toml"
16
+ )
17
+ SERVICES_DEFAULT_CONFIG_PATH = (
18
+ Path(__file__).parent / "config" / "services_default_config.toml"
19
+ )
20
+
21
+ # Read config
22
+ BERTOPIC_CONFIG = load_toml_config(BERTOPIC_DEFAULT_CONFIG_PATH)
23
+ BERTREND_CONFIG = load_toml_config(BERTREND_DEFAULT_CONFIG_PATH)
24
+ SERVICES_CONFIG = load_toml_config(SERVICES_DEFAULT_CONFIG_PATH)
25
+
26
+ EMBEDDING_CONFIG = SERVICES_CONFIG["embedding_service"]
27
+ LLM_CONFIG = SERVICES_CONFIG["llm_service"]
28
+
29
+ # Linux command to find the index of the GPU device currently less used than the others
30
+ BEST_CUDA_DEVICE = r"\`nvidia-smi --query-gpu=index,memory.used --format=csv,nounits | tail -n +2 | sort -t',' -k2 -n | head -n 1 | cut -d',' -f1\`"
31
+
32
+ BERTREND_BASE_DIR = os.getenv("BERTREND_BASE_DIR", None)
33
+ BASE_PATH = (
34
+ Path(BERTREND_BASE_DIR)
35
+ if BERTREND_BASE_DIR
36
+ else Path(__file__).parent.parent.parent
37
+ )
38
+
39
+ # Base dirs
40
+ DATA_PATH = BASE_PATH / "data"
41
+ CACHE_PATH = BASE_PATH / "cache"
42
+ OUTPUT_PATH = BASE_PATH / "output"
43
+ CONFIG_PATH = BASE_PATH / "config"
44
+
45
+ FEED_BASE_PATH = DATA_PATH / "feeds"
46
+ BERTREND_LOG_PATH = BASE_PATH / "logs"
47
+
48
+ # Weak signals
49
+ MODELS_DIR = CACHE_PATH / "models"
50
+ ZEROSHOT_TOPICS_DATA_DIR = CACHE_PATH / "zeroshot_topics_data"
51
+ SIGNAL_EVOLUTION_DATA_DIR = CACHE_PATH / "signal_evolution_data"
52
+
53
+ # Create directories if they do not exist
54
+ DATA_PATH.mkdir(parents=True, exist_ok=True)
55
+ OUTPUT_PATH.mkdir(parents=True, exist_ok=True)
56
+ CACHE_PATH.mkdir(parents=True, exist_ok=True)
57
+ CONFIG_PATH.mkdir(parents=True, exist_ok=True)
58
+ BERTREND_LOG_PATH.mkdir(parents=True, exist_ok=True)
59
+ MODELS_DIR.mkdir(parents=True, exist_ok=True)
@@ -0,0 +1,48 @@
1
+ # Default configuration file to be used for topic model
2
+
3
+ # Global parameters
4
+ [global]
5
+ language = "French"
6
+
7
+ # BERTopic parameters: https://maartengr.github.io/BERTopic/api/bertopic.html#bertopic._bertopic.BERTopic.__init__
8
+ [bertopic_model]
9
+ top_n_words = 10
10
+ verbose = true
11
+ representation_model = ["MaximalMarginalRelevance"] # KeyBERTInspired, OpenAI
12
+ zeroshot_topic_list = []
13
+ zeroshot_min_similarity = 0
14
+
15
+ # UMAP parameters: https://umap-learn.readthedocs.io/en/latest/api.html
16
+ [umap_model]
17
+ n_neighbors = 5
18
+ n_components = 5
19
+ min_dist = 0.0
20
+ metric = "cosine"
21
+ random_state = 42
22
+
23
+ # HDBSCAN parameters: https://hdbscan.readthedocs.io/en/latest/api.html
24
+ [hdbscan_model]
25
+ min_cluster_size = 5
26
+ min_samples = 5
27
+ metric = "euclidean"
28
+ cluster_selection_method = "eom"
29
+ prediction_data = true
30
+
31
+ # CountVectorizer: https://scikit-learn.org/1.5/modules/generated/sklearn.feature_extraction.text.CountVectorizer.html
32
+ [vectorizer_model]
33
+ ngram_range = [1, 1]
34
+ stop_words = true # If true, will check `language` parameter and load associated stopwords file
35
+ min_df = 2
36
+
37
+ # ClassTfidfTransformer: https://maartengr.github.io/BERTopic/api/ctfidf.html
38
+ [ctfidf_model]
39
+ bm25_weighting = false
40
+ reduce_frequent_words = true
41
+
42
+ # MaximalMarginalRelevance: https://maartengr.github.io/BERTopic/api/representation/mmr.html
43
+ [mmr_model]
44
+ diversity = 0.3
45
+
46
+ # Reduce outliers: https://maartengr.github.io/BERTopic/api/bertopic.html#bertopic._bertopic.BERTopic.reduce_outliers
47
+ [reduce_outliers]
48
+ strategy = "c-tf-idf"
@@ -0,0 +1,16 @@
1
+ # Copyright (c) 2024, RTE (https://www.rte-france.com)
2
+ # See AUTHORS.txt
3
+ # SPDX-License-Identifier: MPL-2.0
4
+ # This file is part of BERTrend.
5
+
6
+ # BERTrend Hyperparameters
7
+
8
+ # Data split settings
9
+ granularity = 2
10
+ # Merge model settings
11
+ min_similarity = 0.7
12
+ # Signal popularity settings
13
+ decay_factor = 0.01
14
+ decay_power = 2
15
+ signal_classif_lower_bound = 10
16
+ signal_classif_upper_bound = 75
@@ -0,0 +1,92 @@
1
+ # Copyright (c) 2024, RTE (https://www.rte-france.com)
2
+ # See AUTHORS.txt
3
+ # SPDX-License-Identifier: MPL-2.0
4
+ # This file is part of BERTrend.
5
+
6
+ import json
7
+ from pathlib import Path
8
+
9
+ import torch
10
+
11
+ from bertrend import BERTREND_CONFIG, EMBEDDING_CONFIG
12
+
13
+ stopwords_en_file = Path(__file__).parent.parent / "resources" / "stopwords-en.json"
14
+ stopwords_fr_file = Path(__file__).parent.parent / "resources" / "stopwords-fr.json"
15
+ stopwords_rte_file = Path(__file__).parent.parent / "resources" / "stopwords-rte.json"
16
+ common_ngrams_file = Path(__file__).parent.parent / "resources" / "common_ngrams.json"
17
+ with open(stopwords_en_file, "r", encoding="utf-8") as file:
18
+ ENGLISH_STOPWORDS = json.load(file)
19
+ with open(stopwords_fr_file, "r", encoding="utf-8") as file:
20
+ FRENCH_STOPWORDS = json.load(file)
21
+ with open(stopwords_rte_file, "r", encoding="utf-8") as file:
22
+ STOP_WORDS_RTE = json.load(file)
23
+ with open(common_ngrams_file, "r", encoding="utf-8") as file:
24
+ COMMON_NGRAMS = json.load(file)
25
+
26
+ STOPWORDS = STOP_WORDS_RTE + COMMON_NGRAMS + FRENCH_STOPWORDS
27
+
28
+ # File names
29
+ STATE_FILE = "app_state.pkl"
30
+ EMBEDDINGS_FILE = "embeddings.npy"
31
+ BERTREND_FILE = "bertrend.dill"
32
+ DOC_INFO_DF_FILE = "doc_info_df.pkl"
33
+ TOPIC_INFO_DF_FILE = "topic_info_df.pkl"
34
+
35
+ # Model file names
36
+ ZEROSHOT_TOPICS_DATA_FILE = "zeroshot_topics_data.json"
37
+ INDIVIDUAL_MODEL_TOPIC_COUNTS_FILE = "individual_topic_counts.json"
38
+ CUMULATIVE_MERGED_TOPIC_COUNTS_FILE = "cumulative_topic_counts.json"
39
+
40
+ # Embedding models
41
+ ENGLISH_EMBEDDING_MODELS = [
42
+ "all-mpnet-base-v2",
43
+ "Alibaba-NLP/gte-base-en-v1.5",
44
+ "all-MiniLM-L12-v2",
45
+ ]
46
+ FRENCH_EMBEDDING_MODELS = [
47
+ "OrdalieTech/Solon-embeddings-base-0.1",
48
+ "OrdalieTech/Solon-embeddings-large-0.1",
49
+ "dangvantuan/sentence-camembert-large",
50
+ "antoinelouis/biencoder-distilcamembert-mmarcoFR",
51
+ ]
52
+
53
+ # MODEL REPRESENTATIONS
54
+ MMR_REPRESENTATION_MODEL = "MaximalMarginalRelevance"
55
+ KEYBERTINSPIRED_REPRESENTATION_MODEL = "KeyBERTInspired"
56
+ OPENAI_REPRESENTATION_MODEL = "OpenAI"
57
+
58
+ # BERTopic Hyperparameters
59
+ BERTOPIC_SERIALIZATION = "safetensors" # or pickle
60
+ LANGUAGES = ["French", "English"]
61
+ REPRESENTATION_MODELS = [
62
+ MMR_REPRESENTATION_MODEL,
63
+ KEYBERTINSPIRED_REPRESENTATION_MODEL,
64
+ OPENAI_REPRESENTATION_MODEL,
65
+ ]
66
+
67
+ # BERTrend parameters
68
+ SIGNAL_CLASSIF_LOWER_BOUND = BERTREND_CONFIG["signal_classif_lower_bound"]
69
+ SIGNAL_CLASSIF_UPPER_BOUND = BERTREND_CONFIG["signal_classif_upper_bound"]
70
+
71
+ # Embedding Settings
72
+ EMBEDDING_DTYPES = EMBEDDING_CONFIG["embedding_dtypes"]
73
+ EMBEDDING_BATCH_SIZE = EMBEDDING_CONFIG["embedding_batch_size"]
74
+ EMBEDDING_MAX_SEQ_LENGTH = EMBEDDING_CONFIG["embedding_max_seq_length"]
75
+ EMBEDDING_DEVICE = "cuda" if torch.cuda.is_available() else "cpu"
76
+
77
+ # Representation models parameters
78
+ KEYBERT_NR_REPR_DOCS = (5,)
79
+ KEYBERT_NR_CANDIDATE_WORDS = (40,)
80
+ KEYBERT_TOP_N_WORDS = 20
81
+ OPENAI_NR_DOCS = 5
82
+
83
+ # Data Processing
84
+ MIN_CHARS_DEFAULT = 1
85
+ SAMPLE_SIZE_DEFAULT = 1.0 # Or whatever default you want, None means all documents
86
+
87
+ # Time Settings
88
+ DEFAULT_WINDOW_SIZE = 7 # days
89
+ MAX_WINDOW_SIZE = 365 # days
90
+
91
+ # Data Analysis Settings
92
+ POPULARITY_THRESHOLD = 0.1 # for weak signal detection, if applicable
@@ -0,0 +1,32 @@
1
+ # Copyright (c) 2024, RTE (https://www.rte-france.com)
2
+ # See AUTHORS.txt
3
+ # SPDX-License-Identifier: MPL-2.0
4
+ # This file is part of BERTrend.
5
+
6
+ # Embedding service parameters
7
+ [embedding_service]
8
+ # Indicates if BERTrend shall use a local embedding service (run by BERTrend) or if the embedding service
9
+ # is delegated to an external service. If set to false, the other properties shall be set properly.
10
+ # It is generally more efficient to use a dedicated service for embeddings.
11
+ use_local = true
12
+ model_name = "OrdalieTech/Solon-embeddings-large-0.1"
13
+ url = "https://10.132.5.44:6464"
14
+ embedding_dtypes = ["float32", "float16", "bfloat16"]
15
+ embedding_batch_size = 2048
16
+ embedding_max_seq_length = 512
17
+
18
+ # LLM service parameters
19
+ [llm_service]
20
+ # Specifies here the information about the LLM to be used to benefit from some LLM features such as
21
+ # improved topic description and topic analysis over time
22
+ # If empty, these features will not be available
23
+ # BERTrend can use any kind of openai-compatible deployment such as
24
+ # - local LLM deployed with VLLM or similar libraries
25
+ # - the official OpenAI endpoint
26
+ # - models deployed on Azure cloud
27
+ api_key = "$AZURE_SE_WATTELSE_OPENAI_API_KEY_DEV"
28
+ endpoint = "$AZURE_SE_WATTELSE_OPENAI_ENDPOINT_DEV"
29
+ model = "$AZURE_SE_WATTELSE_OPENAI_DEFAULT_MODEL_NAME_DEV"
30
+ temperature = 0.1
31
+ max_output_tokens = 2048
32
+ system_prompt = "You are a helpful assistant, skilled in detailing topic evolution over time for the detection of emerging trends and signals."
@@ -0,0 +1,4 @@
1
+ # Copyright (c) 2024, RTE (https://www.rte-france.com)
2
+ # See AUTHORS.txt
3
+ # SPDX-License-Identifier: MPL-2.0
4
+ # This file is part of BERTrend.
@@ -0,0 +1,13 @@
1
+ # Copyright (c) 2024, RTE (https://www.rte-france.com)
2
+ # See AUTHORS.txt
3
+ # SPDX-License-Identifier: MPL-2.0
4
+ # This file is part of BERTrend.
5
+ import streamlit as st
6
+
7
+
8
+ def is_admin_mode() -> bool:
9
+ """Indicates whether the application is in an admin mode."""
10
+ admin_param = st.query_params.get("admin")
11
+ if not admin_param:
12
+ return False
13
+ return admin_param.lower() == "true"
@@ -0,0 +1,267 @@
1
+ # Copyright (c) 2024, RTE (https://www.rte-france.com)
2
+ # See AUTHORS.txt
3
+ # SPDX-License-Identifier: MPL-2.0
4
+ # This file is part of BERTrend.
5
+
6
+ from pathlib import Path
7
+ from tempfile import TemporaryDirectory
8
+
9
+ import pandas as pd
10
+ import streamlit as st
11
+ from streamlit.runtime.uploaded_file_manager import UploadedFile
12
+
13
+ from bertrend import DATA_PATH
14
+ from bertrend.demos.demos_utils.icons import (
15
+ WARNING_ICON,
16
+ JSON_ICON,
17
+ CSV_ICON,
18
+ PARQUET_ICON,
19
+ XLSX_ICON,
20
+ CLIENT_STORAGE_ICON,
21
+ SERVER_STORAGE_ICON,
22
+ )
23
+ from bertrend.demos.demos_utils.messages import NO_DATA_AFTER_PREPROCESSING_MESSAGE
24
+ from bertrend.demos.demos_utils.state_utils import (
25
+ save_widget_state,
26
+ register_widget,
27
+ SessionStateManager,
28
+ )
29
+ from bertrend.config.parameters import MIN_CHARS_DEFAULT, SAMPLE_SIZE_DEFAULT
30
+ from bertrend.utils.data_loading import (
31
+ find_compatible_files,
32
+ TEXT_COLUMN,
33
+ load_data,
34
+ split_data,
35
+ TIMESTAMP_COLUMN,
36
+ )
37
+
38
+ NO_DATASET_WARNING = "Please select at least one dataset to proceed."
39
+ FORMAT_ICONS = {
40
+ "xlsx": XLSX_ICON,
41
+ "csv": CSV_ICON,
42
+ "parquet": PARQUET_ICON,
43
+ "json": JSON_ICON,
44
+ "jsonl": JSON_ICON,
45
+ "jsonlines": JSON_ICON,
46
+ "jsonl.gz": JSON_ICON,
47
+ }
48
+
49
+
50
+ def _process_uploaded_files(
51
+ files: list[UploadedFile],
52
+ ) -> list[pd.DataFrame]:
53
+ """Read a list of uploaded files and return a list of dataframes containing the associated data"""
54
+ dataframes = []
55
+ with TemporaryDirectory() as tmpdir:
56
+ for f in files:
57
+ with open(tmpdir + "/" + f.name, "wb") as tmp_file:
58
+ tmp_file.write(f.getvalue())
59
+ if tmp_file is not None:
60
+ df = load_data(
61
+ Path(tmp_file.name),
62
+ SessionStateManager.get("language", "French"),
63
+ )
64
+ if df is not None:
65
+ dataframes.append(df)
66
+ return dataframes
67
+
68
+
69
+ def _load_files(
70
+ files: list[Path],
71
+ ) -> list[pd.DataFrame]:
72
+ """Read a list of files from storage and return a list of dataframes containing the associated data"""
73
+ dfs = []
74
+ for selected_file in files:
75
+ file_path = DATA_PATH / selected_file
76
+ df = load_data(
77
+ file_path,
78
+ SessionStateManager.get("language", "French"),
79
+ )
80
+ if df is not None:
81
+ dfs.append(df)
82
+ return dfs
83
+
84
+
85
+ # TODO: if loaded data column names do not match our default values, show a popup for column data mapping
86
+ def display_data_loading_component():
87
+ """
88
+ Component for a streamlit app about topic modelling. It allows to choose data to load and preprocess data.
89
+ Preprocessing of data includes:
90
+ - concatenation of data from different files
91
+ - adding potentially missing columns to make all datasets homogeneous
92
+ - removal of duplicates
93
+ - splitting the dataset by paragraphs to avoid too long textes
94
+ - filtering based on timestamp range
95
+ - filtering based on a minimum number of characters
96
+
97
+ The initial dataframe (one line per document) is stored after filtering of bad data inside a Streamlit state
98
+ variable "initial_df". After split by paragraph, it is stored in "df_split". The final dataframe (possibly
99
+ split by paragraph from initial documents and in all cases filtered by dates) is stored inside the Streamlit
100
+ state variable "time_filtered_df".
101
+ """
102
+ # Data loading section
103
+ st.header("Data loading")
104
+
105
+ # Find files in the current directory and subdirectories
106
+ tab1, tab2 = st.tabs(
107
+ [
108
+ CLIENT_STORAGE_ICON + " Data from local storage",
109
+ SERVER_STORAGE_ICON + " Data from server data",
110
+ ]
111
+ )
112
+ compatible_extensions = FORMAT_ICONS.keys()
113
+
114
+ with tab1:
115
+ st.file_uploader(
116
+ label="Select dataset from local storage (.xlsx, .csv, .json, .jsonl, .parquet)",
117
+ type=compatible_extensions,
118
+ accept_multiple_files=True,
119
+ help="Drag and drop files to be used as dataset in this area",
120
+ on_change=save_widget_state,
121
+ key="uploaded_files",
122
+ )
123
+
124
+ with tab2:
125
+ register_widget("selected_files")
126
+ st.multiselect(
127
+ label="Select one or more datasets from the server data",
128
+ options=find_compatible_files(DATA_PATH, compatible_extensions),
129
+ default=SessionStateManager.get("selected_files", []),
130
+ key="selected_files",
131
+ format_func=lambda x: FORMAT_ICONS[x.suffix.lstrip(".")] + " " + str(x),
132
+ on_change=save_widget_state,
133
+ )
134
+
135
+ if (
136
+ not SessionStateManager.get("uploaded_files", [])
137
+ and not SessionStateManager.get("selected_files")
138
+ and "initial_df" not in st.session_state
139
+ ):
140
+ st.warning(NO_DATASET_WARNING, icon=WARNING_ICON)
141
+ st.stop()
142
+
143
+ # Load each selected file, then concatenate them
144
+ # Priority to local data if both are set
145
+ dfs = None
146
+ if SessionStateManager.get("uploaded_files"):
147
+ dfs = _process_uploaded_files(
148
+ SessionStateManager.get("uploaded_files"),
149
+ )
150
+ elif SessionStateManager.get("selected_files"):
151
+ dfs = _load_files(
152
+ SessionStateManager.get("selected_files"),
153
+ )
154
+
155
+ # If DataFrames found, concatenate them
156
+ if dfs:
157
+ # Concatenate DataFrames
158
+ df = pd.concat(dfs, ignore_index=True)
159
+
160
+ # Save state of initial DF (before split and data selection)
161
+ st.session_state["initial_df"] = df.copy()
162
+ # If dfs is None and there is no 'initial_df' in session_state, show warining
163
+ elif "initial_df" not in st.session_state:
164
+ st.warning(
165
+ NO_DATA_AFTER_PREPROCESSING_MESSAGE,
166
+ icon=WARNING_ICON,
167
+ )
168
+ st.stop()
169
+ # If dfs is None but there is initial_df, set it
170
+ else:
171
+ df = st.session_state["initial_df"]
172
+
173
+ # Show raw data info
174
+ st.write(
175
+ f"Number of documents in raw data: **{len(st.session_state['initial_df'])}**"
176
+ )
177
+
178
+ # Data filtering section
179
+ st.header("Data filtering")
180
+
181
+ # Display number input and checkbox for preprocessing options
182
+ col1, col2, col3 = st.columns(3)
183
+ with col1:
184
+ register_widget("min_chars")
185
+ st.number_input(
186
+ "Minimum Characters",
187
+ value=MIN_CHARS_DEFAULT,
188
+ min_value=0,
189
+ max_value=1000,
190
+ key="min_chars",
191
+ on_change=save_widget_state,
192
+ help="Minimum number of characters each document must contain.",
193
+ )
194
+ with col2:
195
+ register_widget("sample_size")
196
+ sample_size = st.number_input(
197
+ "Sample ratio",
198
+ value=SAMPLE_SIZE_DEFAULT,
199
+ min_value=0.0,
200
+ max_value=1.0,
201
+ key="sample_size",
202
+ on_change=save_widget_state,
203
+ help="Fraction of raw data to use for computing topics. Randomly samples documents from raw data.",
204
+ )
205
+ with col3:
206
+ register_widget("split_by_paragraph")
207
+ SessionStateManager.get_or_set("split_by_paragraph", "no")
208
+ st.segmented_control(
209
+ "Split text by paragraphs",
210
+ key="split_by_paragraph",
211
+ options=["no", "yes", "enhanced"],
212
+ selection_mode="single",
213
+ help="'No split': No splitting on the documents ; 'Split by paragraphs': Split documents into paragraphs ; "
214
+ "'Enhanced split': uses a more advanced but slower method for splitting that considers the embedding "
215
+ "model's maximum input length.",
216
+ on_change=save_widget_state,
217
+ )
218
+
219
+ df = split_data(
220
+ df,
221
+ SessionStateManager.get("min_chars"),
222
+ SessionStateManager.get("split_by_paragraph"),
223
+ embedding_model_name=SessionStateManager.get("embedding_model_name"),
224
+ )
225
+
226
+ # Deduplicate using all columns
227
+ df = df.drop_duplicates()
228
+
229
+ # Save state of split dataframe (before time-based filtering)
230
+ st.session_state["split_df"] = df.copy()
231
+
232
+ # Select timeframe
233
+ min_date, max_date = st.session_state["initial_df"][TIMESTAMP_COLUMN].dt.date.agg(
234
+ ["min", "max"]
235
+ )
236
+ register_widget("timeframe_slider")
237
+ start_date, end_date = st.slider(
238
+ "Select Timeframe",
239
+ min_value=min_date,
240
+ max_value=max_date,
241
+ value=(min_date, max_date),
242
+ key="timeframe_slider",
243
+ on_change=save_widget_state,
244
+ )
245
+
246
+ # Filter and sample the DataFrame
247
+ df_filtered = df[
248
+ (df[TIMESTAMP_COLUMN].dt.date >= start_date)
249
+ & (df[TIMESTAMP_COLUMN].dt.date <= end_date)
250
+ ]
251
+ df_filtered = df_filtered.sort_values(by=TIMESTAMP_COLUMN).reset_index(drop=True)
252
+
253
+ if sample_size < 1:
254
+ df_filtered = df_filtered.sample(frac=sample_size, random_state=42)
255
+
256
+ df_filtered = df_filtered.sort_values(by=TIMESTAMP_COLUMN).reset_index(drop=True)
257
+
258
+ SessionStateManager.set("time_filtered_df", df_filtered)
259
+ st.write(
260
+ f"Number of documents in filtered data: **{len(SessionStateManager.get_dataframe('time_filtered_df'))}**"
261
+ )
262
+ st.dataframe(
263
+ SessionStateManager.get_dataframe("time_filtered_df")[
264
+ [TEXT_COLUMN, "timestamp"]
265
+ ],
266
+ use_container_width=True,
267
+ )
@@ -0,0 +1,52 @@
1
+ # Copyright (c) 2024, RTE (https://www.rte-france.com)
2
+ # See AUTHORS.txt
3
+ # SPDX-License-Identifier: MPL-2.0
4
+ # This file is part of BERTrend.
5
+ import streamlit as st
6
+
7
+ from bertrend.demos.demos_utils.icons import SUCCESS_ICON
8
+ from bertrend.demos.demos_utils.messages import EMBEDDINGS_CALCULATED_MESSAGE
9
+ from bertrend.demos.demos_utils.state_utils import SessionStateManager
10
+ from bertrend.services.embedding_service import EmbeddingService
11
+ from bertrend.utils.data_loading import TEXT_COLUMN
12
+
13
+
14
+ def display_embed_documents_component():
15
+ """
16
+ Common Streamlit UI component for embedding documents.
17
+ """
18
+ # Embed documents
19
+ if st.button("Embed Documents"):
20
+ with st.spinner("Embedding documents..."):
21
+ embedding_dtype = SessionStateManager.get("embedding_dtype")
22
+ embedding_model_name = SessionStateManager.get("embedding_model_name")
23
+ if SessionStateManager.get("embedding_service_type", "remote") == "local":
24
+ embedding_service = EmbeddingService(
25
+ local=True,
26
+ model_name=embedding_model_name,
27
+ embedding_dtype=embedding_dtype,
28
+ )
29
+ else:
30
+ embedding_service = EmbeddingService(
31
+ local=False,
32
+ url=SessionStateManager.get("embedding_service_url"),
33
+ )
34
+
35
+ texts = SessionStateManager.get_dataframe("time_filtered_df")[
36
+ TEXT_COLUMN
37
+ ].tolist()
38
+
39
+ embeddings, token_strings, token_embeddings = embedding_service.embed(
40
+ texts=texts,
41
+ )
42
+
43
+ SessionStateManager.set(
44
+ "embedding_model", embedding_service.embedding_model
45
+ )
46
+ SessionStateManager.set("embeddings", embeddings)
47
+ SessionStateManager.set("token_strings", token_strings)
48
+ SessionStateManager.set("token_embeddings", token_embeddings)
49
+
50
+ SessionStateManager.set("data_embedded", True)
51
+
52
+ st.success(EMBEDDINGS_CALCULATED_MESSAGE, icon=SUCCESS_ICON)
@@ -0,0 +1,41 @@
1
+ # Copyright (c) 2024, RTE (https://www.rte-france.com)
2
+ # See AUTHORS.txt
3
+ # SPDX-License-Identifier: MPL-2.0
4
+ # This file is part of BERTrend.
5
+
6
+ WARNING_ICON = ":material/warning:"
7
+ ERROR_ICON = ":material/error:"
8
+ INFO_ICON = ":material/info:"
9
+ EDIT_ICON = ":material/edit:"
10
+ ADD_ICON = ":material/add_circle:"
11
+ DELETE_ICON = ":material/delete:"
12
+ SUCCESS_ICON = ":material/check:"
13
+ SETTINGS_ICON = ":material/settings:"
14
+ TOPIC_ICON = ":material/speaker_notes:"
15
+ TREND_ICON = ":material/trending_up:"
16
+ MODELS_ICON = ":material/network_intelligence:"
17
+ EMBEDDING_ICON = ":material/memory:"
18
+ SAVE_ICON = ":material/save:"
19
+ TOPIC_EXPLORATION_ICON = ":material/explore:"
20
+ TOPIC_VISUALIZATION_ICON = ":material/monitoring:"
21
+ TEMPORAL_VISUALIZATION_ICON = ":material/timelapse:"
22
+ NEWSLETTER_ICON = ":material/newspaper:"
23
+ ANALYSIS_ICON = ":material/query_stats:"
24
+ DATA_LOADING_ICON = ":material/drive_folder_upload:"
25
+ MODEL_TRAINING_ICON = ":material/cognition:"
26
+ SERVER_STORAGE_ICON = ":material/database:"
27
+ CLIENT_STORAGE_ICON = ":material/upload:"
28
+ UNHAPPY_ICON = ":material/sentiment_extremely_dissatisfied:"
29
+ TOGGLE_ON_ICON = ":material/toggle_on:"
30
+ TOGGLE_OFF_ICON = ":material/toggle_off:"
31
+ NOISE_ICON = ":material/signal_cellular_off:"
32
+ WEAK_SIGNAL_ICON = ":material/signal_cellular_1_bar:"
33
+ STRONG_SIGNAL_ICON = ":material/signal_cellular_3_bar:"
34
+ DOWNLOAD_ICON = ":material/download:"
35
+ EMAIL_ICON = ":material/mail:"
36
+ RESTART_ICON = ":material/restart_alt:"
37
+
38
+ JSON_ICON = "๐Ÿงพ"
39
+ PARQUET_ICON = "๐Ÿ“ฆ๏ธ"
40
+ CSV_ICON = "๐Ÿ“‰"
41
+ XLSX_ICON = "๐Ÿ“Š"
@@ -0,0 +1,11 @@
1
+ # Copyright (c) 2024, RTE (https://www.rte-france.com)
2
+ # See AUTHORS.txt
3
+ # SPDX-License-Identifier: MPL-2.0
4
+ # This file is part of BERTrend.
5
+
6
+ EMBEDDINGS_CALCULATED_MESSAGE = "Embeddings calculated successfully!"
7
+ NO_EMBEDDINGS_WARNING_MESSAGE = (
8
+ "Please embed data and train models before proceeding to analysis."
9
+ )
10
+ MODEL_TRAINING_COMPLETE_MESSAGE = "Model training complete!"
11
+ NO_DATA_AFTER_PREPROCESSING_MESSAGE = "No data available after preprocessing. Please check the selected files and preprocessing options."