bertrend 0.4.8__tar.gz → 0.4.9__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 (346) hide show
  1. {bertrend-0.4.8 → bertrend-0.4.9}/PKG-INFO +1 -1
  2. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/bertrend_apps/services/queue_management/bertrend_worker.py +82 -21
  3. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/bertrend_apps/services/queue_management/queue_manager.py +49 -0
  4. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/bertrend_apps/services/queue_management/rabbitmq_config.py +4 -1
  5. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/bertrend_apps/services/queue_monitor/app.py +156 -5
  6. bertrend-0.4.9/bertrend/tests/services/article_scoring/test_article_scoring_server.py +84 -0
  7. bertrend-0.4.9/bertrend/tests/services/queue_management/test_bertrend_worker.py +294 -0
  8. bertrend-0.4.9/bertrend/tests/services/queue_management/test_queue_manager.py +146 -0
  9. bertrend-0.4.9/bertrend/tests/services/summary/__init__.py +4 -0
  10. bertrend-0.4.9/bertrend/tests/utils/__init__.py +4 -0
  11. {bertrend-0.4.8 → bertrend-0.4.9}/pyproject.toml +1 -1
  12. {bertrend-0.4.8 → bertrend-0.4.9}/AUTHORS.txt +0 -0
  13. {bertrend-0.4.8 → bertrend-0.4.9}/LICENSE.md +0 -0
  14. {bertrend-0.4.8 → bertrend-0.4.9}/README.md +0 -0
  15. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/BERTopicModel.py +0 -0
  16. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/BERTrend.py +0 -0
  17. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/__init__.py +0 -0
  18. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/article_scoring/__init__.py +0 -0
  19. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/article_scoring/article_scoring.py +0 -0
  20. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/article_scoring/prompts.py +0 -0
  21. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/article_scoring/scoring_agent.py +0 -0
  22. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/bertrend_apps/__init__.py +0 -0
  23. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/bertrend_apps/common/__init__.py +0 -0
  24. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/bertrend_apps/common/apscheduler_utils.py +0 -0
  25. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/bertrend_apps/common/crontab_utils.py +0 -0
  26. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/bertrend_apps/common/date_utils.py +0 -0
  27. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/bertrend_apps/common/mail_utils.py +0 -0
  28. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/bertrend_apps/common/scheduler_utils.py +0 -0
  29. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/bertrend_apps/config/feeds/arxiv_feed.toml +0 -0
  30. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/bertrend_apps/config/feeds/decarbonation_feed.toml +0 -0
  31. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/bertrend_apps/config/feeds/feed_deep_research_example.toml +0 -0
  32. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/bertrend_apps/config/feeds/llm_en_feed.toml +0 -0
  33. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/bertrend_apps/config/feeds/llm_feed.toml +0 -0
  34. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/bertrend_apps/config/feeds/nlp_feed.toml +0 -0
  35. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/bertrend_apps/config/feeds/rte_feed.toml +0 -0
  36. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/bertrend_apps/config/newsletters/arxiv_newsletter_from_last.toml +0 -0
  37. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/bertrend_apps/config/newsletters/decarbonation_newsletter_from_last.toml +0 -0
  38. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/bertrend_apps/config/newsletters/llm_en_newsletter_from_last.toml +0 -0
  39. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/bertrend_apps/config/newsletters/llm_newsletter_from_last.toml +0 -0
  40. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/bertrend_apps/config/newsletters/nlp_newsletter_from_last.toml +0 -0
  41. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/bertrend_apps/config/newsletters/rte_newsletter_from_last.toml +0 -0
  42. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/bertrend_apps/data_provider/__init__.py +0 -0
  43. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/bertrend_apps/data_provider/__main__.py +0 -0
  44. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/bertrend_apps/data_provider/arxiv_provider.py +0 -0
  45. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/bertrend_apps/data_provider/atom_feed_provider.py +0 -0
  46. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/bertrend_apps/data_provider/bing_news_provider.py +0 -0
  47. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/bertrend_apps/data_provider/data_provider.py +0 -0
  48. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/bertrend_apps/data_provider/data_provider_utils.py +0 -0
  49. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/bertrend_apps/data_provider/deep_research_provider.py +0 -0
  50. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/bertrend_apps/data_provider/google_news_provider.py +0 -0
  51. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/bertrend_apps/data_provider/newscatcher_provider.py +0 -0
  52. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/bertrend_apps/data_provider/rss_feed_provider.py +0 -0
  53. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/bertrend_apps/data_provider/utils.py +0 -0
  54. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/bertrend_apps/exploration/__init__.py +0 -0
  55. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/bertrend_apps/exploration/curebot/.streamlit/config.toml +0 -0
  56. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/bertrend_apps/exploration/curebot/__init__.py +0 -0
  57. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/bertrend_apps/exploration/curebot/app.py +0 -0
  58. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/bertrend_apps/exploration/curebot/app_utils.py +0 -0
  59. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/bertrend_apps/exploration/curebot/config.toml +0 -0
  60. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/bertrend_apps/exploration/curebot/import_extraction_article.ipynb +0 -0
  61. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/bertrend_apps/exploration/curebot/prompts.py +0 -0
  62. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/bertrend_apps/exploration/curebot/start_newsletter_generator.sh +0 -0
  63. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/bertrend_apps/exploration/curebot/tabs/__init__.py +0 -0
  64. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/bertrend_apps/exploration/curebot/tabs/tab1.py +0 -0
  65. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/bertrend_apps/exploration/curebot/tabs/tab2.py +0 -0
  66. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/bertrend_apps/exploration/curebot/tabs/tab3.py +0 -0
  67. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/bertrend_apps/exploration/geoviz/.streamlit/config.toml +0 -0
  68. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/bertrend_apps/exploration/geoviz/__init__.py +0 -0
  69. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/bertrend_apps/exploration/geoviz/app.py +0 -0
  70. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/bertrend_apps/exploration/geoviz/backend.py +0 -0
  71. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/bertrend_apps/exploration/start_apps.sh +0 -0
  72. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/bertrend_apps/exploration/stop_apps.sh +0 -0
  73. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/bertrend_apps/install_feeds_and_newsletters.py +0 -0
  74. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/bertrend_apps/newsletters/__init__.py +0 -0
  75. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/bertrend_apps/newsletters/__main__.py +0 -0
  76. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/bertrend_apps/newsletters/newsletter_generation.py +0 -0
  77. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/bertrend_apps/newsletters/utils.py +0 -0
  78. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/bertrend_apps/prospective_demo/.streamlit/config.toml +0 -0
  79. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/bertrend_apps/prospective_demo/.streamlit/secrets.toml.example +0 -0
  80. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/bertrend_apps/prospective_demo/__init__.py +0 -0
  81. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/bertrend_apps/prospective_demo/app.py +0 -0
  82. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/bertrend_apps/prospective_demo/authentication.py +0 -0
  83. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/bertrend_apps/prospective_demo/automated_report_generation.py +0 -0
  84. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/bertrend_apps/prospective_demo/dashboard_analysis.py +0 -0
  85. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/bertrend_apps/prospective_demo/dashboard_common.py +0 -0
  86. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/bertrend_apps/prospective_demo/dashboard_comparative.py +0 -0
  87. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/bertrend_apps/prospective_demo/dashboard_signals.py +0 -0
  88. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/bertrend_apps/prospective_demo/data_model.py +0 -0
  89. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/bertrend_apps/prospective_demo/detailed_report_template.html +0 -0
  90. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/bertrend_apps/prospective_demo/feeds_common.py +0 -0
  91. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/bertrend_apps/prospective_demo/feeds_config.py +0 -0
  92. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/bertrend_apps/prospective_demo/feeds_data.py +0 -0
  93. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/bertrend_apps/prospective_demo/i18n.py +0 -0
  94. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/bertrend_apps/prospective_demo/i18n_translations.py +0 -0
  95. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/bertrend_apps/prospective_demo/llm_utils.py +0 -0
  96. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/bertrend_apps/prospective_demo/models_info.py +0 -0
  97. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/bertrend_apps/prospective_demo/perf_utils.py +0 -0
  98. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/bertrend_apps/prospective_demo/process_new_data.py +0 -0
  99. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/bertrend_apps/prospective_demo/report_generation.py +0 -0
  100. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/bertrend_apps/prospective_demo/report_generation_utils.py +0 -0
  101. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/bertrend_apps/prospective_demo/start_demo.sh +0 -0
  102. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/bertrend_apps/prospective_demo/utils.py +0 -0
  103. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/bertrend_apps/services/__init__.py +0 -0
  104. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/bertrend_apps/services/bertrend/__init__.py +0 -0
  105. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/bertrend_apps/services/bertrend/bertrend_service.py +0 -0
  106. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/bertrend_apps/services/bertrend/config/__init__.py +0 -0
  107. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/bertrend_apps/services/bertrend/config/default_config.toml +0 -0
  108. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/bertrend_apps/services/bertrend/config/settings.py +0 -0
  109. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/bertrend_apps/services/bertrend/models/__init__.py +0 -0
  110. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/bertrend_apps/services/bertrend/models/bertrend_app_models.py +0 -0
  111. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/bertrend_apps/services/bertrend/models/data_provider_models.py +0 -0
  112. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/bertrend_apps/services/bertrend/models/newsletters_models.py +0 -0
  113. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/bertrend_apps/services/bertrend/routers/__init__.py +0 -0
  114. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/bertrend_apps/services/bertrend/routers/bertrend_app.py +0 -0
  115. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/bertrend_apps/services/bertrend/routers/data_provider.py +0 -0
  116. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/bertrend_apps/services/bertrend/routers/info.py +0 -0
  117. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/bertrend_apps/services/bertrend/routers/newsletters.py +0 -0
  118. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/bertrend_apps/services/bertrend/start.py +0 -0
  119. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/bertrend_apps/services/bertrend/status.py +0 -0
  120. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/bertrend_apps/services/bertrend/stop.py +0 -0
  121. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/bertrend_apps/services/queue_management/__init__.py +0 -0
  122. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/bertrend_apps/services/queue_management/client.py +0 -0
  123. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/bertrend_apps/services/queue_management/docker-compose-rabbitmq.yml +0 -0
  124. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/bertrend_apps/services/queue_management/start_rabbitmq.sh +0 -0
  125. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/bertrend_apps/services/queue_monitor/.streamlit/config.toml +0 -0
  126. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/bertrend_apps/services/queue_monitor/__init__.py +0 -0
  127. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/bertrend_apps/services/utils/__init__.py +0 -0
  128. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/bertrend_apps/services/utils/logging_utils.py +0 -0
  129. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/config/bertopic_default_config.toml +0 -0
  130. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/config/bertrend_default_config.toml +0 -0
  131. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/config/parameters.py +0 -0
  132. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/config/services_default_config.toml +0 -0
  133. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/demos/__init__.py +0 -0
  134. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/demos/demos_utils/__init__.py +0 -0
  135. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/demos/demos_utils/data_loading_component.py +0 -0
  136. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/demos/demos_utils/embed_documents_component.py +0 -0
  137. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/demos/demos_utils/i18n.py +0 -0
  138. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/demos/demos_utils/i18n_translations.py +0 -0
  139. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/demos/demos_utils/icons.py +0 -0
  140. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/demos/demos_utils/parameters_component.py +0 -0
  141. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/demos/demos_utils/state_utils.py +0 -0
  142. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/demos/start_summarizer_app.sh +0 -0
  143. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/demos/start_topic_analysis_app.sh +0 -0
  144. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/demos/start_weak_signals_app.sh +0 -0
  145. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/demos/streamlit_components/__init__.py +0 -0
  146. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/demos/streamlit_components/clickable_df_component.py +0 -0
  147. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/demos/streamlit_components/input_with_pills_component.py +0 -0
  148. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/demos/summarization/__init__.py +0 -0
  149. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/demos/summarization/summarizer_app.py +0 -0
  150. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/demos/topic_analysis/.streamlit/config.toml +0 -0
  151. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/demos/topic_analysis/__init__.py +0 -0
  152. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/demos/topic_analysis/app.py +0 -0
  153. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/demos/topic_analysis/app_utils.py +0 -0
  154. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/demos/topic_analysis/demo_pages/__init__.py +0 -0
  155. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/demos/topic_analysis/demo_pages/explore_topics.py +0 -0
  156. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/demos/topic_analysis/demo_pages/newsletters_generation.py +0 -0
  157. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/demos/topic_analysis/demo_pages/temporal_visualizations.py +0 -0
  158. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/demos/topic_analysis/demo_pages/topic_visualizations.py +0 -0
  159. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/demos/topic_analysis/demo_pages/training_page.py +0 -0
  160. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/demos/topic_analysis/i18n_translations.py +0 -0
  161. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/demos/weak_signals/.streamlit/config.toml +0 -0
  162. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/demos/weak_signals/__init__.py +0 -0
  163. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/demos/weak_signals/app.py +0 -0
  164. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/demos/weak_signals/i18n_translations.py +0 -0
  165. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/demos/weak_signals/visualizations_utils.py +0 -0
  166. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/llm_utils/__init__.py +0 -0
  167. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/llm_utils/agent_utils.py +0 -0
  168. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/llm_utils/newsletter.css +0 -0
  169. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/llm_utils/newsletter_features.py +0 -0
  170. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/llm_utils/newsletter_model.py +0 -0
  171. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/llm_utils/newsletter_outlook_template.html +0 -0
  172. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/llm_utils/newsletter_template.html +0 -0
  173. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/llm_utils/openai_client.py +0 -0
  174. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/llm_utils/prompts.py +0 -0
  175. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/metrics/__init__.py +0 -0
  176. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/metrics/temporal_metrics_embedding.py +0 -0
  177. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/metrics/topic_metrics.py +0 -0
  178. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/resources/common_ngrams.json +0 -0
  179. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/resources/stopwords-en.json +0 -0
  180. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/resources/stopwords-fr.json +0 -0
  181. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/resources/stopwords-rte.json +0 -0
  182. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/services/__init__.py +0 -0
  183. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/services/article_scoring_server/__init__.py +0 -0
  184. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/services/article_scoring_server/config/__init__.py +0 -0
  185. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/services/article_scoring_server/config/default_config.toml +0 -0
  186. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/services/article_scoring_server/config/settings.py +0 -0
  187. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/services/article_scoring_server/models.py +0 -0
  188. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/services/article_scoring_server/routers/__init__.py +0 -0
  189. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/services/article_scoring_server/routers/info.py +0 -0
  190. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/services/article_scoring_server/routers/scoring.py +0 -0
  191. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/services/article_scoring_server/scoring_service.py +0 -0
  192. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/services/article_scoring_server/start.py +0 -0
  193. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/services/authentication.py +0 -0
  194. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/services/cert.pem +0 -0
  195. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/services/embedding_client.py +0 -0
  196. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/services/embedding_server/README.md +0 -0
  197. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/services/embedding_server/__init__.py +0 -0
  198. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/services/embedding_server/bertrend_client_registry.json +0 -0
  199. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/services/embedding_server/config/default_config.toml +0 -0
  200. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/services/embedding_server/config/settings.py +0 -0
  201. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/services/embedding_server/main.py +0 -0
  202. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/services/embedding_server/models.py +0 -0
  203. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/services/embedding_server/requirements.txt +0 -0
  204. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/services/embedding_server/routers/__init__.py +0 -0
  205. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/services/embedding_server/routers/authentication.py +0 -0
  206. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/services/embedding_server/routers/embeddings.py +0 -0
  207. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/services/embedding_server/routers/info.py +0 -0
  208. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/services/embedding_server/security.py +0 -0
  209. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/services/embedding_server/start.py +0 -0
  210. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/services/embedding_server/status.py +0 -0
  211. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/services/embedding_server/stop.py +0 -0
  212. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/services/embedding_service.py +0 -0
  213. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/services/key.pem +0 -0
  214. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/services/scheduling/.dockerignore +0 -0
  215. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/services/scheduling/Docker_setup.md +0 -0
  216. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/services/scheduling/Dockerfile +0 -0
  217. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/services/scheduling/__init__.py +0 -0
  218. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/services/scheduling/dashboard_scheduling.py +0 -0
  219. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/services/scheduling/docker-compose.yml +0 -0
  220. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/services/scheduling/job_utils/__init__.py +0 -0
  221. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/services/scheduling/job_utils/job_functions.py +0 -0
  222. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/services/scheduling/models/__init__.py +0 -0
  223. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/services/scheduling/models/scheduling_models.py +0 -0
  224. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/services/scheduling/requirements.txt +0 -0
  225. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/services/scheduling/routers/info.py +0 -0
  226. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/services/scheduling/routers/scheduling.py +0 -0
  227. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/services/scheduling/scheduling_service.py +0 -0
  228. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/services/scheduling/start_docker_scheduling_service.sh +0 -0
  229. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/services/summarizer.py +0 -0
  230. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/services/summary/__init__.py +0 -0
  231. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/services/summary/abstractive_summarizer.py +0 -0
  232. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/services/summary/chatgpt_summarizer.py +0 -0
  233. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/services/summary/extractive_summarizer.py +0 -0
  234. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/services/summary/lexrank.py +0 -0
  235. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/services/summary/prompts.py +0 -0
  236. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/services/summary_client.py +0 -0
  237. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/services/summary_server/__init__.py +0 -0
  238. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/services/summary_server/config/__init__.py +0 -0
  239. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/services/summary_server/config/default_config.toml +0 -0
  240. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/services/summary_server/config/settings.py +0 -0
  241. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/services/summary_server/main.py +0 -0
  242. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/services/summary_server/models.py +0 -0
  243. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/services/summary_server/routers/__init__.py +0 -0
  244. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/services/summary_server/routers/info.py +0 -0
  245. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/services/summary_server/routers/summarize.py +0 -0
  246. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/services/summary_server/start.py +0 -0
  247. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/tests/README_integration_tests.md +0 -0
  248. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/tests/__init__.py +0 -0
  249. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/tests/apps/__init__.py +0 -0
  250. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/tests/apps/test_process_new_data.py +0 -0
  251. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/tests/conftest.py +0 -0
  252. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/tests/core/__init__.py +0 -0
  253. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/tests/core/test_bertrend.py +0 -0
  254. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/tests/core/test_data_structure.py +0 -0
  255. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/tests/core/test_representative_docs.py +0 -0
  256. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/tests/core/test_temporal_metrics_embedding.py +0 -0
  257. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/tests/core/test_topic_description.py +0 -0
  258. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/tests/core/test_topic_metrics.py +0 -0
  259. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/tests/core/test_topic_model.py +0 -0
  260. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/tests/core/test_trend_analysis_data_structure.py +0 -0
  261. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/tests/core/test_trend_analysis_prompts.py +0 -0
  262. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/tests/core/test_trend_analysis_visualizations.py +0 -0
  263. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/tests/core/test_trend_analysis_weak_signals.py +0 -0
  264. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/tests/core/test_visualizations.py +0 -0
  265. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/tests/data_provider/__init__.py +0 -0
  266. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/tests/data_provider/test_arxiv_provider.py +0 -0
  267. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/tests/data_provider/test_bing_news_provider.py +0 -0
  268. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/tests/data_provider/test_data_provider_base.py +0 -0
  269. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/tests/data_provider/test_data_provider_utils.py +0 -0
  270. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/tests/data_provider/test_deep_research_live.py +0 -0
  271. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/tests/data_provider/test_deep_research_provider.py +0 -0
  272. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/tests/data_provider/test_google_news_provider.py +0 -0
  273. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/tests/data_provider/test_newscatcher_provider.py +0 -0
  274. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/tests/data_provider/test_rss_atom_provider.py +0 -0
  275. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/tests/demos/__init__.py +0 -0
  276. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/tests/demos/summarization/__init__.py +0 -0
  277. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/tests/demos/summarization/test_summarizer_app.py +0 -0
  278. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/tests/llm/__init__.py +0 -0
  279. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/tests/llm/test_agent_utils.py +0 -0
  280. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/tests/llm/test_article_scoring.py +0 -0
  281. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/tests/llm/test_article_scoring_models.py +0 -0
  282. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/tests/llm/test_newsletter_features.py +0 -0
  283. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/tests/llm/test_newsletter_model.py +0 -0
  284. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/tests/llm/test_openai_client.py +0 -0
  285. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/tests/services/__init__.py +0 -0
  286. {bertrend-0.4.8/bertrend/tests/services/embedding → bertrend-0.4.9/bertrend/tests/services/article_scoring}/__init__.py +0 -0
  287. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/tests/services/bertrend/__init__.py +0 -0
  288. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/tests/services/bertrend/test_bertrend_service.py +0 -0
  289. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/tests/services/bertrend/test_models.py +0 -0
  290. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/tests/services/bertrend/test_routers_bertrend_app.py +0 -0
  291. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/tests/services/bertrend/test_routers_info.py +0 -0
  292. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/tests/services/bertrend/test_routers_newsletters.py +0 -0
  293. {bertrend-0.4.8/bertrend/tests/services/embedding_server → bertrend-0.4.9/bertrend/tests/services/embedding}/__init__.py +0 -0
  294. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/tests/services/embedding/test_embedding_client.py +0 -0
  295. {bertrend-0.4.8/bertrend/tests/services/queue → bertrend-0.4.9/bertrend/tests/services/embedding_server}/__init__.py +0 -0
  296. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/tests/services/embedding_server/test_models.py +0 -0
  297. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/tests/services/embedding_server/test_routers.py +0 -0
  298. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/tests/services/embedding_server/test_security.py +0 -0
  299. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/tests/services/embedding_server/test_settings.py +0 -0
  300. {bertrend-0.4.8/bertrend/tests/services/scheduling → bertrend-0.4.9/bertrend/tests/services/queue}/__init__.py +0 -0
  301. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/tests/services/queue/test_bertrend_worker.py +0 -0
  302. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/tests/services/queue/test_client.py +0 -0
  303. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/tests/services/queue/test_queue_manager.py +0 -0
  304. {bertrend-0.4.8/bertrend/tests/services/summarizer → bertrend-0.4.9/bertrend/tests/services/queue_management}/__init__.py +0 -0
  305. {bertrend-0.4.8/bertrend/tests/services/summary → bertrend-0.4.9/bertrend/tests/services/scheduling}/__init__.py +0 -0
  306. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/tests/services/scheduling/test_job_exception_fix.py +0 -0
  307. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/tests/services/scheduling/test_scheduling_service.py +0 -0
  308. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/tests/services/scheduling/test_stress_test.py +0 -0
  309. {bertrend-0.4.8/bertrend/tests/utils → bertrend-0.4.9/bertrend/tests/services/summarizer}/__init__.py +0 -0
  310. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/tests/services/summarizer/test_abstractive_summarizer.py +0 -0
  311. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/tests/services/summarizer/test_chatgpt_summarizer.py +0 -0
  312. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/tests/services/summarizer/test_extractive_summarizer.py +0 -0
  313. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/tests/services/summarizer/test_summarizer.py +0 -0
  314. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/tests/services/summary/test_summary_client.py +0 -0
  315. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/tests/services/summary/test_summary_config.py +0 -0
  316. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/tests/services/summary/test_summary_server.py +0 -0
  317. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/tests/test_installation.py +0 -0
  318. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/tests/test_integration.py +0 -0
  319. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/tests/utils/test_apscheduler_utils.py +0 -0
  320. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/tests/utils/test_cache_utils.py +0 -0
  321. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/tests/utils/test_config_utils.py +0 -0
  322. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/tests/utils/test_crontab_utils.py +0 -0
  323. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/tests/utils/test_data_loading.py +0 -0
  324. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/tests/utils/test_date_utils.py +0 -0
  325. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/tests/utils/test_lazy_scheduler.py +0 -0
  326. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/tests/utils/test_mail_utils.py +0 -0
  327. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/tests/utils/test_multiworker.py +0 -0
  328. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/tests/utils/test_scheduler_utils.py +0 -0
  329. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/tests/utils/test_utils_init.py +0 -0
  330. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/topic_analysis/__init__.py +0 -0
  331. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/topic_analysis/data_structure.py +0 -0
  332. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/topic_analysis/prompts.py +0 -0
  333. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/topic_analysis/representative_docs.py +0 -0
  334. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/topic_analysis/topic_description.py +0 -0
  335. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/topic_analysis/visualizations.py +0 -0
  336. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/trend_analysis/__init__.py +0 -0
  337. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/trend_analysis/data_structure.py +0 -0
  338. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/trend_analysis/prompts.py +0 -0
  339. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/trend_analysis/signal_llm_template_en.html +0 -0
  340. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/trend_analysis/signal_llm_template_fr.html +0 -0
  341. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/trend_analysis/visualizations.py +0 -0
  342. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/trend_analysis/weak_signals.py +0 -0
  343. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/utils/__init__.py +0 -0
  344. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/utils/cache_utils.py +0 -0
  345. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/utils/config_utils.py +0 -0
  346. {bertrend-0.4.8 → bertrend-0.4.9}/bertrend/utils/data_loading.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: bertrend
