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.
- {bertrend-0.3.1 → bertrend-0.3.2}/PKG-INFO +12 -8
- {bertrend-0.3.1 → bertrend-0.3.2}/README.md +10 -6
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/llm_utils/newsletter_features.py +3 -3
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/services/summary/abstractive_summarizer.py +1 -1
- bertrend-0.3.2/bertrend/tests/README_integration_tests.md +91 -0
- bertrend-0.3.2/bertrend/tests/conftest.py +11 -0
- bertrend-0.3.2/bertrend/tests/test_integration.py +442 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/topic_analysis/representative_docs.py +6 -5
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/newsletters/__main__.py +4 -2
- {bertrend-0.3.1 → bertrend-0.3.2}/pyproject.toml +2 -2
- {bertrend-0.3.1 → bertrend-0.3.2}/AUTHORS.txt +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/LICENSE.md +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/BERTopicModel.py +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/BERTrend.py +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/__init__.py +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/config/bertopic_default_config.toml +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/config/bertrend_default_config.toml +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/config/parameters.py +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/config/services_default_config.toml +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/demos/__init__.py +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/demos/demos_utils/__init__.py +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/demos/demos_utils/data_loading_component.py +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/demos/demos_utils/embed_documents_component.py +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/demos/demos_utils/icons.py +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/demos/demos_utils/messages.py +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/demos/demos_utils/parameters_component.py +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/demos/demos_utils/state_utils.py +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/demos/start_summarizer_app.sh +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/demos/start_topic_analysis_app.sh +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/demos/start_weak_signals_app.sh +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/demos/summarization/__init__.py +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/demos/summarization/summarizer_app.py +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/demos/topic_analysis/__init__.py +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/demos/topic_analysis/app.py +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/demos/topic_analysis/app_utils.py +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/demos/topic_analysis/demo_pages/__init__.py +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/demos/topic_analysis/demo_pages/explore_topics.py +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/demos/topic_analysis/demo_pages/newsletters_generation.py +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/demos/topic_analysis/demo_pages/temporal_visualizations.py +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/demos/topic_analysis/demo_pages/topic_visualizations.py +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/demos/topic_analysis/demo_pages/training_page.py +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/demos/topic_analysis/messages.py +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/demos/weak_signals/__init__.py +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/demos/weak_signals/app.py +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/demos/weak_signals/messages.py +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/demos/weak_signals/visualizations_utils.py +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/llm_utils/__init__.py +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/llm_utils/newsletter.css +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/llm_utils/openai_client.py +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/llm_utils/prompts.py +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/metrics/__init__.py +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/metrics/temporal_metrics_embedding.py +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/metrics/topic_metrics.py +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/resources/common_ngrams.json +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/resources/stopwords-en.json +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/resources/stopwords-fr.json +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/resources/stopwords-rte.json +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/services/__init__.py +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/services/authentication.py +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/services/embedding_client.py +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/services/embedding_service.py +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/services/summarizer.py +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/services/summary/__init__.py +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/services/summary/chatgpt_summarizer.py +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/services/summary/extractive_summarizer.py +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/services/summary/lexrank.py +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/services/summary/prompts.py +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/tests/__init__.py +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/tests/test_abstractive_summarizer.py +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/tests/test_bertrend.py +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/tests/test_cache_utils.py +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/tests/test_chatgpt_summarizer.py +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/tests/test_config_utils.py +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/tests/test_data_loading.py +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/tests/test_data_structure.py +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/tests/test_embedding_client.py +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/tests/test_extractive_summarizer.py +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/tests/test_installation.py +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/tests/test_newsletter_features.py +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/tests/test_openai_client.py +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/tests/test_representative_docs.py +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/tests/test_summarizer.py +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/tests/test_temporal_metrics_embedding.py +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/tests/test_topic_description.py +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/tests/test_topic_metrics.py +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/tests/test_topic_model.py +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/tests/test_trend_analysis_data_structure.py +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/tests/test_trend_analysis_prompts.py +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/tests/test_trend_analysis_visualizations.py +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/tests/test_trend_analysis_weak_signals.py +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/tests/test_utils_init.py +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/tests/test_visualizations.py +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/topic_analysis/__init__.py +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/topic_analysis/data_structure.py +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/topic_analysis/prompts.py +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/topic_analysis/topic_description.py +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/topic_analysis/visualizations.py +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/trend_analysis/__init__.py +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/trend_analysis/data_structure.py +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/trend_analysis/prompts.py +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/trend_analysis/signal_llm_template_en.html +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/trend_analysis/signal_llm_template_fr.html +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/trend_analysis/visualizations.py +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/trend_analysis/weak_signals.py +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/utils/__init__.py +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/utils/cache_utils.py +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/utils/config_utils.py +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend/utils/data_loading.py +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/__init__.py +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/common/__init__.py +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/common/crontab_utils.py +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/common/mail_utils.py +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/config/feeds/arxiv_feed.toml +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/config/feeds/decarbonation_feed.toml +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/config/feeds/llm_en_feed.toml +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/config/feeds/llm_feed.toml +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/config/feeds/nlp_feed.toml +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/config/feeds/rte_feed.toml +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/config/gmail_credentials.json +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/config/newsletters/arxiv_newsletter_from_last.toml +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/config/newsletters/arxiv_newsletter_from_scratch.toml +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/config/newsletters/decarbonation_newsletter_from_last.toml +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/config/newsletters/decarbonation_newsletter_from_scratch.toml +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/config/newsletters/llm_en_newsletter_from_last.toml +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/config/newsletters/llm_newsletter_from_last.toml +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/config/newsletters/nlp_newsletter_from_last.toml +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/config/newsletters/rte_newsletter_from_last.toml +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/config/newsletters/rte_newsletter_from_scratch.toml +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/data_provider/__init__.py +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/data_provider/__main__.py +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/data_provider/arxiv_provider.py +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/data_provider/bing_news_provider.py +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/data_provider/curebot_provider.py +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/data_provider/data_provider.py +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/data_provider/google_news_provider.py +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/data_provider/newscatcher_provider.py +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/data_provider/utils.py +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/exploration/__init__.py +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/exploration/curebot/__init__.py +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/exploration/curebot/app.py +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/exploration/curebot/app_utils.py +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/exploration/curebot/config.toml +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/exploration/curebot/import_extraction_article.ipynb +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/exploration/curebot/old_veille_analyse.py +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/exploration/curebot/prompts.py +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/exploration/curebot/start_newsletter_generator.sh +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/exploration/curebot/tabs/tab1.py +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/exploration/curebot/tabs/tab2.py +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/exploration/curebot/tabs/tab3.py +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/exploration/dataviz/Main_page.py +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/exploration/dataviz/__init__.py +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/exploration/dataviz/pages/1_Data_feeds.py +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/exploration/geolocalization/__init__.py +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/exploration/geolocalization/entity_utils.py +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/exploration/geolocalization/spacy/__init__.py +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/exploration/geolocalization/spacy/common_factory.py +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/exploration/geolocalization/spacy/dbpedia.py +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/exploration/geolocalization/spacy/nlp.config +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/exploration/geolocalization/spacy/utils.py +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/install_feeds_and_newsletters.py +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/newsletters/__init__.py +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/prospective_demo/.streamlit/config.toml +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/prospective_demo/.streamlit/secrets.toml +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/prospective_demo/__init__.py +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/prospective_demo/app.py +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/prospective_demo/authentication.py +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/prospective_demo/dashboard_analysis.py +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/prospective_demo/dashboard_common.py +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/prospective_demo/dashboard_signals.py +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/prospective_demo/feeds_common.py +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/prospective_demo/feeds_config.py +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/prospective_demo/feeds_data.py +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/prospective_demo/i18n.py +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/prospective_demo/llm_utils.py +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/prospective_demo/models_info.py +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/prospective_demo/perf_utils.py +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/prospective_demo/process_new_data.py +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/prospective_demo/report_generation.py +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/prospective_demo/report_template.html +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/prospective_demo/report_utils.py +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/prospective_demo/start_demo.sh +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/prospective_demo/streamlit_utils.py +0 -0
- {bertrend-0.3.1 → bertrend-0.3.2}/bertrend_apps/start_apps.sh +0 -0
- {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.
|
|
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 (
|
|
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
|
[](https://github.com/rte-france/BERTrend "Go to GitHub repo")
|
|
76
|
-

|
|
77
76
|
[](https://github.com/rte-france/BERTrend)
|
|
78
77
|
[](https://github.com/rte-france/BERTrend)
|
|
79
78
|
[](https://github.com/rte-france/BERTrend/issues)
|
|
80
|
-
](https://pepy.tech/project/bertrend)
|
|
80
|
+
[](https://pypi.org/project/bertrend/)
|
|
81
|
+
[](https://pypi.org/project/bertrend/)
|
|
82
|
+

|
|
81
83
|
|
|
82
84
|

|
|
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
|
[](https://github.com/rte-france/BERTrend "Go to GitHub repo")
|
|
4
|
-

|
|
5
4
|
[](https://github.com/rte-france/BERTrend)
|
|
6
5
|
[](https://github.com/rte-france/BERTrend)
|
|
7
6
|
[](https://github.com/rte-france/BERTrend/issues)
|
|
8
|
-
](https://pepy.tech/project/bertrend)
|
|
8
|
+
[](https://pypi.org/project/bertrend/)
|
|
9
|
+
[](https://pypi.org/project/bertrend/)
|
|
10
|
+

|
|
9
11
|
|
|
10
12
|

|
|
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)}")
|