bertrend 0.3.13__tar.gz → 0.3.14__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 (216) hide show
  1. {bertrend-0.3.13 → bertrend-0.3.14}/PKG-INFO +10 -4
  2. {bertrend-0.3.13 → bertrend-0.3.14}/README.md +2 -0
  3. bertrend-0.3.14/bertrend/article_scoring/article_scoring.py +326 -0
  4. bertrend-0.3.14/bertrend/article_scoring/prompts.py +143 -0
  5. bertrend-0.3.14/bertrend/article_scoring/scoring_agent.py +55 -0
  6. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend/demos/demos_utils/icons.py +1 -0
  7. bertrend-0.3.14/bertrend/demos/streamlit_components/__init__.py +0 -0
  8. bertrend-0.3.14/bertrend/demos/streamlit_components/input_with_pills_components.py +83 -0
  9. bertrend-0.3.14/bertrend/llm_utils/agent_utils.py +391 -0
  10. bertrend-0.3.14/bertrend/tests/test_agent_utils.py +443 -0
  11. bertrend-0.3.14/bertrend/tests/test_article_scoring.py +544 -0
  12. bertrend-0.3.14/bertrend/tests/test_article_scoring_models.py +389 -0
  13. bertrend-0.3.14/bertrend/tests/test_mail_utils.py +468 -0
  14. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend_apps/common/mail_utils.py +5 -5
  15. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend_apps/config/feeds/decarbonation_feed.toml +3 -0
  16. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend_apps/config/feeds/nlp_feed.toml +3 -0
  17. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend_apps/data_provider/__main__.py +23 -1
  18. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend_apps/data_provider/data_provider.py +41 -2
  19. bertrend-0.3.14/bertrend_apps/newsletters/__init__.py +4 -0
  20. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend_apps/newsletters/__main__.py +7 -1
  21. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend_apps/prospective_demo/dashboard_common.py +1 -1
  22. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend_apps/prospective_demo/detailed_report_template.html +2 -2
  23. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend_apps/prospective_demo/feeds_config.py +39 -1
  24. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend_apps/prospective_demo/i18n_translations.py +18 -2
  25. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend_apps/prospective_demo/models_info.py +11 -6
  26. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend_apps/prospective_demo/report_generation.py +9 -4
  27. {bertrend-0.3.13 → bertrend-0.3.14}/pyproject.toml +5 -3
  28. bertrend-0.3.13/bertrend_apps/config/gmail_credentials.json +0 -1
  29. {bertrend-0.3.13 → bertrend-0.3.14}/AUTHORS.txt +0 -0
  30. {bertrend-0.3.13 → bertrend-0.3.14}/LICENSE.md +0 -0
  31. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend/BERTopicModel.py +0 -0
  32. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend/BERTrend.py +0 -0
  33. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend/__init__.py +0 -0
  34. {bertrend-0.3.13/bertrend_apps/newsletters → bertrend-0.3.14/bertrend/article_scoring}/__init__.py +0 -0
  35. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend/config/bertopic_default_config.toml +0 -0
  36. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend/config/bertrend_default_config.toml +0 -0
  37. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend/config/parameters.py +0 -0
  38. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend/config/services_default_config.toml +0 -0
  39. {bertrend-0.3.13/bertrend_apps/exploration/curebot → bertrend-0.3.14/bertrend/demos}/__init__.py +0 -0
  40. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend/demos/demos_utils/__init__.py +0 -0
  41. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend/demos/demos_utils/data_loading_component.py +0 -0
  42. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend/demos/demos_utils/embed_documents_component.py +0 -0
  43. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend/demos/demos_utils/i18n.py +0 -0
  44. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend/demos/demos_utils/i18n_translations.py +0 -0
  45. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend/demos/demos_utils/parameters_component.py +0 -0
  46. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend/demos/demos_utils/state_utils.py +0 -0
  47. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend/demos/start_summarizer_app.sh +0 -0
  48. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend/demos/start_topic_analysis_app.sh +0 -0
  49. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend/demos/start_weak_signals_app.sh +0 -0
  50. /bertrend-0.3.13/bertrend_apps/prospective_demo/streamlit_utils.py → /bertrend-0.3.14/bertrend/demos/streamlit_components/clickable_df_component.py +0 -0
  51. {bertrend-0.3.13/bertrend_apps/exploration → bertrend-0.3.14/bertrend/demos/summarization}/__init__.py +0 -0
  52. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend/demos/summarization/summarizer_app.py +0 -0
  53. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend/demos/topic_analysis/.streamlit/config.toml +0 -0
  54. {bertrend-0.3.13/bertrend_apps/common → bertrend-0.3.14/bertrend/demos/topic_analysis}/__init__.py +0 -0
  55. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend/demos/topic_analysis/app.py +0 -0
  56. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend/demos/topic_analysis/app_utils.py +0 -0
  57. {bertrend-0.3.13/bertrend_apps → bertrend-0.3.14/bertrend/demos/topic_analysis/demo_pages}/__init__.py +0 -0
  58. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend/demos/topic_analysis/demo_pages/explore_topics.py +0 -0
  59. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend/demos/topic_analysis/demo_pages/newsletters_generation.py +0 -0
  60. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend/demos/topic_analysis/demo_pages/temporal_visualizations.py +0 -0
  61. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend/demos/topic_analysis/demo_pages/topic_visualizations.py +0 -0
  62. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend/demos/topic_analysis/demo_pages/training_page.py +0 -0
  63. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend/demos/topic_analysis/i18n_translations.py +0 -0
  64. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend/demos/weak_signals/.streamlit/config.toml +0 -0
  65. {bertrend-0.3.13/bertrend/trend_analysis → bertrend-0.3.14/bertrend/demos/weak_signals}/__init__.py +0 -0
  66. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend/demos/weak_signals/app.py +0 -0
  67. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend/demos/weak_signals/i18n_translations.py +0 -0
  68. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend/demos/weak_signals/visualizations_utils.py +0 -0
  69. {bertrend-0.3.13/bertrend/topic_analysis → bertrend-0.3.14/bertrend/llm_utils}/__init__.py +0 -0
  70. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend/llm_utils/newsletter.css +0 -0
  71. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend/llm_utils/newsletter_features.py +0 -0
  72. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend/llm_utils/newsletter_model.py +0 -0
  73. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend/llm_utils/newsletter_outlook_template.html +0 -0
  74. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend/llm_utils/newsletter_template.html +0 -0
  75. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend/llm_utils/openai_client.py +0 -0
  76. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend/llm_utils/prompts.py +0 -0
  77. {bertrend-0.3.13/bertrend/tests → bertrend-0.3.14/bertrend/metrics}/__init__.py +0 -0
  78. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend/metrics/temporal_metrics_embedding.py +0 -0
  79. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend/metrics/topic_metrics.py +0 -0
  80. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend/resources/common_ngrams.json +0 -0
  81. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend/resources/stopwords-en.json +0 -0
  82. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend/resources/stopwords-fr.json +0 -0
  83. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend/resources/stopwords-rte.json +0 -0
  84. {bertrend-0.3.13/bertrend/services/summary → bertrend-0.3.14/bertrend/services}/__init__.py +0 -0
  85. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend/services/authentication.py +0 -0
  86. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend/services/embedding_client.py +0 -0
  87. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend/services/embedding_server/README.md +0 -0
  88. {bertrend-0.3.13/bertrend/services/embedding_server/routers → bertrend-0.3.14/bertrend/services/embedding_server}/__init__.py +0 -0
  89. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend/services/embedding_server/bertrend_client_registry.json +0 -0
  90. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend/services/embedding_server/config/default_config.toml +0 -0
  91. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend/services/embedding_server/config/settings.py +0 -0
  92. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend/services/embedding_server/main.py +0 -0
  93. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend/services/embedding_server/models.py +0 -0
  94. {bertrend-0.3.13/bertrend/services/embedding_server → bertrend-0.3.14/bertrend/services/embedding_server/routers}/__init__.py +0 -0
  95. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend/services/embedding_server/routers/authentication.py +0 -0
  96. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend/services/embedding_server/routers/embeddings.py +0 -0
  97. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend/services/embedding_server/routers/info.py +0 -0
  98. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend/services/embedding_server/security.py +0 -0
  99. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend/services/embedding_server/start.py +0 -0
  100. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend/services/embedding_server/status.py +0 -0
  101. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend/services/embedding_server/stop.py +0 -0
  102. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend/services/embedding_service.py +0 -0
  103. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend/services/summarizer.py +0 -0
  104. {bertrend-0.3.13/bertrend/services → bertrend-0.3.14/bertrend/services/summary}/__init__.py +0 -0
  105. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend/services/summary/abstractive_summarizer.py +0 -0
  106. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend/services/summary/chatgpt_summarizer.py +0 -0
  107. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend/services/summary/extractive_summarizer.py +0 -0
  108. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend/services/summary/lexrank.py +0 -0
  109. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend/services/summary/prompts.py +0 -0
  110. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend/tests/README_integration_tests.md +0 -0
  111. {bertrend-0.3.13/bertrend/metrics → bertrend-0.3.14/bertrend/tests}/__init__.py +0 -0
  112. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend/tests/conftest.py +0 -0
  113. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend/tests/test_abstractive_summarizer.py +0 -0
  114. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend/tests/test_bertrend.py +0 -0
  115. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend/tests/test_cache_utils.py +0 -0
  116. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend/tests/test_chatgpt_summarizer.py +0 -0
  117. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend/tests/test_config_utils.py +0 -0
  118. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend/tests/test_data_loading.py +0 -0
  119. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend/tests/test_data_structure.py +0 -0
  120. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend/tests/test_embedding_client.py +0 -0
  121. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend/tests/test_extractive_summarizer.py +0 -0
  122. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend/tests/test_installation.py +0 -0
  123. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend/tests/test_integration.py +0 -0
  124. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend/tests/test_newsletter_features.py +0 -0
  125. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend/tests/test_newsletter_model.py +0 -0
  126. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend/tests/test_openai_client.py +0 -0
  127. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend/tests/test_representative_docs.py +0 -0
  128. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend/tests/test_summarizer.py +0 -0
  129. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend/tests/test_temporal_metrics_embedding.py +0 -0
  130. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend/tests/test_topic_description.py +0 -0
  131. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend/tests/test_topic_metrics.py +0 -0
  132. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend/tests/test_topic_model.py +0 -0
  133. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend/tests/test_trend_analysis_data_structure.py +0 -0
  134. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend/tests/test_trend_analysis_prompts.py +0 -0
  135. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend/tests/test_trend_analysis_visualizations.py +0 -0
  136. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend/tests/test_trend_analysis_weak_signals.py +0 -0
  137. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend/tests/test_utils_init.py +0 -0
  138. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend/tests/test_visualizations.py +0 -0
  139. {bertrend-0.3.13/bertrend/llm_utils → bertrend-0.3.14/bertrend/topic_analysis}/__init__.py +0 -0
  140. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend/topic_analysis/data_structure.py +0 -0
  141. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend/topic_analysis/prompts.py +0 -0
  142. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend/topic_analysis/representative_docs.py +0 -0
  143. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend/topic_analysis/topic_description.py +0 -0
  144. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend/topic_analysis/visualizations.py +0 -0
  145. {bertrend-0.3.13/bertrend/demos/weak_signals → bertrend-0.3.14/bertrend/trend_analysis}/__init__.py +0 -0
  146. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend/trend_analysis/data_structure.py +0 -0
  147. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend/trend_analysis/prompts.py +0 -0
  148. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend/trend_analysis/signal_llm_template_en.html +0 -0
  149. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend/trend_analysis/signal_llm_template_fr.html +0 -0
  150. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend/trend_analysis/visualizations.py +0 -0
  151. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend/trend_analysis/weak_signals.py +0 -0
  152. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend/utils/__init__.py +0 -0
  153. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend/utils/cache_utils.py +0 -0
  154. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend/utils/config_utils.py +0 -0
  155. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend/utils/data_loading.py +0 -0
  156. {bertrend-0.3.13/bertrend/demos/topic_analysis/demo_pages → bertrend-0.3.14/bertrend_apps}/__init__.py +0 -0
  157. {bertrend-0.3.13/bertrend/demos/topic_analysis → bertrend-0.3.14/bertrend_apps/common}/__init__.py +0 -0
  158. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend_apps/common/crontab_utils.py +0 -0
  159. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend_apps/config/feeds/arxiv_feed.toml +0 -0
  160. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend_apps/config/feeds/llm_en_feed.toml +0 -0
  161. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend_apps/config/feeds/llm_feed.toml +0 -0
  162. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend_apps/config/feeds/rte_feed.toml +0 -0
  163. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend_apps/config/newsletters/arxiv_newsletter_from_last.toml +0 -0
  164. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend_apps/config/newsletters/decarbonation_newsletter_from_last.toml +0 -0
  165. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend_apps/config/newsletters/decarbonation_newsletter_from_scratch.toml +0 -0
  166. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend_apps/config/newsletters/llm_en_newsletter_from_last.toml +0 -0
  167. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend_apps/config/newsletters/llm_newsletter_from_last.toml +0 -0
  168. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend_apps/config/newsletters/nlp_newsletter_from_last.toml +0 -0
  169. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend_apps/config/newsletters/rte_newsletter_from_last.toml +0 -0
  170. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend_apps/config/newsletters/rte_newsletter_from_scratch.toml +0 -0
  171. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend_apps/data_provider/__init__.py +0 -0
  172. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend_apps/data_provider/arxiv_provider.py +0 -0
  173. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend_apps/data_provider/atom_feed_provider.py +0 -0
  174. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend_apps/data_provider/bing_news_provider.py +0 -0
  175. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend_apps/data_provider/google_news_provider.py +0 -0
  176. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend_apps/data_provider/newscatcher_provider.py +0 -0
  177. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend_apps/data_provider/utils.py +0 -0
  178. {bertrend-0.3.13/bertrend/demos/summarization → bertrend-0.3.14/bertrend_apps/exploration}/__init__.py +0 -0
  179. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend_apps/exploration/curebot/.streamlit/config.toml +0 -0
  180. {bertrend-0.3.13/bertrend/demos → bertrend-0.3.14/bertrend_apps/exploration/curebot}/__init__.py +0 -0
  181. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend_apps/exploration/curebot/app.py +0 -0
  182. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend_apps/exploration/curebot/app_utils.py +0 -0
  183. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend_apps/exploration/curebot/config.toml +0 -0
  184. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend_apps/exploration/curebot/import_extraction_article.ipynb +0 -0
  185. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend_apps/exploration/curebot/prompts.py +0 -0
  186. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend_apps/exploration/curebot/start_newsletter_generator.sh +0 -0
  187. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend_apps/exploration/curebot/tabs/tab1.py +0 -0
  188. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend_apps/exploration/curebot/tabs/tab2.py +0 -0
  189. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend_apps/exploration/curebot/tabs/tab3.py +0 -0
  190. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend_apps/exploration/dataviz/Main_page.py +0 -0
  191. {bertrend-0.3.13/bertrend_apps/exploration/geolocalization/spacy → bertrend-0.3.14/bertrend_apps/exploration/dataviz}/__init__.py +0 -0
  192. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend_apps/exploration/dataviz/pages/1_Data_feeds.py +0 -0
  193. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend_apps/exploration/geolocalization/__init__.py +0 -0
  194. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend_apps/exploration/geolocalization/entity_utils.py +0 -0
  195. {bertrend-0.3.13/bertrend_apps/exploration/dataviz → bertrend-0.3.14/bertrend_apps/exploration/geolocalization/spacy}/__init__.py +0 -0
  196. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend_apps/exploration/geolocalization/spacy/common_factory.py +0 -0
  197. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend_apps/exploration/geolocalization/spacy/dbpedia.py +0 -0
  198. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend_apps/exploration/geolocalization/spacy/nlp.config +0 -0
  199. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend_apps/exploration/geolocalization/spacy/utils.py +0 -0
  200. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend_apps/install_feeds_and_newsletters.py +0 -0
  201. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend_apps/prospective_demo/.streamlit/config.toml +0 -0
  202. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend_apps/prospective_demo/.streamlit/secrets.toml +0 -0
  203. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend_apps/prospective_demo/__init__.py +0 -0
  204. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend_apps/prospective_demo/app.py +0 -0
  205. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend_apps/prospective_demo/authentication.py +0 -0
  206. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend_apps/prospective_demo/dashboard_analysis.py +0 -0
  207. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend_apps/prospective_demo/dashboard_signals.py +0 -0
  208. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend_apps/prospective_demo/data_model.py +0 -0
  209. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend_apps/prospective_demo/feeds_common.py +0 -0
  210. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend_apps/prospective_demo/feeds_data.py +0 -0
  211. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend_apps/prospective_demo/llm_utils.py +0 -0
  212. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend_apps/prospective_demo/perf_utils.py +0 -0
  213. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend_apps/prospective_demo/process_new_data.py +0 -0
  214. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend_apps/prospective_demo/start_demo.sh +0 -0
  215. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend_apps/start_apps.sh +0 -0
  216. {bertrend-0.3.13 → bertrend-0.3.14}/bertrend_apps/stop_apps.sh +0 -0
