bertrend 0.3.1__tar.gz → 0.3.2__tar.gz

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 (184) hide show
  1. {bertrend-0.3.1 → bertrend-0.3.2}/PKG-INFO +12 -8
  2. {bertrend-0.3.1 → bertrend-0.3.2}/README.md +10 -6
  3. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/llm_utils/newsletter_features.py +3 -3
  4. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/services/summary/abstractive_summarizer.py +1 -1
  5. bertrend-0.3.2/bertrend/tests/README_integration_tests.md +91 -0
  6. bertrend-0.3.2/bertrend/tests/conftest.py +11 -0
  7. bertrend-0.3.2/bertrend/tests/test_integration.py +442 -0
  8. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/topic_analysis/representative_docs.py +6 -5
  9. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/newsletters/__main__.py +4 -2
  10. {bertrend-0.3.1 → bertrend-0.3.2}/pyproject.toml +2 -2
  11. {bertrend-0.3.1 → bertrend-0.3.2}/AUTHORS.txt +0 -0
  12. {bertrend-0.3.1 → bertrend-0.3.2}/LICENSE.md +0 -0
  13. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/BERTopicModel.py +0 -0
  14. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/BERTrend.py +0 -0
  15. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/__init__.py +0 -0
  16. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/config/bertopic_default_config.toml +0 -0
  17. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/config/bertrend_default_config.toml +0 -0
  18. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/config/parameters.py +0 -0
  19. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/config/services_default_config.toml +0 -0
  20. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/demos/__init__.py +0 -0
  21. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/demos/demos_utils/__init__.py +0 -0
  22. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/demos/demos_utils/data_loading_component.py +0 -0
  23. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/demos/demos_utils/embed_documents_component.py +0 -0
  24. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/demos/demos_utils/icons.py +0 -0
  25. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/demos/demos_utils/messages.py +0 -0
  26. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/demos/demos_utils/parameters_component.py +0 -0
  27. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/demos/demos_utils/state_utils.py +0 -0
  28. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/demos/start_summarizer_app.sh +0 -0
  29. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/demos/start_topic_analysis_app.sh +0 -0
  30. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/demos/start_weak_signals_app.sh +0 -0
  31. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/demos/summarization/__init__.py +0 -0
  32. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/demos/summarization/summarizer_app.py +0 -0
  33. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/demos/topic_analysis/__init__.py +0 -0
  34. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/demos/topic_analysis/app.py +0 -0
  35. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/demos/topic_analysis/app_utils.py +0 -0
  36. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/demos/topic_analysis/demo_pages/__init__.py +0 -0
  37. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/demos/topic_analysis/demo_pages/explore_topics.py +0 -0
  38. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/demos/topic_analysis/demo_pages/newsletters_generation.py +0 -0
  39. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/demos/topic_analysis/demo_pages/temporal_visualizations.py +0 -0
  40. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/demos/topic_analysis/demo_pages/topic_visualizations.py +0 -0
  41. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/demos/topic_analysis/demo_pages/training_page.py +0 -0
  42. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/demos/topic_analysis/messages.py +0 -0
  43. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/demos/weak_signals/__init__.py +0 -0
  44. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/demos/weak_signals/app.py +0 -0
  45. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/demos/weak_signals/messages.py +0 -0
  46. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/demos/weak_signals/visualizations_utils.py +0 -0
  47. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/llm_utils/__init__.py +0 -0
  48. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/llm_utils/newsletter.css +0 -0
  49. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/llm_utils/openai_client.py +0 -0
  50. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/llm_utils/prompts.py +0 -0
  51. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/metrics/__init__.py +0 -0
  52. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/metrics/temporal_metrics_embedding.py +0 -0
  53. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/metrics/topic_metrics.py +0 -0
  54. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/resources/common_ngrams.json +0 -0
  55. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/resources/stopwords-en.json +0 -0
  56. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/resources/stopwords-fr.json +0 -0
  57. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/resources/stopwords-rte.json +0 -0
  58. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/services/__init__.py +0 -0
  59. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/services/authentication.py +0 -0
  60. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/services/embedding_client.py +0 -0
  61. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/services/embedding_service.py +0 -0
  62. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/services/summarizer.py +0 -0
  63. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/services/summary/__init__.py +0 -0
  64. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/services/summary/chatgpt_summarizer.py +0 -0
  65. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/services/summary/extractive_summarizer.py +0 -0
  66. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/services/summary/lexrank.py +0 -0
  67. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/services/summary/prompts.py +0 -0
  68. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/tests/__init__.py +0 -0
  69. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/tests/test_abstractive_summarizer.py +0 -0
  70. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/tests/test_bertrend.py +0 -0
  71. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/tests/test_cache_utils.py +0 -0
  72. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/tests/test_chatgpt_summarizer.py +0 -0
  73. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/tests/test_config_utils.py +0 -0
  74. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/tests/test_data_loading.py +0 -0
  75. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/tests/test_data_structure.py +0 -0
  76. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/tests/test_embedding_client.py +0 -0
  77. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/tests/test_extractive_summarizer.py +0 -0
  78. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/tests/test_installation.py +0 -0
  79. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/tests/test_newsletter_features.py +0 -0
  80. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/tests/test_openai_client.py +0 -0
  81. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/tests/test_representative_docs.py +0 -0
  82. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/tests/test_summarizer.py +0 -0
  83. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/tests/test_temporal_metrics_embedding.py +0 -0
  84. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/tests/test_topic_description.py +0 -0
  85. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/tests/test_topic_metrics.py +0 -0
  86. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/tests/test_topic_model.py +0 -0
  87. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/tests/test_trend_analysis_data_structure.py +0 -0
  88. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/tests/test_trend_analysis_prompts.py +0 -0
  89. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/tests/test_trend_analysis_visualizations.py +0 -0
  90. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/tests/test_trend_analysis_weak_signals.py +0 -0
  91. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/tests/test_utils_init.py +0 -0
  92. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/tests/test_visualizations.py +0 -0
  93. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/topic_analysis/__init__.py +0 -0
  94. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/topic_analysis/data_structure.py +0 -0
  95. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/topic_analysis/prompts.py +0 -0
  96. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/topic_analysis/topic_description.py +0 -0
  97. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/topic_analysis/visualizations.py +0 -0
  98. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/trend_analysis/__init__.py +0 -0
  99. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/trend_analysis/data_structure.py +0 -0
  100. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/trend_analysis/prompts.py +0 -0
  101. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/trend_analysis/signal_llm_template_en.html +0 -0
  102. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/trend_analysis/signal_llm_template_fr.html +0 -0
  103. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/trend_analysis/visualizations.py +0 -0
  104. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/trend_analysis/weak_signals.py +0 -0
  105. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/utils/__init__.py +0 -0
  106. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/utils/cache_utils.py +0 -0
  107. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/utils/config_utils.py +0 -0
  108. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/utils/data_loading.py +0 -0
  109. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/__init__.py +0 -0
  110. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/common/__init__.py +0 -0
  111. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/common/crontab_utils.py +0 -0
  112. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/common/mail_utils.py +0 -0
  113. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/config/feeds/arxiv_feed.toml +0 -0
  114. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/config/feeds/decarbonation_feed.toml +0 -0
  115. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/config/feeds/llm_en_feed.toml +0 -0
  116. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/config/feeds/llm_feed.toml +0 -0
  117. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/config/feeds/nlp_feed.toml +0 -0
  118. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/config/feeds/rte_feed.toml +0 -0
  119. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/config/gmail_credentials.json +0 -0
  120. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/config/newsletters/arxiv_newsletter_from_last.toml +0 -0
  121. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/config/newsletters/arxiv_newsletter_from_scratch.toml +0 -0
  122. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/config/newsletters/decarbonation_newsletter_from_last.toml +0 -0
  123. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/config/newsletters/decarbonation_newsletter_from_scratch.toml +0 -0
  124. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/config/newsletters/llm_en_newsletter_from_last.toml +0 -0
  125. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/config/newsletters/llm_newsletter_from_last.toml +0 -0
  126. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/config/newsletters/nlp_newsletter_from_last.toml +0 -0
  127. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/config/newsletters/rte_newsletter_from_last.toml +0 -0
  128. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/config/newsletters/rte_newsletter_from_scratch.toml +0 -0
  129. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/data_provider/__init__.py +0 -0
  130. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/data_provider/__main__.py +0 -0
  131. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/data_provider/arxiv_provider.py +0 -0
  132. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/data_provider/bing_news_provider.py +0 -0
  133. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/data_provider/curebot_provider.py +0 -0
  134. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/data_provider/data_provider.py +0 -0
  135. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/data_provider/google_news_provider.py +0 -0
  136. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/data_provider/newscatcher_provider.py +0 -0
  137. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/data_provider/utils.py +0 -0
  138. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/exploration/__init__.py +0 -0
  139. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/exploration/curebot/__init__.py +0 -0
  140. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/exploration/curebot/app.py +0 -0
  141. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/exploration/curebot/app_utils.py +0 -0
  142. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/exploration/curebot/config.toml +0 -0
  143. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/exploration/curebot/import_extraction_article.ipynb +0 -0
  144. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/exploration/curebot/old_veille_analyse.py +0 -0
  145. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/exploration/curebot/prompts.py +0 -0
  146. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/exploration/curebot/start_newsletter_generator.sh +0 -0
  147. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/exploration/curebot/tabs/tab1.py +0 -0
  148. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/exploration/curebot/tabs/tab2.py +0 -0
  149. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/exploration/curebot/tabs/tab3.py +0 -0
  150. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/exploration/dataviz/Main_page.py +0 -0
  151. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/exploration/dataviz/__init__.py +0 -0
  152. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/exploration/dataviz/pages/1_Data_feeds.py +0 -0
  153. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/exploration/geolocalization/__init__.py +0 -0
  154. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/exploration/geolocalization/entity_utils.py +0 -0
  155. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/exploration/geolocalization/spacy/__init__.py +0 -0
  156. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/exploration/geolocalization/spacy/common_factory.py +0 -0
  157. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/exploration/geolocalization/spacy/dbpedia.py +0 -0
  158. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/exploration/geolocalization/spacy/nlp.config +0 -0
  159. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/exploration/geolocalization/spacy/utils.py +0 -0
  160. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/install_feeds_and_newsletters.py +0 -0
  161. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/newsletters/__init__.py +0 -0
  162. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/prospective_demo/.streamlit/config.toml +0 -0
  163. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/prospective_demo/.streamlit/secrets.toml +0 -0
  164. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/prospective_demo/__init__.py +0 -0
  165. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/prospective_demo/app.py +0 -0
  166. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/prospective_demo/authentication.py +0 -0
  167. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/prospective_demo/dashboard_analysis.py +0 -0
  168. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/prospective_demo/dashboard_common.py +0 -0
  169. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/prospective_demo/dashboard_signals.py +0 -0
  170. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/prospective_demo/feeds_common.py +0 -0
  171. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/prospective_demo/feeds_config.py +0 -0
  172. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/prospective_demo/feeds_data.py +0 -0
  173. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/prospective_demo/i18n.py +0 -0
  174. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/prospective_demo/llm_utils.py +0 -0
  175. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/prospective_demo/models_info.py +0 -0
  176. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/prospective_demo/perf_utils.py +0 -0
  177. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/prospective_demo/process_new_data.py +0 -0
  178. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/prospective_demo/report_generation.py +0 -0
  179. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/prospective_demo/report_template.html +0 -0
  180. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/prospective_demo/report_utils.py +0 -0
  181. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/prospective_demo/start_demo.sh +0 -0
  182. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/prospective_demo/streamlit_utils.py +0 -0
  183. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/start_apps.sh +0 -0
  184. {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/stop_apps.sh +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: bertrend
3
- Version: 0.3.1
3
+ Version: 0.3.2
4
4
  Summary: BERTrend analyses topic evolution over time using state-of-the-art transformer models: topic modelling and LLM
5
5
  Home-page: https://github.com/rte-france/BERTrend
6
6
  License: LICENSE.md
@@ -64,7 +64,7 @@ Requires-Dist: thefuzz (>=0.22.1,<0.23.0)
64
64
  Requires-Dist: tiktoken (>=0.8.0,<0.9.0)
65
65
  Requires-Dist: tldextract (>=2.0.1)
66
66
  Requires-Dist: tokenizers (<0.20.0)
67
- Requires-Dist: torch (>=2.5.1,<3.0.0)
67
+ Requires-Dist: torch (==2.7.0)
68
68
  Requires-Dist: tqdm (>=4.67.1,<5.0.0)
69
69
  Requires-Dist: umap-learn (>=0.5.7,<0.6.0)
70
70
  Project-URL: Repository, https://github.com/rte-france/BERTrend
@@ -73,18 +73,16 @@ Description-Content-Type: text/markdown
73
73
  # BERTrend
74
74
 
75
75
  [![rte-france - BERTrend](https://img.shields.io/static/v1?label=rte-france&message=BERTrend&color=blue&logo=github)](https://github.com/rte-france/BERTrend "Go to GitHub repo")
76
- ![GitHub License](https://img.shields.io/github/license/rte-france/BERTrend)
77
76
  [![stars - BERTrend](https://img.shields.io/github/stars/rte-france/BERTrend?style=social)](https://github.com/rte-france/BERTrend)
78
77
  [![forks - BERTrend](https://img.shields.io/github/forks/rte-france/BERTrend?style=social)](https://github.com/rte-france/BERTrend)
79
78
  [![issues - BERTrend](https://img.shields.io/github/issues/rte-france/BERTrend)](https://github.com/rte-france/BERTrend/issues)
80
- ![Static Badge](https://img.shields.io/badge/python-%3E%3D3.12-blue)
79
+ [![Downloads](https://pepy.tech/badge/bertrend)](https://pepy.tech/project/bertrend)
80
+ [![PyPi_Version](https://img.shields.io/pypi/v/bertrend.svg)](https://pypi.org/project/bertrend/)
81
+ [![PyPi_Compat](https://img.shields.io/pypi/pyversions/bertrend.svg)](https://pypi.org/project/bertrend/)
82
+ ![GitHub License](https://img.shields.io/github/license/rte-france/BERTrend)
81
83
 
82
84
  ![BERTrend pipeline](./BERTrend_overview.png)
83
85
 
84
- ## Work in progress
85
-
86
- - make BERTrend available as a python package in PyPI
87
-
88
86
  ## Paper
89
87
 
90
88
  The code in this repository is part of the work described in the paper:
@@ -164,6 +162,12 @@ BERTrend requires the following environment variables to be set:
164
162
 
165
163
  `python -m venv <path>/bertrend`
166
164
 
165
+
166
+ - Installation from pypi
167
+
168
+ `pip install bertrend`
169
+
170
+
167
171
  - Install using pip or poetry in the BERTrend home directory:
168
172
 
169
173
  ```bash
@@ -1,18 +1,16 @@
1
1
  # BERTrend
2
2
 
3
3
  [![rte-france - BERTrend](https://img.shields.io/static/v1?label=rte-france&message=BERTrend&color=blue&logo=github)](https://github.com/rte-france/BERTrend "Go to GitHub repo")
4
- ![GitHub License](https://img.shields.io/github/license/rte-france/BERTrend)
5
4
  [![stars - BERTrend](https://img.shields.io/github/stars/rte-france/BERTrend?style=social)](https://github.com/rte-france/BERTrend)
6
5
  [![forks - BERTrend](https://img.shields.io/github/forks/rte-france/BERTrend?style=social)](https://github.com/rte-france/BERTrend)
7
6
  [![issues - BERTrend](https://img.shields.io/github/issues/rte-france/BERTrend)](https://github.com/rte-france/BERTrend/issues)
8
- ![Static Badge](https://img.shields.io/badge/python-%3E%3D3.12-blue)
7
+ [![Downloads](https://pepy.tech/badge/bertrend)](https://pepy.tech/project/bertrend)
8
+ [![PyPi_Version](https://img.shields.io/pypi/v/bertrend.svg)](https://pypi.org/project/bertrend/)
9
+ [![PyPi_Compat](https://img.shields.io/pypi/pyversions/bertrend.svg)](https://pypi.org/project/bertrend/)
10
+ ![GitHub License](https://img.shields.io/github/license/rte-france/BERTrend)
9
11
 
10
12
  ![BERTrend pipeline](./BERTrend_overview.png)
11
13
 
12
- ## Work in progress
13
-
14
- - make BERTrend available as a python package in PyPI
15
-
16
14
  ## Paper
17
15
 
18
16
  The code in this repository is part of the work described in the paper:
@@ -92,6 +90,12 @@ BERTrend requires the following environment variables to be set:
92
90
 
93
91
  `python -m venv <path>/bertrend`
94
92
 
93
+
94
+ - Installation from pypi
95
+
96
+ `pip install bertrend`
97
+
98
+
95
99
  - Install using pip or poetry in the BERTrend home directory:
96
100
 
97
101
  ```bash
@@ -126,9 +126,9 @@ def generate_newsletter(
126
126
  # Iterate over topics
127
127
  for i in tqdm(range(top_n_topics), desc="Processing topics..."):
128
128
  sub_df = get_most_representative_docs(
129
- topic_model,
130
- df,
131
- topics,
129
+ topic_model=topic_model,
130
+ df=df,
131
+ topics=topics,
132
132
  mode=top_n_docs_mode,
133
133
  df_split=df_split,
134
134
  topic_number=i,
@@ -22,7 +22,7 @@ class AbstractiveSummarizer(Summarizer):
22
22
  def __init__(self, model_name=DEFAULT_ABSTRACTIVE_MODEL):
23
23
  self.model_name = model_name
24
24
  self.WHITESPACE_HANDLER = lambda k: re.sub(
25
- "\s+", " ", re.sub("\n+", " ", k.strip())
25
+ r"\s+", " ", re.sub(r"\n+", " ", k.strip())
26
26
  )
27
27
  self.tokenizer = AutoTokenizer.from_pretrained(model_name)
28
28
  self.model = AutoModelForSeq2SeqLM.from_pretrained(model_name)
@@ -0,0 +1,91 @@
1
+ # BERTrend Integration Tests
2
+
3
+ This directory contains integration tests for the BERTrend project. These tests verify that the different components of the system work together correctly in real-world scenarios.
4
+
5
+ ## Overview
6
+
7
+ The integration tests are organized into several test classes, each focusing on a specific integration point:
8
+
9
+ 1. **TestEmbeddingServiceIntegration**: Tests the integration of the EmbeddingService with local embedding models.
10
+ 2. **TestBERTopicModelIntegration**: Tests the integration of BERTopicModel with pre-computed embeddings.
11
+ 3. **TestBERTrendIntegration**: Tests the end-to-end workflow of BERTrend, including training topic models, calculating signal popularity, and classifying signals.
12
+ 4. **TestDataPipelineIntegration**: Tests the data loading and processing pipeline.
13
+ 5. **TestNewsletterIntegration**: Tests the newsletter generation functionality, which combines topic modeling with LLM-based summarization.
14
+
15
+ ## Running the Tests
16
+
17
+ To run the integration tests, you need to have the BERTrend package installed with all its dependencies. You can run the tests using pytest with the following command:
18
+
19
+ ```bash
20
+ pytest -m integration bertrend/tests/test_integration.py -v
21
+ ```
22
+
23
+ This will run only the tests marked with the `integration` marker. The `-v` flag provides verbose output.
24
+
25
+ ### Running Specific Tests
26
+
27
+ To run a specific test class, you can use:
28
+
29
+ ```bash
30
+ pytest -m integration bertrend/tests/test_integration.py::TestBERTrendIntegration -v
31
+ ```
32
+
33
+ To run a specific test method, you can use:
34
+
35
+ ```bash
36
+ pytest -m integration bertrend/tests/test_integration.py::TestBERTrendIntegration::test_bertrend_end_to_end -v
37
+ ```
38
+
39
+ ## Test Requirements
40
+
41
+ The integration tests have the following requirements:
42
+
43
+ 1. **Sentence Transformer Models**: The tests use the `all-MiniLM-L6-v2` model, which will be downloaded automatically if not already present.
44
+ 2. **OpenAI API Key**: The newsletter generation test requires an OpenAI API key. If the key is not available, the test will be skipped.
45
+ 3. **Temporary Directory**: The tests use pytest's `tmp_path` fixture to create temporary directories for saving models and files.
46
+
47
+ ## Adding New Integration Tests
48
+
49
+ When adding new integration tests, please follow these guidelines:
50
+
51
+ 1. Use the `@pytest.mark.integration` decorator to mark the test as an integration test.
52
+ 2. Use small, fast models and synthetic data to ensure the tests run efficiently.
53
+ 3. Include assertions that verify the correct integration between components.
54
+ 4. Handle external dependencies gracefully, skipping tests if necessary.
55
+ 5. Clean up any resources created during the test.
56
+
57
+ ## Example
58
+
59
+ Here's an example of a simple integration test:
60
+
61
+ ```python
62
+ @pytest.mark.integration
63
+ def test_embedding_service_with_local_model():
64
+ """Test that EmbeddingService works with a local model."""
65
+ # Use a small, fast model for testing
66
+ model_name = "all-MiniLM-L6-v2"
67
+
68
+ # Create a small test dataset
69
+ texts = [
70
+ "This is a test document about artificial intelligence.",
71
+ "Machine learning is a subset of artificial intelligence.",
72
+ "Natural language processing is used in many applications."
73
+ ]
74
+
75
+ # Initialize the embedding service with local model
76
+ embedding_service = EmbeddingService(
77
+ local=True,
78
+ model_name=model_name
79
+ )
80
+
81
+ # Generate embeddings
82
+ embeddings, token_strings, token_embeddings = embedding_service.embed(texts)
83
+
84
+ # Verify the results
85
+ assert embeddings is not None
86
+ assert embeddings.shape == (len(texts), 384) # 384 is the dimension for all-MiniLM-L6-v2
87
+ assert token_strings is not None
88
+ assert token_embeddings is not None
89
+ assert len(token_strings) == len(texts)
90
+ assert len(token_embeddings) == len(texts)
91
+ ```
@@ -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
+ import pytest
7
+
8
+
9
+ def pytest_configure(config):
10
+ """Register custom markers."""
11
+ config.addinivalue_line("markers", "integration: mark test as an integration test")
@@ -0,0 +1,442 @@
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 os
7
+ import pytest
8
+ import numpy as np
9
+ import pandas as pd
10
+ from pathlib import Path
11
+ from datetime import datetime, timedelta
12
+
13
+ from bertopic import BERTopic
14
+ from sentence_transformers import SentenceTransformer
15
+
16
+ from bertrend.BERTrend import BERTrend
17
+ from bertrend.BERTopicModel import BERTopicModel
18
+ from bertrend.services.embedding_service import EmbeddingService
19
+ from bertrend.utils.data_loading import load_data, split_data, group_by_days
20
+
21
+
22
+ class TestEmbeddingServiceIntegration:
23
+ """Integration tests for the EmbeddingService."""
24
+
25
+ @pytest.mark.integration
26
+ def test_embedding_service_with_local_model(self):
27
+ """Test that EmbeddingService works with a local model."""
28
+ # Use a small, fast model for testing
29
+ model_name = "all-MiniLM-L6-v2"
30
+
31
+ # Create a small test dataset
32
+ texts = [
33
+ "This is a test document about artificial intelligence.",
34
+ "Machine learning is a subset of artificial intelligence.",
35
+ "Natural language processing is used in many applications.",
36
+ ]
37
+
38
+ # Initialize the embedding service with local model
39
+ embedding_service = EmbeddingService(local=True, model_name=model_name)
40
+
41
+ # Generate embeddings
42
+ embeddings, token_strings, token_embeddings = embedding_service.embed(texts)
43
+
44
+ # Verify the results
45
+ assert embeddings is not None
46
+ assert embeddings.shape == (
47
+ len(texts),
48
+ 384,
49
+ ) # 384 is the dimension for all-MiniLM-L6-v2
50
+ assert token_strings is not None
51
+ assert token_embeddings is not None
52
+ assert len(token_strings) == len(texts)
53
+ assert len(token_embeddings) == len(texts)
54
+
55
+
56
+ class TestBERTopicModelIntegration:
57
+ """Integration tests for the BERTopicModel."""
58
+
59
+ @pytest.mark.integration
60
+ def test_bertopic_model_with_embeddings(self):
61
+ """Test that BERTopicModel works with pre-computed embeddings."""
62
+ # Create a small test dataset
63
+ docs = [
64
+ "This is a document about artificial intelligence.",
65
+ "Machine learning is a subset of artificial intelligence.",
66
+ "Natural language processing is used in many applications.",
67
+ "Deep learning models require large amounts of data.",
68
+ "Neural networks are inspired by the human brain.",
69
+ "Computer vision is used for image recognition.",
70
+ "Reinforcement learning is used for decision making.",
71
+ "Supervised learning requires labeled data.",
72
+ "Unsupervised learning finds patterns in unlabeled data.",
73
+ "Transfer learning uses knowledge from one task for another.",
74
+ ] * 5 # Duplicate to have enough data for clustering
75
+
76
+ # Use a small, fast model for testing
77
+ model_name = "all-MiniLM-L6-v2"
78
+
79
+ # Initialize the embedding service
80
+ embedding_service = EmbeddingService(local=True, model_name=model_name)
81
+
82
+ # Generate embeddings
83
+ embeddings, _, _ = embedding_service.embed(docs)
84
+
85
+ # Initialize the BERTopicModel with parameters suitable for small test datasets
86
+ topic_model = BERTopicModel(
87
+ {
88
+ "vectorizer_model": {
89
+ "min_df": 1
90
+ }, # Reduce min_df to handle small datasets
91
+ "umap_model": {
92
+ "n_components": 2,
93
+ "n_neighbors": 3,
94
+ }, # Reduce dimensions for small datasets
95
+ "hdbscan_model": {
96
+ "min_cluster_size": 2,
97
+ "min_samples": 1,
98
+ }, # Adjust clustering for small datasets
99
+ }
100
+ )
101
+
102
+ # Fit the model
103
+ output = topic_model.fit(
104
+ docs=docs, embeddings=embeddings, embedding_model=model_name
105
+ )
106
+
107
+ # Verify the results
108
+ assert output is not None
109
+ assert output.topic_model is not None
110
+ assert output.topics is not None
111
+ assert output.probs is not None
112
+ assert output.embeddings is None # Not stored in output
113
+ assert len(output.topics) == len(docs)
114
+
115
+
116
+ class TestBERTrendIntegration:
117
+ """Integration tests for the BERTrend class."""
118
+
119
+ @pytest.fixture
120
+ def sample_data(self):
121
+ """Create a sample dataset for testing."""
122
+ # Create timestamps spanning 3 months
123
+ start_date = datetime(2023, 1, 1)
124
+ dates = [start_date + timedelta(days=i) for i in range(0, 90, 3)]
125
+
126
+ # Create sample data with timestamps
127
+ data = []
128
+ for i, date in enumerate(dates):
129
+ data.append(
130
+ {
131
+ "text": f"Document {i} about artificial intelligence and machine learning.",
132
+ "timestamp": date,
133
+ "document_id": f"doc_{i}",
134
+ "source": f"source_{i % 3}",
135
+ "url": f"http://example.com/{i}",
136
+ }
137
+ )
138
+ data.append(
139
+ {
140
+ "text": f"Document {i} about natural language processing and neural networks.",
141
+ "timestamp": date,
142
+ "document_id": f"doc_{i+100}",
143
+ "source": f"source_{i % 3}",
144
+ "url": f"http://example.com/{i+100}",
145
+ }
146
+ )
147
+ data.append(
148
+ {
149
+ "text": f"Document {i} about computer vision and deep learning.",
150
+ "timestamp": date,
151
+ "document_id": f"doc_{i+200}",
152
+ "source": f"source_{i % 3}",
153
+ "url": f"http://example.com/{i+200}",
154
+ }
155
+ )
156
+
157
+ return pd.DataFrame(data)
158
+
159
+ @pytest.mark.integration
160
+ def test_bertrend_end_to_end(self, sample_data, tmp_path):
161
+ """Test the end-to-end workflow of BERTrend."""
162
+ # Use a small, fast model for testing
163
+ model_name = "all-MiniLM-L6-v2"
164
+
165
+ # Initialize the embedding service
166
+ embedding_service = EmbeddingService(local=True, model_name=model_name)
167
+
168
+ # Generate embeddings for the entire dataset
169
+ embeddings, _, _ = embedding_service.embed(sample_data["text"])
170
+
171
+ # Initialize BERTopicModel with parameters suitable for small test datasets
172
+ topic_model = BERTopicModel(
173
+ {
174
+ "vectorizer_model": {
175
+ "min_df": 1
176
+ }, # Reduce min_df to handle small datasets
177
+ "umap_model": {
178
+ "n_components": 2,
179
+ "n_neighbors": 3,
180
+ }, # Reduce dimensions for small datasets
181
+ "hdbscan_model": {
182
+ "min_cluster_size": 2,
183
+ "min_samples": 1,
184
+ }, # Adjust clustering for small datasets
185
+ }
186
+ )
187
+
188
+ # Initialize BERTrend
189
+ bertrend = BERTrend(topic_model=topic_model)
190
+
191
+ # Group data by days
192
+ grouped_data = group_by_days(sample_data, day_granularity=7)
193
+
194
+ # Train topic models
195
+ try:
196
+ bertrend.train_topic_models(
197
+ grouped_data=grouped_data,
198
+ embedding_model=model_name,
199
+ embeddings=embeddings,
200
+ bertrend_models_path=tmp_path,
201
+ save_topic_models=True,
202
+ )
203
+ print("train_topic_models completed successfully")
204
+ except Exception as e:
205
+ print(f"Error in train_topic_models: {e}")
206
+ raise
207
+
208
+ # Calculate signal popularity
209
+ try:
210
+ bertrend.calculate_signal_popularity()
211
+ print("calculate_signal_popularity completed successfully")
212
+ except Exception as e:
213
+ print(f"Error in calculate_signal_popularity: {e}")
214
+ raise
215
+
216
+ # Classify signals
217
+ try:
218
+ current_date = sample_data["timestamp"].max()
219
+ print(f"Current date: {current_date}")
220
+ result = bertrend.classify_signals(
221
+ window_size=30, current_date=current_date
222
+ )
223
+ print(f"classify_signals returned: {result}")
224
+ noise_df, weak_signal_df, strong_signal_df = result
225
+ except Exception as e:
226
+ print(f"Error in classify_signals: {e}")
227
+ raise
228
+
229
+ # Verify the results
230
+ print("Checking _is_fitted:", bertrend._is_fitted)
231
+ assert bertrend._is_fitted is True
232
+
233
+ print("Checking last_topic_model:", bertrend.last_topic_model)
234
+ assert bertrend.last_topic_model is not None
235
+
236
+ print(
237
+ "Checking last_topic_model_timestamp:", bertrend.last_topic_model_timestamp
238
+ )
239
+ assert bertrend.last_topic_model_timestamp is not None
240
+
241
+ print("Checking doc_groups:", len(bertrend.doc_groups))
242
+ assert len(bertrend.doc_groups) > 0
243
+
244
+ print("Checking emb_groups:", len(bertrend.emb_groups))
245
+ assert len(bertrend.emb_groups) > 0
246
+
247
+ print("Checking merged_df:", bertrend.merged_df)
248
+ assert bertrend.merged_df is not None
249
+
250
+ print("Checking all_merge_histories_df:", bertrend.all_merge_histories_df)
251
+ assert bertrend.all_merge_histories_df is not None
252
+
253
+ print("Checking all_new_topics_df:", bertrend.all_new_topics_df)
254
+ assert bertrend.all_new_topics_df is not None
255
+
256
+ # Verify that we have classified signals
257
+ print("Checking noise_df:", noise_df)
258
+ assert isinstance(noise_df, pd.DataFrame)
259
+
260
+ print("Checking weak_signal_df:", weak_signal_df)
261
+ assert isinstance(weak_signal_df, pd.DataFrame)
262
+
263
+ print("Checking strong_signal_df:", strong_signal_df)
264
+ assert isinstance(strong_signal_df, pd.DataFrame)
265
+
266
+ # Save and restore the model
267
+ bertrend.save_model(models_path=tmp_path)
268
+ restored_bertrend = BERTrend.restore_model(models_path=tmp_path)
269
+
270
+ # Verify the restored model
271
+ assert restored_bertrend._is_fitted is True
272
+ assert len(restored_bertrend.doc_groups) == len(bertrend.doc_groups)
273
+ assert len(restored_bertrend.emb_groups) == len(bertrend.emb_groups)
274
+
275
+
276
+ class TestDataPipelineIntegration:
277
+ """Integration tests for the data pipeline."""
278
+
279
+ @pytest.fixture
280
+ def sample_csv_file(self, tmp_path):
281
+ """Create a sample CSV file for testing."""
282
+ file_path = tmp_path / "sample_data.csv"
283
+
284
+ # Create sample data
285
+ data = []
286
+ for i in range(20):
287
+ data.append(
288
+ {
289
+ "text": f"Document {i} about artificial intelligence.",
290
+ "timestamp": f"2023-01-{i+1:02d}",
291
+ "document_id": f"doc_{i}",
292
+ "source": f"source_{i % 3}",
293
+ "url": f"http://example.com/{i}",
294
+ }
295
+ )
296
+
297
+ # Create DataFrame and save to CSV
298
+ df = pd.DataFrame(data)
299
+ df.to_csv(file_path, index=False)
300
+
301
+ return file_path
302
+
303
+ @pytest.mark.integration
304
+ def test_data_loading_and_processing(self, sample_csv_file):
305
+ """Test loading data from a file and processing it."""
306
+ # Load data from CSV file
307
+ df = load_data(sample_csv_file)
308
+
309
+ # Verify the loaded data
310
+ assert isinstance(df, pd.DataFrame)
311
+ assert len(df) == 20
312
+ assert "text" in df.columns
313
+ assert "timestamp" in df.columns
314
+ assert "document_id" in df.columns
315
+ assert "source" in df.columns
316
+ assert "url" in df.columns
317
+
318
+ # Split data by paragraphs
319
+ split_df = split_data(df)
320
+
321
+ # Verify the split data
322
+ assert isinstance(split_df, pd.DataFrame)
323
+ assert len(split_df) >= len(df) # Should be at least the same size
324
+
325
+ # Group data by days
326
+ grouped_data = group_by_days(df, day_granularity=7)
327
+
328
+ # Verify the grouped data
329
+ assert isinstance(grouped_data, dict)
330
+ assert all(isinstance(key, pd.Timestamp) for key in grouped_data.keys())
331
+ assert all(isinstance(value, pd.DataFrame) for value in grouped_data.values())
332
+
333
+
334
+ class TestNewsletterIntegration:
335
+ """Integration tests for the newsletter generation functionality."""
336
+
337
+ @pytest.mark.integration
338
+ def test_newsletter_generation(self, tmp_path):
339
+ """Test generating a newsletter from topic model results."""
340
+ # This test requires OpenAI API key, so we'll skip it if not available
341
+ if not os.environ.get("OPENAI_API_KEY"):
342
+ pytest.skip("OPENAI_API_KEY not set, skipping newsletter generation test")
343
+
344
+ # Import here to avoid dependency issues if OpenAI is not available
345
+ from bertrend.llm_utils.newsletter_features import (
346
+ generate_newsletter,
347
+ export_md_string,
348
+ )
349
+
350
+ # Create a sample dataset
351
+ docs = [
352
+ "This is a document about artificial intelligence.",
353
+ "Machine learning is a subset of artificial intelligence.",
354
+ "Natural language processing is used in many applications.",
355
+ "Deep learning models require large amounts of data.",
356
+ "Neural networks are inspired by the human brain.",
357
+ "Computer vision is used for image recognition.",
358
+ "Reinforcement learning is used for decision making.",
359
+ "Supervised learning requires labeled data.",
360
+ "Unsupervised learning finds patterns in unlabeled data.",
361
+ "Transfer learning uses knowledge from one task for another.",
362
+ ] * 5 # Duplicate to have enough data for clustering
363
+
364
+ # Create a DataFrame with the necessary columns
365
+ df = pd.DataFrame(
366
+ {
367
+ "text": docs,
368
+ "timestamp": [
369
+ datetime(2023, 1, 1) + timedelta(days=i) for i in range(len(docs))
370
+ ],
371
+ "document_id": [f"doc_{i}" for i in range(len(docs))],
372
+ "source": [f"source_{i % 3}" for i in range(len(docs))],
373
+ "url": [f"http://example.com/{i}" for i in range(len(docs))],
374
+ }
375
+ )
376
+
377
+ # Use a small, fast model for testing
378
+ model_name = "all-MiniLM-L6-v2"
379
+
380
+ # Initialize the embedding service
381
+ embedding_service = EmbeddingService(local=True, model_name=model_name)
382
+
383
+ # Generate embeddings
384
+ embeddings, _, _ = embedding_service.embed(docs)
385
+
386
+ # Initialize and fit the BERTopicModel with parameters suitable for small test datasets
387
+ topic_model = BERTopicModel(
388
+ {
389
+ "vectorizer_model": {
390
+ "min_df": 1
391
+ }, # Reduce min_df to handle small datasets
392
+ "umap_model": {
393
+ "n_components": 2,
394
+ "n_neighbors": 3,
395
+ }, # Reduce dimensions for small datasets
396
+ "hdbscan_model": {
397
+ "min_cluster_size": 2,
398
+ "min_samples": 1,
399
+ }, # Adjust clustering for small datasets
400
+ }
401
+ )
402
+ output = topic_model.fit(
403
+ docs=docs, embeddings=embeddings, embedding_model=model_name
404
+ )
405
+
406
+ # Generate a newsletter
407
+ newsletter_params = {
408
+ "title": "Test Newsletter",
409
+ "subtitle": "Integration Test",
410
+ "date": datetime.now().strftime("%Y-%m-%d"),
411
+ "summary_mode": "document",
412
+ "max_topics": 3,
413
+ "max_docs_per_topic": 2,
414
+ "language": "English",
415
+ }
416
+
417
+ try:
418
+ # Generate the newsletter
419
+ newsletter = generate_newsletter(
420
+ topic_model=output.topic_model,
421
+ topics=output.topics,
422
+ docs=docs,
423
+ df=df,
424
+ **newsletter_params,
425
+ )
426
+
427
+ # Export the newsletter to markdown
428
+ md_path = tmp_path / "newsletter.md"
429
+ export_md_string(newsletter, str(md_path), format="md")
430
+
431
+ # Verify the results
432
+ assert newsletter is not None
433
+ assert "title" in newsletter
434
+ assert "subtitle" in newsletter
435
+ assert "date" in newsletter
436
+ assert "topics" in newsletter
437
+ assert len(newsletter["topics"]) <= newsletter_params["max_topics"]
438
+ assert md_path.exists()
439
+
440
+ except Exception as e:
441
+ # If there's an error with the LLM service, we'll skip the test
442
+ pytest.skip(f"Error with LLM service: {str(e)}")