3
- Version: 0.4.8
3
+ Version: 0.4.9
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
6
  License-File: AUTHORS.txt
@@ -253,6 +253,45 @@ class BertrendWorker:
253
253
  "traceback": traceback.format_exc(),
254
254
  }
255
255
 
256
+ def _get_retry_count(self, message: aio_pika.abc.AbstractIncomingMessage) -> int:
257
+ """Return the current retry count stored in the message header x-retry-count."""
258
+ headers = message.headers or {}
259
+ try:
260
+ return int(headers.get("x-retry-count", 0))
261
+ except (TypeError, ValueError):
262
+ return 0
263
+
264
+ async def _nack_or_discard(
265
+ self,
266
+ message: aio_pika.abc.AbstractIncomingMessage,
267
+ reason: str,
268
+ correlation_id: str | None,
269
+ ):
270
+ """
271
+ Retry the message if the retry limit has not been reached, otherwise
272
+ reject it (requeue=False) so it goes to the DLQ.
273
+
274
+ Delegates the retry-limit check to republish_with_retry(), which
275
+ returns False when the limit is reached (and skips publishing).
276
+ The original message is always rejected with requeue=False so that
277
+ RabbitMQ does not re-deliver the same copy.
278
+ """
279
+ retry_count = self._get_retry_count(message)
280
+ republished = await self.queue_manager.republish_with_retry(
281
+ message, retry_count
282
+ )
283
+ if republished:
284
+ logger.warning(
285
+ f"Message {correlation_id} failed (attempt {retry_count + 1}/{self.config.max_retries}) "
286
+ f"— republished with incremented retry count. Reason: {reason}"
287
+ )
288
+ else:
289
+ logger.warning(
290
+ f"Message {correlation_id} exceeded max retries ({self.config.max_retries}) "
291
+ f"— sending to DLQ. Reason: {reason}"
292
+ )
293
+ await message.reject(requeue=False)
294
+
256
295
  async def callback(
257
296
  self,
258
297
  message: aio_pika.abc.AbstractIncomingMessage,
@@ -268,41 +307,63 @@ class BertrendWorker:
268
307
 
269
308
  logger.info(f"Request endpoint: {request_data.get('endpoint')}")
270
309
 
271
- # Process request
272
- response_data = await self.process_request(request_data)
310
+ # Process request with a timeout to avoid blocking the queue indefinitely
311
+ response_data = await asyncio.wait_for(
312
+ self.process_request(request_data),
313
+ timeout=self.config.job_timeout,
314
+ )
273
315
 
274
316
  # Add metadata
275
317
  response_data["correlation_id"] = correlation_id
276
318
  response_data["request_data"] = request_data
277
319
 
278
- # Publish response/error if reply_to is specified
279
- if message.reply_to:
280
- if response_data.get("status") == "error":
281
- await self.queue_manager.publish_error(
282
- error_data=response_data, correlation_id=correlation_id
283
- )
284
- else:
285
- await self.queue_manager.publish_response(
286
- response_data=response_data, correlation_id=correlation_id
287
- )
288
-
289
- # Acknowledge message
290
- await message.ack()
291
- logger.info(
292
- f"Completed request: {correlation_id} - Status: {response_data.get('status')}"
293
- )
294
-
295
320
  except json.JSONDecodeError as e:
296
321
  logger.error(f"Invalid message format: {str(e)}")
297
322
  # Reject and don't requeue invalid messages
298
323
  await message.reject(requeue=False)
324
+ return
325
+
326
+ except asyncio.TimeoutError:
327
+ await self._nack_or_discard(
328
+ message,
329
+ reason=f"job timed out after {self.config.job_timeout}s",
330
+ correlation_id=correlation_id,
331
+ )
332
+ return
299
333
 
300
334
  except Exception as e:
301
335
  logger.error(f"Error processing message: {str(e)}")
302
336
  logger.error(traceback.format_exc())
337
+ await self._nack_or_discard(
338
+ message,
339
+ reason=str(e),
340
+ correlation_id=correlation_id,
341
+ )
342
+ return
303
343
 
304
- # Requeue for retry (will go to DLQ after max retries)
305
- await message.nack(requeue=True)
344
+ # Acknowledge message before publishing the response, so that the message
345
+ # is always acked regardless of any subsequent publish failure
346
+ await message.ack()
347
+ logger.info(
348
+ f"Completed request: {correlation_id} - Status: {response_data.get('status')}"
349
+ )
350
+
351
+ # Publish response/error if reply_to is specified
352
+ if message.reply_to:
353
+ try:
354
+ if response_data.get("status") == "error":
355
+ await self.queue_manager.publish_error(
356
+ error_data=response_data, correlation_id=correlation_id
357
+ )
358
+ else:
359
+ await self.queue_manager.publish_response(
360
+ response_data=response_data, correlation_id=correlation_id
361
+ )
362
+ except Exception as e:
363
+ logger.error(
364
+ f"Failed to publish response for {correlation_id}: {str(e)}"
365
+ )
366
+ logger.error(traceback.format_exc())
306
367
 
307
368
  async def start(self):
308
369
  """Start the worker"""
@@ -159,6 +159,55 @@ class QueueManager:
159
159
 
160
160
  await queue.consume(callback, no_ack=False)
161
161
 
162
+ async def republish_with_retry(
163
+ self,
164
+ original_message: aio_pika.abc.AbstractIncomingMessage,
165
+ retry_count: int,
166
+ ) -> bool:
167
+ """Republish a failed message with an incremented x-retry-count header.
168
+
169
+ RabbitMQ does not modify headers on requeue, so we must republish
170
+ manually with the updated counter instead of using nack(requeue=True).
171
+
172
+ Returns True if the message was republished, False if the retry limit
173
+ has already been reached and the message was not republished.
174
+ """
175
+ new_retry_count = retry_count + 1
176
+ if new_retry_count > self.config.max_retries:
177
+ logger.warning(
178
+ f"Message {original_message.correlation_id} has reached the maximum "
179
+ f"retry limit ({self.config.max_retries}) — not republishing."
180
+ )
181
+ return False
182
+
183
+ if not self.channel or self.channel.is_closed:
184
+ await self.connect()
185
+
186
+ # Carry over all existing headers and bump the retry counter
187
+ headers = dict(original_message.headers or {})
188
+ headers["x-retry-count"] = new_retry_count
189
+
190
+ message = aio_pika.Message(
191
+ body=original_message.body,
192
+ headers=headers,
193
+ delivery_mode=aio_pika.DeliveryMode.PERSISTENT,
194
+ priority=original_message.priority,
195
+ correlation_id=original_message.correlation_id,
196
+ content_type=original_message.content_type,
197
+ reply_to=original_message.reply_to,
198
+ )
199
+
200
+ await self.channel.default_exchange.publish(
201
+ message,
202
+ routing_key=self.config.request_queue,
203
+ )
204
+
205
+ logger.info(
206
+ f"Republished message {original_message.correlation_id} "
207
+ f"with x-retry-count={new_retry_count}"
208
+ )
209
+ return True
210
+
162
211
  async def publish_response(self, response_data: dict, correlation_id: str):
163
212
  """Publish response to response queue_management"""
164
213
  if not self.channel or self.channel.is_closed:
@@ -36,5 +36,8 @@ class RabbitMQConfig:
36
36
  prefetch_count: int = int(os.getenv("RABBITMQ_PREFETCH_COUNT", 1))
37
37
 
38
38
  # Retry configuration
39
- max_retries: int = int(os.getenv("RABBITMQ_MAX_RETRIES", 3))
39
+ max_retries: int = int(os.getenv("RABBITMQ_MAX_RETRIES", 2))
40
40
  retry_delay: int = int(os.getenv("RABBITMQ_RETRY_DELAY", 5000)) # milliseconds
41
+
42
+ # Job timeout: max seconds a single job may run before being nacked (default: 1 hour)
43
+ job_timeout: int = int(os.getenv("RABBITMQ_JOB_TIMEOUT", 3600))
@@ -60,6 +60,18 @@ class RabbitMQAdminClient:
60
60
  r.raise_for_status()
61
61
  return r.json()
62
62
 
63
+ def get_consumers(self) -> List[Dict[str, Any]]:
64
+ url = f"{self.base_url}/api/consumers/{self._vhost_path()}"
65
+ r = self._session.get(url, timeout=5)
66
+ r.raise_for_status()
67
+ return r.json()
68
+
69
+ def get_channels(self) -> List[Dict[str, Any]]:
70
+ url = f"{self.base_url}/api/channels"
71
+ r = self._session.get(url, timeout=5)
72
+ r.raise_for_status()
73
+ return r.json()
74
+
63
75
  def peek_messages(self, name: str, count: int = 10) -> List[Dict[str, Any]]:
64
76
  url = f"{self.base_url}/api/queues/{self._vhost_path()}/{quote(name, safe='')}/get"
65
77
  payload = {
@@ -262,7 +274,14 @@ client = RabbitMQAdminClient(client_cfg, management_url=mgmt_url)
262
274
 
263
275
  # --- Data Fetching ---
264
276
  queue_data = {}
265
- global_stats = {"total": 0, "ready": 0, "unacked": 0, "consumers": 0}
277
+ global_stats = {
278
+ "total": 0,
279
+ "requests": 0,
280
+ "responses": 0,
281
+ "failed": 0,
282
+ "unacked": 0,
283
+ "consumers": 0,
284
+ }
266
285
  error_msg = None
267
286
 
268
287
  try:
@@ -272,9 +291,14 @@ try:
272
291
 
273
292
  # Aggregation
274
293
  global_stats["total"] += info.get("messages", 0)
275
- global_stats["ready"] += info.get("messages_ready", 0)
276
294
  global_stats["unacked"] += info.get("messages_unacknowledged", 0)
277
295
  global_stats["consumers"] += info.get("consumers", 0)
296
+ if "requests" in q:
297
+ global_stats["requests"] += info.get("messages", 0)
298
+ elif "responses" in q or "response" in q:
299
+ global_stats["responses"] += info.get("messages", 0)
300
+ elif "failed" in q:
301
+ global_stats["failed"] += info.get("messages", 0)
278
302
 
279
303
  except Exception as e:
280
304
  error_msg = str(e)
@@ -288,18 +312,33 @@ if error_msg:
288
312
 
289
313
  # 1. Global Overview Row
290
314
  st.markdown(f"### {DASHBOARD_ICON} System Overview")
291
- kpi1, kpi2, kpi3, kpi4 = st.columns(4)
315
+ kpi1, kpi2, kpi3, kpi4, kpi5, kpi6 = st.columns(6)
292
316
  kpi1.metric(
293
317
  "Total Backlog", global_stats["total"], help="Total messages in all selected queues"
294
318
  )
295
- kpi2.metric("Ready to Process", global_stats["ready"], delta_color="normal")
319
+ kpi2.metric(
320
+ "Requests",
321
+ global_stats["requests"],
322
+ help="Messages in bertrend_requests queue",
323
+ )
296
324
  kpi3.metric(
325
+ "Responses",
326
+ global_stats["responses"],
327
+ help="Messages in bertrend_responses queue",
328
+ )
329
+ kpi4.metric(
330
+ "Failed",
331
+ global_stats["failed"],
332
+ delta_color="inverse",
333
+ help="Messages in bertrend_failed queue",
334
+ )
335
+ kpi5.metric(
297
336
  "Unacknowledged",
298
337
  global_stats["unacked"],
299
338
  delta_color="inverse",
300
339
  help="Messages currently being processed but not acked",
301
340
  )
302
- kpi4.metric("Active Consumers", global_stats["consumers"])
341
+ kpi6.metric("Active Consumers", global_stats["consumers"])
303
342
 
304
343
  st.divider()
305
344
 
@@ -327,6 +366,108 @@ else:
327
366
 
328
367
  st.divider()
329
368
 
369
+ # B. Unacknowledged Messages Details
370
+ unacked_count = info.get("messages_unacknowledged", 0)
371
+ with st.expander(
372
+ f"Unacknowledged Messages ({unacked_count})",
373
+ expanded=unacked_count > 0,
374
+ ):
375
+ if unacked_count == 0:
376
+ st.info("No unacknowledged messages in this queue.", icon=INFO_ICON)
377
+ else:
378
+ try:
379
+ all_consumers = client.get_consumers()
380
+ all_channels = client.get_channels()
381
+
382
+ # Filter consumers for this queue
383
+ queue_consumers = [
384
+ c
385
+ for c in all_consumers
386
+ if c.get("queue", {}).get("name") == qname
387
+ ]
388
+
389
+ # Build a map of channel_name -> channel details
390
+ channel_map = {ch.get("name"): ch for ch in all_channels}
391
+
392
+ if not queue_consumers:
393
+ st.warning(
394
+ "Unacknowledged messages exist but no active consumers found. "
395
+ "Messages may be held by a disconnected consumer."
396
+ )
397
+ else:
398
+ st.caption(
399
+ f"{len(queue_consumers)} consumer(s) holding unacknowledged messages:"
400
+ )
401
+ for cons in queue_consumers:
402
+ ch_name = cons.get("channel_details", {}).get(
403
+ "name", ""
404
+ )
405
+ ch = channel_map.get(ch_name, {})
406
+ ch_unacked = ch.get("messages_unacknowledged", 0)
407
+ consumer_tag = cons.get("consumer_tag", "unknown")
408
+ peer_host = cons.get("channel_details", {}).get(
409
+ "peer_host", "?"
410
+ )
411
+ peer_port = cons.get("channel_details", {}).get(
412
+ "peer_port", ""
413
+ )
414
+ prefetch = cons.get("prefetch_count", 0)
415
+ ack_required = cons.get("ack_required", True)
416
+
417
+ with st.container(border=True):
418
+ cc1, cc2, cc3, cc4 = st.columns(4)
419
+ cc1.metric("Unacked (channel)", ch_unacked)
420
+ cc2.markdown(f"**Consumer Tag**\n`{consumer_tag}`")
421
+ cc3.markdown(
422
+ f"**Client**\n`{peer_host}:{peer_port}`"
423
+ )
424
+ cc4.markdown(
425
+ f"**Prefetch / Ack required**\n`{prefetch}` / `{ack_required}`"
426
+ )
427
+
428
+ # Channel-level stats
429
+ ch_stats = ch.get("message_stats", {})
430
+ ack_rate = (
431
+ ch_stats.get("ack_details", {}).get("rate", 0.0)
432
+ if ch_stats
433
+ else 0.0
434
+ )
435
+ deliver_rate = (
436
+ ch_stats.get("deliver_details", {}).get(
437
+ "rate", 0.0
438
+ )
439
+ if ch_stats
440
+ else 0.0
441
+ )
442
+ total_acked = (
443
+ ch_stats.get("ack", 0) if ch_stats else 0
444
+ )
445
+ total_delivered = (
446
+ ch_stats.get("deliver", 0) if ch_stats else 0
447
+ )
448
+
449
+ sc1, sc2, sc3, sc4 = st.columns(4)
450
+ sc1.metric("Ack Rate", f"{ack_rate:.2f}/s")
451
+ sc2.metric("Deliver Rate", f"{deliver_rate:.2f}/s")
452
+ sc3.metric("Total Acked", total_acked)
453
+ sc4.metric("Total Delivered", total_delivered)
454
+
455
+ # Connection info
456
+ conn = ch.get("connection_details", {})
457
+ if conn:
458
+ st.caption(
459
+ f"Connection: `{conn.get('name', ch_name)}` "
460
+ f"— state: `{ch.get('state', '?')}`"
461
+ )
462
+
463
+ except Exception as e:
464
+ st.error(
465
+ f"Failed to fetch consumer/channel details: {e}",
466
+ icon=ERROR_ICON,
467
+ )
468
+
469
+ st.divider()
470
+
330
471
  # B. Message Peeking Interface
331
472
  c_head, c_peek_n, c_sort = st.columns([3, 1, 1])
332
473
  c_head.subheader("Message Browser")
@@ -546,6 +687,7 @@ else:
546
687
  obj = d["obj"]
547
688
 
548
689
  props = m.get("properties", {})
690
+ headers = props.get("headers") or {}
549
691
  corr_id = props.get("correlation_id", "No-ID")
550
692
  msg_size = (
551
693
  m.get("payload_bytes", 0)
@@ -562,6 +704,9 @@ else:
562
704
  info_tags.append(f"User: {d['user']}")
563
705
  if d["model_id"]:
564
706
  info_tags.append(f"Model: {d['model_id']}")
707
+ retry_count = headers.get("x-retry-count")
708
+ if retry_count is not None:
709
+ info_tags.append(f"Retry: {retry_count}")
565
710
 
566
711
  info_str = f" | {' | '.join(info_tags)}" if info_tags else ""
567
712
  label = f"#{idx + 1} | {fmt} | CID: {corr_id}{info_str} | {msg_size} bytes"
@@ -617,6 +762,12 @@ else:
617
762
  "Key": "App ID",
618
763
  "Val": str(props.get("app_id") or ""),
619
764
  },
765
+ {
766
+ "Key": "x-retry-count",
767
+ "Val": str(headers.get("x-retry-count"))
768
+ if headers.get("x-retry-count") is not None
769
+ else "",
770
+ },
620
771
  ]
621
772
  ).dropna()