@@ -1,8 +1,10 @@
1
- Metadata-Version: 2.3
1
+ Metadata-Version: 2.4
2
2
  Name: bertrend
3
- Version: 0.3.13
3
+ Version: 0.3.14
4
4
  Summary: BERTrend analyses topic evolution over time using state-of-the-art transformer models: topic modelling and LLM
5
5
  License: LICENSE.md
6
+ License-File: AUTHORS.txt
7
+ License-File: LICENSE.md
6
8
  Keywords: topics,BERTopic,transformers,weak signals,LLM
7
9
  Author: Jérôme Picault
8
10
  Author-email: jerome.picault@rte-france.com
@@ -42,14 +44,17 @@ Requires-Dist: newspaper4k (>=0.9.3.1) ; extra == "apps"
42
44
  Requires-Dist: nltk (>=3.9.1)
43
45
  Requires-Dist: numpy (<2)
44
46
  Requires-Dist: openai (>=1.77.0)
47
+ Requires-Dist: openai-agents[litellm] (>=0.2.5)
45
48
  Requires-Dist: openpyxl (>=3.1.5)
46
49
  Requires-Dist: opentelemetry-exporter-otlp-proto-grpc (==1.25.0)
47
50
  Requires-Dist: pandas (>=2.2.2)
48
51
  Requires-Dist: plotly (>=5.24.1)
49
52
  Requires-Dist: plotly-resampler (>=0.10.0)
53
+ Requires-Dist: pyarrow (>=21.0.0)
50
54
  Requires-Dist: pygooglenews (>=0.1.3) ; extra == "apps"
51
55
  Requires-Dist: pylabeladjust (==0.1.13)
52
56
  Requires-Dist: pytest (>=8.4.0)
57
+ Requires-Dist: pytest-asyncio
53
58
  Requires-Dist: requests (>=2.32.3)
54
59
  Requires-Dist: scikit-learn (>=1.5.2)
55
60
  Requires-Dist: scipy (==1.12)
@@ -57,9 +62,8 @@ Requires-Dist: seaborn (>=0.13.2)
57
62
  Requires-Dist: sentence-transformers (==3.0.1)
58
63
  Requires-Dist: sentencepiece (>=0.2.0)
59
64
  Requires-Dist: starlette (>=0.46.2)
60
- Requires-Dist: streamlit (>=1.46)
65
+ Requires-Dist: streamlit (>=1.48)
61
66
  Requires-Dist: streamlit-code-editor (>=0.1.22)
62
- Requires-Dist: streamlit-tags
63
67
  Requires-Dist: supervisor (>=4.2.5)
64
68
  Requires-Dist: thefuzz (>=0.22.1)