622
773
  st.dataframe(meta_df, hide_index=True, width="stretch")
@@ -0,0 +1,84 @@
1
+ # Copyright (c) 2024-2026, RTE (https://www.rte-france.com)
2
+ # See AUTHORS.txt
3
+ # SPDX-License-Identifier: MPL-2.0
4
+ # This file is part of BERTrend.
5
+
6
+ from unittest.mock import patch
7
+
8
+ import pytest
9
+ from fastapi.testclient import TestClient
10
+
11
+ from bertrend.article_scoring.article_scoring import ArticleScore, CriteriaScores
12
+ from bertrend.llm_utils.agent_utils import ProcessingResult
13
+ from bertrend.services.article_scoring_server.scoring_service import app
14
+
15
+
16
+ @pytest.fixture
17
+ def client():
18
+ return TestClient(app)
19
+
20
+
21
+ def _make_article_score(
22
+ depth: int = 4, originality: int = 3, relevance_to_rte: int = 5
23
+ ):
24
+ return ArticleScore(
25
+ scores=CriteriaScores(
26
+ depth_of_reporting=depth / 5,
27
+ originality_and_exclusivity=originality / 5,
28
+ source_quality_and_transparency=0.8,
29
+ accuracy_and_fact_checking_rigor=0.8,
30
+ clarity_and_accessibility=0.8,
31
+ balance_and_fairness=0.8,
32
+ narrative_and_engagement=0.8,
33
+ timeliness_and_relevance=0.8,
34
+ ethical_considerations_and_sensitivity=0.8,
35
+ rte_relevance_and_strategic_impact=relevance_to_rte / 5,
36
+ )
37
+ )
38
+
39
+
40
+ def test_health(client):
41
+ response = client.get("/health")
42
+ assert response.status_code == 200
43
+ assert response.json() == {"status": "ok"}
44
+
45
+
46
+ def test_score_articles_success(client):
47
+ mock_results = [
48
+ ProcessingResult(input_index=0, output=_make_article_score()),
49
+ ProcessingResult(
50
+ input_index=1, output=_make_article_score(depth=2), error="timeout"
51
+ ),
52
+ ]
53
+
54
+ with patch(
55
+ "bertrend.services.article_scoring_server.routers.scoring.score_articles",
56
+ return_value=mock_results,
57
+ ) as mock_score:
58
+ response = client.post("/score", json={"articles": ["article 1", "article 2"]})
59
+
60
+ assert response.status_code == 200
61
+ mock_score.assert_called_once_with(["article 1", "article 2"])
62
+
63
+ data = response.json()
64
+ assert len(data["results"]) == 2
65
+ assert data["results"][0]["article_index"] == 0
66
+ assert data["results"][0]["quality_metrics"] is not None
67
+ assert data["results"][0]["overall_quality"] == "POOR"
68
+ assert data["results"][0]["error"] is None
69
+
70
+ assert data["results"][1]["article_index"] == 1
71
+ assert data["results"][1]["quality_metrics"] is not None
72
+ assert data["results"][1]["overall_quality"] is None
73
+ assert data["results"][1]["error"] == "timeout"
74
+
75
+
76
+ def test_score_articles_backend_exception(client):
77
+ with patch(
78
+ "bertrend.services.article_scoring_server.routers.scoring.score_articles",
79
+ side_effect=RuntimeError("provider down"),
80
+ ):
81
+ response = client.post("/score", json={"articles": ["article 1"]})
82
+
83
+ assert response.status_code == 500
84
+ assert "provider down" in response.json()["detail"]