65
69
  Requires-Dist: tiktoken (>=0.8.0)
@@ -229,6 +233,8 @@ Recommended public datasets:
229
233
 
230
234
  For custom dataset creation, please refer to the documentation page about [data providers](./docs/data_provider.md).
231
235
 
236
+ For email configuration and newsletter sending (optional), please refer to the [mail configuration guide](./docs/mail_configuration.md).
237
+
232
238
 
233
239
  In order to use the provided demos, datasets :
234
240
  - can be stored on the server in: `$BERTREND_BASE_DIR/data/bertrend/`
@@ -152,6 +152,8 @@ Recommended public datasets:
152
152
 
153
153
  For custom dataset creation, please refer to the documentation page about [data providers](./docs/data_provider.md).
154
154
 
155
+ For email configuration and newsletter sending (optional), please refer to the [mail configuration guide](./docs/mail_configuration.md).
156
+
155
157
 
156
158
  In order to use the provided demos, datasets :
157
159
  - can be stored on the server in: `$BERTREND_BASE_DIR/data/bertrend/`
@@ -0,0 +1,326 @@
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 json
6
+ from functools import total_ordering
7
+
8
+ from pydantic import (
9
+ BaseModel,
10
+ Field,
11
+ model_validator,
12
+ ConfigDict,
13
+ computed_field,
14
+ model_serializer,
15
+ )
16
+ from typing import Optional, List, Dict, Any
17
+ from enum import Enum
18
+
19
+
20
+ @total_ordering
21
+ class QualityLevel(Enum):
22
+ """Quality level categories based on score ranges"""
23
+
24
+ POOR = "Poor" # (0.0-0.25)
25
+ FAIR = "Fair" # (0.25-0.50)
26
+ AVERAGE = "Average" # (0.50-0.65)
27
+ GOOD = "Good" # (0.65-0.8)
28
+ EXCELLENT = "Excellent" # (0.8-1.0)
29
+
30
+ def __lt__(self, other):
31
+ if not isinstance(other, QualityLevel):
32
+ return NotImplemented
33
+ return self.index < other.index
34
+
35
+ def __eq__(self, other):
36
+ if not isinstance(other, QualityLevel):
37
+ return NotImplemented
38
+ return self.name == other.name
39
+
40
+ @classmethod
41
+ def from_string(cls, value):
42
+ """Create a QualityLevel from a string (case-insensitive)"""
43
+ if isinstance(value, cls):
44
+ return value
45
+
46
+ value_lower = str(value).lower().strip()
47
+ for level in cls:
48
+ if level.value.lower() == value_lower:
49
+ return level
50
+
51
+ valid_values = [level.value for level in cls]
52
+ raise ValueError(
53
+ f"'{value}' is not a valid QualityLevel. Valid values are: {valid_values}"
54
+ )
55
+
56
+ @property
57
+ def index(self):
58
+ """Get the index (position) of this QualityLevel in the enum definition order"""
59
+ return list(QualityLevel).index(self)
60
+
61
+
62
+ class CriteriaScores(BaseModel):
63
+ """Individual scores for each quality criterion"""
64
+
65
+ model_config = ConfigDict(validate_assignment=True, extra="forbid")
66
+
67
+ depth_of_reporting: float = Field(
68
+ ...,
69
+ ge=0.0,
70
+ le=1.0,
71
+ description="How well the article provides background, context, explanations, and connects to broader trends",
72
+ )
73
+ originality_and_exclusivity: float = Field(
74
+ ...,
75
+ ge=0.0,
76
+ le=1.0,
77
+ description="Unique information, exclusive insights, original reporting, or fresh perspectives",
78
+ )
79
+ source_quality_and_transparency: float = Field(
80
+ ...,
81
+ ge=0.0,
82
+ le=1.0,
83
+ description="Quality and transparency of sources, credibility, independence, and conflict disclosure",
84
+ )
85
+ accuracy_and_fact_checking_rigor: float = Field(
86
+ ...,
87
+ ge=0.0,
88
+ le=1.0,
89
+ description="Precision, verifiability, appropriate evidence strength, and error handling",
90
+ )
91
+ clarity_and_accessibility: float = Field(
92
+ ...,
93
+ ge=0.0,
94
+ le=1.0,
95
+ description="Clear communication, logical hierarchy, and accessibility for target audience",
96
+ )
97
+ balance_and_fairness: float = Field(
98
+ ...,
99
+ ge=0.0,
100
+ le=1.0,
101
+ description="Fair presentation of viewpoints, absence of bias, acknowledgment of uncertainties",
102
+ )
103
+ narrative_and_engagement: float = Field(
104
+ ...,
105
+ ge=0.0,
106
+ le=1.0,
107
+ description="Reader engagement, storytelling effectiveness, and balance with journalistic integrity",
108
+ )
109
+ timeliness_and_relevance: float = Field(
110
+ ...,
111
+ ge=0.0,
112
+ le=1.0,
113
+ description="Currency of information, relevance to current context, and appropriate timing",
114
+ )
115
+ ethical_considerations_and_sensitivity: float = Field(
116
+ ...,
117
+ ge=0.0,
118
+ le=1.0,
119
+ description="Ethical reporting standards, sensitivity to affected populations, avoiding sensationalism",
120
+ )
121
+ rte_relevance_and_strategic_impact: float = Field(
122
+ ...,
123
+ ge=0.0,
124
+ le=1.0,
125
+ description="Relevance to RTE operations, energy transmission, grid management, and strategic planning",
126
+ )
127
+
128
+ @classmethod
129
+ def get_criterion_names(cls) -> List[str]:
130
+ """Get list of all criterion names"""
131
+ return list(cls.model_fields.keys())
132
+
133
+ def to_dict(self) -> Dict[str, float]:
134
+ """Convert scores to dictionary"""
135
+ return {name: getattr(self, name) for name in self.get_criterion_names()}
136
+
137
+
138
+ class WeightConfig(BaseModel):
139
+ """Configurable weights for each criterion"""
140
+
141
+ model_config = ConfigDict(validate_assignment=True, extra="forbid")
142
+ depth_of_reporting: float = Field(0.15, ge=0.0, le=1.0)
143
+ originality_and_exclusivity: float = Field(0.10, ge=0.0, le=1.0)
144
+ source_quality_and_transparency: float = Field(0.15, ge=0.0, le=1.0)
145
+ accuracy_and_fact_checking_rigor: float = Field(0.15, ge=0.0, le=1.0)
146
+ clarity_and_accessibility: float = Field(0.10, ge=0.0, le=1.0)
147
+ balance_and_fairness: float = Field(0.10, ge=0.0, le=1.0)
148
+ narrative_and_engagement: float = Field(0.10, ge=0.0, le=1.0)
149
+ timeliness_and_relevance: float = Field(0.05, ge=0.0, le=1.0)
150
+ ethical_considerations_and_sensitivity: float = Field(0.05, ge=0.0, le=1.0)
151
+ rte_relevance_and_strategic_impact: float = Field(0.05, ge=0.0, le=1.0)
152
+
153
+ @model_validator(mode="after")
154
+ def weights_validator(self) -> "WeightConfig":
155
+ """Ensure all weights sum to less than the number of criteria"""
156
+ total = sum(
157
+ getattr(self, name) for name in CriteriaScores.get_criterion_names()
158
+ )
159
+ if not total <= len(CriteriaScores.get_criterion_names()):
160
+ raise ValueError(
161
+ f"Weights must not exceed {len(CriteriaScores.get_criterion_names())}, got {total:.6f}"
162
+ )
163
+ return self
164
+
165
+ def to_dict(self) -> Dict[str, float]:
166
+ """Convert weights to dictionary"""
167
+ return {
168
+ name: getattr(self, name) for name in CriteriaScores.get_criterion_names()
169
+ }
170
+
171
+
172
+ class ArticleScore(BaseModel):
173
+ """Complete article scoring with individual scores and final result"""
174
+
175
+ model_config = ConfigDict(
176
+ validate_assignment=True,
177
+ extra="forbid",
178
+ arbitrary_types_allowed=True,
179
+ use_enum_values=True, # to serialize enum values
180
+ )
181
+
182
+ # Required fields
183
+ scores: CriteriaScores
184
+
185
+ # Optional configuration
186
+ weights: WeightConfig = Field(default_factory=WeightConfig)
187
+
188
+ # Assessment metadata
189
+ assessment_summary: Optional[str] = Field(
190
+ None, description="Brief summary of strengths and weaknesses"
191
+ )
192
+
193
+ @computed_field
194
+ @property
195
+ def final_score(self) -> float:
196
+ """Calculate weighted final score"""
197
+ score_dict = self.scores.to_dict()
198
+ weight_dict = self.weights.to_dict()
199
+
200
+ weighted_sum = sum(
201
+ score_dict[name] * weight_dict[name] for name in score_dict.keys()
202
+ )
203
+ return round(weighted_sum, 3) / len(score_dict)
204
+
205
+ @computed_field
206
+ @property
207
+ def quality_level(self) -> QualityLevel:
208
+ """Determine quality level based on final score"""
209
+ score = self.final_score
210
+ if 0.0 <= score < 0.25:
211
+ return QualityLevel.POOR
212
+ elif 0.25 <= score < 0.50:
213
+ return QualityLevel.FAIR
214
+ elif 0.5 <= score < 0.65:
215
+ return QualityLevel.AVERAGE
216
+ elif 0.65 <= score < 0.8:
217
+ return QualityLevel.GOOD
218
+ else: # 0.8 <= score <= 1.0
219
+ return QualityLevel.EXCELLENT
220
+
221
+ def get_detailed_breakdown(self) -> Dict[str, Dict[str, float]]:
222
+ """Get detailed score breakdown with weights and contributions"""
223
+ score_dict = self.scores.to_dict()
224
+ weight_dict = self.weights.to_dict()
225
+
226
+ return {
227
+ criterion: {
228
+ "score": score_dict[criterion],
229
+ "weight": weight_dict[criterion],
230
+ "weighted_contribution": round(
231
+ score_dict[criterion] * weight_dict[criterion], 3
232
+ ),
233
+ }
234
+ for criterion in score_dict.keys()
235
+ }
236
+
237
+ def get_top_strengths(self, n: int = 3) -> List[tuple[str, float]]:
238
+ """Get top N scoring criteria"""
239
+ score_dict = self.scores.to_dict()
240
+ sorted_scores = sorted(score_dict.items(), key=lambda x: x[1], reverse=True)
241
+ return sorted_scores[:n]
242
+
243
+ def get_top_weaknesses(self, n: int = 3) -> List[tuple[str, float]]:
244
+ """Get bottom N scoring criteria"""
245
+ score_dict = self.scores.to_dict()
246
+ sorted_scores = sorted(score_dict.items(), key=lambda x: x[1])
247
+ return sorted_scores[:n]
248
+
249
+ def to_report(self, include_breakdown: bool = True) -> str:
250
+ """Generate a formatted assessment report"""
251
+ report_lines = [
252
+ "ARTICLE QUALITY ASSESSMENT REPORT",
253
+ "=" * 50,
254
+ "",
255
+ f"FINAL SCORE: {self.final_score}/1.0 ({self.quality_level.value})",
256
+ "",
257
+ ]
258
+
259
+ if include_breakdown:
260
+ report_lines.append("INDIVIDUAL SCORES:")
261
+ breakdown = self.get_detailed_breakdown()
262
+ for i, (criterion, data) in enumerate(breakdown.items(), 1):
263
+ criterion_name = criterion.replace("_", " ").title()
264
+ report_lines.append(
265
+ f"{i:2d}. {criterion_name}: {data['score']:.2f}/1.0 "
266
+ f"(Weight: {data['weight']:.0%}, Contribution: {data['weighted_contribution']:.3f})"
267
+ )
268
+ report_lines.append("")
269
+
270
+ # Add strengths and weaknesses
271
+ strengths = self.get_top_strengths(3)
272
+ weaknesses = self.get_top_weaknesses(3)
273
+
274
+ report_lines.extend(
275
+ [
276
+ "TOP STRENGTHS:",
277
+ *[
278
+ f"• {name.replace('_', ' ').title()}: {score:.2f}"
279
+ for name, score in strengths
280
+ ],
281
+ "",
282
+ "AREAS FOR IMPROVEMENT:",
283
+ *[
284
+ f"• {name.replace('_', ' ').title()}: {score:.2f}"
285
+ for name, score in weaknesses
286
+ ],
287
+ "",
288
+ ]
289
+ )
290
+
291
+ if self.assessment_summary:
292
+ report_lines.extend(
293
+ [
294
+ "OVERALL ASSESSMENT:",
295
+ self.assessment_summary,
296
+ "",
297
+ ]
298
+ )
299
+
300
+ return "\n".join(report_lines)
301
+
302
+ def export_to_dict(self) -> Dict[str, Any]:
303
+ """Export complete assessment to dictionary for serialization"""
304
+ return {
305
+ "scores": self.scores.model_dump(),
306
+ "weights": self.weights.model_dump(),
307
+ "final_score": self.final_score,
308
+ "quality_level": self.quality_level.value, # Explicitly convert enum to string
309
+ "assessment_summary": self.assessment_summary,
310
+ "detailed_breakdown": self.get_detailed_breakdown(),
311
+ }
312
+
313
+ def to_json(self) -> str:
314
+ """Export to JSON string"""
315
+ return json.dumps(self.export_to_dict(), indent=2)
316
+
317
+ @model_serializer
318
+ def serialize_model(self) -> Dict[str, Any]:
319
+ """Custom serializer to handle QualityLevel enum"""
320
+ return {
321
+ "scores": self.scores.model_dump(),
322
+ "weights": self.weights.model_dump(),
323
+ "final_score": self.final_score,
324
+ "quality_level": self.quality_level.value,
325
+ "assessment_summary": self.assessment_summary,
326
+ }
@@ -0,0 +1,143 @@
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
+ ARTICLE_SCORING_PROMPT = """
7
+ # News Article Quality Assessment Prompt
8
+
9
+ You are an expert news analyst tasked with evaluating the quality of a news article. Please assess the article across 10 key criteria score between 0 (poor) and 1 (excellent).
10
+
11
+ ## Assessment Instructions
12
+
13
+ For each criterion below, assign a score from 0 to 1 based on how well the article meets the specified requirements.
14
+
15
+ ### Scoring Scale:
16
+ - **0.0-0.25**: Poor - Fails to meet basic standards
17
+ - **0.25-0.5**: Fair - Meets some requirements but has significant gaps
18
+ - **0.5-0.65**: Average - Meets most basic requirements
19
+ - **0.65-0.8**: Good - Exceeds expectations in most areas
20
+ - **0.8-1.0**: Excellent - Exceptional quality, sets the standard
21
+
22
+ ## Evaluation Criteria
23
+
24
+ ### 1. Depth of Reporting
25
+ **Score: ___/1.0**
26
+
27
+ Evaluate:
28
+ - How well does the article provide background, context, and explanations?
29
+ - Does it go beyond surface facts to explain why or how?
30
+ - Does it cover implications and consequences?
31
+ - Are expert opinions or analysis included?
32
+ - Does it connect current events to broader trends or historical patterns?
33
+
34
+ ### 2. Originality and Exclusivity
35
+ **Score: ___/1.0**
36
+
37
+ Evaluate:
38
+ - Does the article provide unique information or exclusive insights not found elsewhere?
39
+ - Are there original interviews, data, or on-the-ground reporting?
40
+ - Does it offer a fresh angle or perspective on a familiar story?
41
+
42
+ ### 3. Source Quality and Transparency
43
+ **Score: ___/1.0**
44
+
45
+ Evaluate:
46
+ - Are sources explicitly named and credible (experts, official data, witnesses)?
47
+ - Are multiple, independent sources cited to avoid reliance on a single perspective?
48
+ - Is there a clear distinction between primary and secondary sources?
49
+ - Are potential conflicts of interest disclosed?
50
+
51
+ ### 4. Accuracy and Fact-Checking Rigor
52
+ **Score: ___/1.0**
53
+
54
+ Evaluate:
55
+ - Is every fact precise and verifiable?
56
+ - Are statistics and data presented with references or links?
57
+ - Are potential errors or corrections addressed openly?
58
+ - Are claims substantiated with appropriate evidence strength (avoiding over-interpretation of preliminary data)?
59
+
60
+ ### 5. Clarity and Accessibility
61
+ **Score: ___/1.0**
62
+
63
+ Evaluate:
64
+ - Is complex information broken down clearly for the target audience?
65
+ - Does the article avoid ambiguity or confusion?
66
+ - Are technical terms well defined or minimized?
67
+ - Is the information hierarchy logical and easy to follow?
68
+
69
+ ### 6. Balance and Fairness
70
+ **Score: ___/1.0**
71
+
72
+ Evaluate:
73
+ - Does the article fairly present conflicting viewpoints or relevant nuances?
74
+ - Is it free from hidden biases or agenda-driven language?
75
+ - Does it acknowledge uncertainties and limitations in the information presented?
76
+
77
+ ### 7. Narrative and Engagement
78
+ **Score: ___/1.0**
79
+
80
+ Evaluate:
81
+ - How well does the article maintain reader interest?
82
+ - Does it use storytelling, vivid examples, or quotes effectively?
83
+ - Is the headline catchy but truthful?
84
+ - Does it strike the right balance between engagement and journalistic integrity?
85
+
86
+ ### 8. Timeliness and Relevance
87
+ **Score: ___/1.0**
88
+
89
+ Evaluate:
90
+ - Is the article up-to-date with the latest developments?
91
+ - Does it highlight why the topic matters now?
92
+ - Does it provide appropriate context about what has changed since previous reporting?
93
+
94
+ ### 9. Ethical Considerations and Sensitivity
95
+ **Score: ___/1.0**
96
+
97
+ Evaluate:
98
+ - Does the article avoid sensationalism or fearmongering?
99
+ - Does it respect privacy and avoid harm?
100
+ - Does it consider the potential impact on vulnerable populations or communities mentioned?
101
+
102
+ ### 10. RTE Relevance and Strategic Impact
103
+ **Score: ___/1.0**
104
+
105
+ Evaluate:
106
+ - Does the article address issues directly impacting electricity transmission, grid management, or energy infrastructure?
107
+ - Are there implications for French energy security, grid stability, or electricity market operations?
108
+ - Does it cover regulatory changes, policy decisions, or geopolitical events affecting energy supply chains?
109
+ - Are there connections to renewable energy integration, nuclear policy, or European energy interconnections?
110
+ - Does it address energy transition challenges relevant to transmission system operations?
111
+ - Are there economic, political, or environmental factors that could influence RTE's strategic planning or operations?
112
+
113
+ ## Assessment Output Format
114
+
115
+ Please provide your assessment in the following format:
116
+
117
+ ---
118
+
119
+ **Article Title:** [Insert title]
120
+
121
+ **Individual Scores:**
122
+ 1. Depth of Reporting: ___
123
+ 2. Originality and Exclusivity: ___
124
+ 3. Source Quality and Transparency: ___
125
+ 4. Accuracy and Fact-Checking Rigor: ___
126
+ 5. Clarity and Accessibility: ___
127
+ 6. Balance and Fairness: ___
128
+ 7. Narrative and Engagement: ___
129
+ 8. Timeliness and Relevance: ___
130
+ 9. Ethical Considerations and Sensitivity: ___
131
+ 10. RTE Relevance and Strategic Impact: ___
132
+
133
+ **Overall Assessment:** [Brief summary of strengths and weaknesses]
134
+
135
+ ---
136
+
137
+ ## Important Notes:
138
+ - Consider the article type (breaking news, investigative, opinion, analysis) when evaluating
139
+ - Weight journalistic fundamentals (accuracy, sources, ethics) more heavily than stylistic elements
140
+ - Be objective and evidence-based in your scoring
141
+ - Provide specific examples from the article to support your scores
142
+
143
+ """
@@ -0,0 +1,55 @@
1
+ import asyncio
2
+ from pathlib import Path
3
+
4
+ from agents import ModelSettings
5
+
6
+ from bertrend.article_scoring.article_scoring import ArticleScore
7
+ from bertrend.article_scoring.prompts import ARTICLE_SCORING_PROMPT
8
+ from bertrend.llm_utils.agent_utils import (
9
+ BaseAgentFactory,
10
+ AsyncAgentConcurrentProcessor,
11
+ progress_reporter,
12
+ )
13
+ from bertrend.utils.data_loading import load_data
14
+
15
+ DEFAULT_CHUNK_SIZE = 25
16
+ DEFAULT_MAX_CONCURRENT_TASKS = 25
17
+
18
+
19
+ async def score_articles(articles: list[str]):
20
+ agent = BaseAgentFactory().create_agent(
21
+ name="scoring_agent",
22
+ instructions=ARTICLE_SCORING_PROMPT,
23
+ output_type=ArticleScore,
24
+ model_settings=ModelSettings(temperature=0.1),
25
+ )
26
+
27
+ # Initialize processor
28
+ processor = AsyncAgentConcurrentProcessor(
29
+ agent=agent, max_concurrent=DEFAULT_MAX_CONCURRENT_TASKS
30
+ )
31
+
32
+ # Uncomment to process multiple items:
33
+ results = await processor.process_list_concurrent(
34
+ articles, progress_callback=progress_reporter, chunk_size=DEFAULT_CHUNK_SIZE
35
+ )
36
+ return results
37
+
38
+
39
+ if __name__ == "__main__":
40
+
41
+ path = Path("/DSIA/nlp/bertrend/data/feeds/feed_nlp/2025-01-07_feed_nlp.jsonl")
42
+ df = load_data(path)
43
+ print(len(df), df.columns)
44
+ l = list(df.text)
45
+ results = asyncio.run(score_articles(l))
46
+
47
+ assert len(results) == len(df)
48
+ df["quality_metrics"] = [r.output if not r.error else None for r in results]
49
+ df["overall_quality"] = df["quality_metrics"].apply(
50
+ lambda x: x.quality_level.name if x else None
51
+ )
52
+ print(df.overall_quality)
53
+ category_percentages = df["overall_quality"].value_counts(normalize=True) * 100
54
+ # Display percentages
55
+ print(category_percentages)
@@ -34,6 +34,7 @@ STRONG_SIGNAL_ICON = ":material/signal_cellular_3_bar:"
34
34
  DOWNLOAD_ICON = ":material/download:"
35
35
  EMAIL_ICON = ":material/mail:"
36
36
  RESTART_ICON = ":material/restart_alt:"
37
+ PERSON_ADD_ICON = ":material/person_add:"
37
38
 
38
39
  JSON_ICON = "🧾"
39
40
  PARQUET_ICON = "📦️"
@@ -0,0 +1,83 @@
1
+ import streamlit as st
2
+ from typing import Callable, Optional, List
3
+
4
+ from bertrend.demos.demos_utils.icons import PERSON_ADD_ICON
5
+
6
+
7
+ @st.fragment
8
+ def input_with_pills(
9
+ label: str,
10
+ key: str,
11
+ placeholder: str = "",
12
+ validate_fn: Optional[Callable[[str], bool]] = None,
13
+ label_visibility="hidden",
14
+ ) -> List[str]:
15
+ """
16
+ A simple Streamlit component that auto-adds valid input to pills.
17
+ Dialog-safe version that doesn't use st.rerun()
18
+ """
19
+
20
+ # Initialize session state
21
+ all_items_key = f"{key}_all_items"
22
+ selected_items_key = f"{key}_selected_items"
23
+ input_key = f"{key}_input"
24
+
25
+ if all_items_key not in st.session_state:
26
+ st.session_state[all_items_key] = []
27
+ if selected_items_key not in st.session_state:
28
+ st.session_state[selected_items_key] = []
29
+
30
+ # Create columns: input on left, pills on right
31
+ col1, col2 = st.columns([3, 3])
32
+
33
+ with col1:
34
+ # Create a form to handle Enter key press without rerun
35
+ with st.form(key=f"{key}_form", clear_on_submit=True, border=False):
36
+ current_input = st.text_input(
37
+ label,
38
+ placeholder=placeholder,
39
+ key=input_key,
40
+ label_visibility=label_visibility,
41
+ )
42
+ # Submit button is required but will be hidden
43
+ submitted = st.form_submit_button(PERSON_ADD_ICON)
44
+
45
+ if submitted and current_input.strip():
46
+ is_valid = True
47
+ error_msg = ""
48
+
49
+ if validate_fn and not validate_fn(current_input.strip()):
50
+ is_valid = False
51
+ error_msg = "Invalid format"
52
+ elif current_input.strip() in st.session_state[all_items_key]:
53
+ is_valid = False
54
+ error_msg = "Already exists"
55
+
56
+ if is_valid:
57
+ # Add to both lists
58
+ st.session_state[all_items_key].append(current_input.strip())
59
+ st.session_state[selected_items_key].append(current_input.strip())
60
+ else:
61
+ st.error(error_msg)
62
+
63
+ with col2:
64
+ # Display pills
65
+ if st.session_state[all_items_key]:
66
+ st.markdown("<div style='margin-top: 8px;'></div>", unsafe_allow_html=True)
67
+
68
+ # Use a callback to handle selection changes
69
+ def on_pills_change():
70
+ selected = st.session_state[f"{key}_pills_widget"]
71
+ st.session_state[selected_items_key] = selected
72
+
73
+ selected_pills = st.pills(
74
+ "Items",
75
+ st.session_state[all_items_key],
76
+ selection_mode="multi",
77
+ key=f"{key}_pills_widget",
78
+ label_visibility="collapsed",
79
+ default=st.session_state[selected_items_key],
80
+ on_change=on_pills_change,
81
+ )
82
+
83
+ return st.session_state[selected_items_key]