bertrend 0.4.0__tar.gz → 0.4.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {bertrend-0.4.0 → bertrend-0.4.2}/PKG-INFO +5 -1
- bertrend-0.4.2/bertrend/bertrend_apps/config/feeds/feed_deep_research_example.toml +9 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/bertrend_apps/data_provider/arxiv_provider.py +3 -4
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/bertrend_apps/data_provider/data_provider_utils.py +9 -2
- bertrend-0.4.2/bertrend/bertrend_apps/data_provider/deep_research_provider.py +501 -0
- bertrend-0.4.2/bertrend/bertrend_apps/exploration/geoviz/.streamlit/config.toml +6 -0
- bertrend-0.4.2/bertrend/bertrend_apps/exploration/geoviz/app.py +174 -0
- bertrend-0.4.2/bertrend/bertrend_apps/exploration/geoviz/backend.py +190 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/bertrend_apps/prospective_demo/feeds_config.py +11 -11
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/bertrend_apps/services/bertrend/models/data_provider_models.py +2 -2
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/config/services_default_config.toml +5 -1
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/llm_utils/agent_utils.py +3 -1
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/services/scheduling/docker-compose.yml +3 -5
- bertrend-0.4.2/bertrend/services/scheduling/start_docker_scheduling_service.sh +16 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/services/summarizer.py +1 -1
- bertrend-0.4.2/bertrend/services/summary_client.py +70 -0
- bertrend-0.4.2/bertrend/services/summary_server/config/default_config.toml +3 -0
- bertrend-0.4.2/bertrend/services/summary_server/config/settings.py +32 -0
- bertrend-0.4.2/bertrend/services/summary_server/main.py +12 -0
- bertrend-0.4.2/bertrend/services/summary_server/models.py +30 -0
- bertrend-0.4.2/bertrend/services/summary_server/routers/info.py +33 -0
- bertrend-0.4.2/bertrend/services/summary_server/routers/summarize.py +87 -0
- bertrend-0.4.2/bertrend/services/summary_server/start.py +18 -0
- bertrend-0.4.2/bertrend/tests/data_provider/test_arxiv_provider.py +59 -0
- bertrend-0.4.2/bertrend/tests/data_provider/test_bing_news_provider.py +46 -0
- bertrend-0.4.2/bertrend/tests/data_provider/test_data_provider_base.py +123 -0
- bertrend-0.4.2/bertrend/tests/data_provider/test_data_provider_utils.py +99 -0
- bertrend-0.4.2/bertrend/tests/data_provider/test_deep_research_live.py +81 -0
- bertrend-0.4.2/bertrend/tests/data_provider/test_deep_research_provider.py +489 -0
- bertrend-0.4.2/bertrend/tests/data_provider/test_google_news_provider.py +52 -0
- bertrend-0.4.2/bertrend/tests/data_provider/test_newscatcher_provider.py +54 -0
- bertrend-0.4.2/bertrend/tests/data_provider/test_rss_atom_provider.py +73 -0
- bertrend-0.4.2/bertrend/tests/services/summarizer/__init__.py +4 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/tests/services/summarizer/test_summarizer.py +2 -2
- bertrend-0.4.2/bertrend/tests/services/summary/__init__.py +4 -0
- bertrend-0.4.2/bertrend/tests/services/summary/test_summary_client.py +182 -0
- bertrend-0.4.2/bertrend/tests/services/summary/test_summary_config.py +44 -0
- bertrend-0.4.2/bertrend/tests/services/summary/test_summary_server.py +230 -0
- bertrend-0.4.2/bertrend/tests/utils/__init__.py +4 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/pyproject.toml +6 -2
- bertrend-0.4.0/bertrend/bertrend_apps/exploration/dataviz/Main_page.py +0 -10
- bertrend-0.4.0/bertrend/bertrend_apps/exploration/dataviz/pages/1_Data_feeds.py +0 -105
- bertrend-0.4.0/bertrend/bertrend_apps/exploration/geolocalization/entity_utils.py +0 -118
- bertrend-0.4.0/bertrend/bertrend_apps/exploration/geolocalization/spacy/common_factory.py +0 -142
- bertrend-0.4.0/bertrend/bertrend_apps/exploration/geolocalization/spacy/dbpedia.py +0 -204
- bertrend-0.4.0/bertrend/bertrend_apps/exploration/geolocalization/spacy/nlp.config +0 -37
- bertrend-0.4.0/bertrend/bertrend_apps/exploration/geolocalization/spacy/utils.py +0 -42
- bertrend-0.4.0/bertrend/bertrend_apps/services/queue_monitor/__init__.py +0 -5
- {bertrend-0.4.0 → bertrend-0.4.2}/AUTHORS.txt +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/LICENSE.md +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/README.md +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/BERTopicModel.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/BERTrend.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/__init__.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/article_scoring/__init__.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/article_scoring/article_scoring.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/article_scoring/prompts.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/article_scoring/scoring_agent.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/bertrend_apps/__init__.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/bertrend_apps/common/__init__.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/bertrend_apps/common/apscheduler_utils.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/bertrend_apps/common/crontab_utils.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/bertrend_apps/common/date_utils.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/bertrend_apps/common/mail_utils.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/bertrend_apps/common/scheduler_utils.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/bertrend_apps/config/feeds/arxiv_feed.toml +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/bertrend_apps/config/feeds/decarbonation_feed.toml +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/bertrend_apps/config/feeds/llm_en_feed.toml +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/bertrend_apps/config/feeds/llm_feed.toml +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/bertrend_apps/config/feeds/nlp_feed.toml +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/bertrend_apps/config/feeds/rte_feed.toml +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/bertrend_apps/config/newsletters/arxiv_newsletter_from_last.toml +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/bertrend_apps/config/newsletters/decarbonation_newsletter_from_last.toml +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/bertrend_apps/config/newsletters/llm_en_newsletter_from_last.toml +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/bertrend_apps/config/newsletters/llm_newsletter_from_last.toml +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/bertrend_apps/config/newsletters/nlp_newsletter_from_last.toml +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/bertrend_apps/config/newsletters/rte_newsletter_from_last.toml +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/bertrend_apps/data_provider/__init__.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/bertrend_apps/data_provider/__main__.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/bertrend_apps/data_provider/atom_feed_provider.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/bertrend_apps/data_provider/bing_news_provider.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/bertrend_apps/data_provider/data_provider.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/bertrend_apps/data_provider/google_news_provider.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/bertrend_apps/data_provider/newscatcher_provider.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/bertrend_apps/data_provider/rss_feed_provider.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/bertrend_apps/data_provider/utils.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/bertrend_apps/exploration/__init__.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/bertrend_apps/exploration/curebot/.streamlit/config.toml +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/bertrend_apps/exploration/curebot/__init__.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/bertrend_apps/exploration/curebot/app.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/bertrend_apps/exploration/curebot/app_utils.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/bertrend_apps/exploration/curebot/config.toml +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/bertrend_apps/exploration/curebot/import_extraction_article.ipynb +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/bertrend_apps/exploration/curebot/prompts.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/bertrend_apps/exploration/curebot/start_newsletter_generator.sh +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/bertrend_apps/exploration/curebot/tabs/__init__.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/bertrend_apps/exploration/curebot/tabs/tab1.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/bertrend_apps/exploration/curebot/tabs/tab2.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/bertrend_apps/exploration/curebot/tabs/tab3.py +0 -0
- {bertrend-0.4.0/bertrend/bertrend_apps/exploration/dataviz → bertrend-0.4.2/bertrend/bertrend_apps/exploration/geoviz}/__init__.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/bertrend_apps/exploration/start_apps.sh +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/bertrend_apps/exploration/stop_apps.sh +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/bertrend_apps/install_feeds_and_newsletters.py +0 -0
- {bertrend-0.4.0/bertrend/bertrend_apps/exploration/geolocalization → bertrend-0.4.2/bertrend/bertrend_apps/newsletters}/__init__.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/bertrend_apps/newsletters/__main__.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/bertrend_apps/newsletters/newsletter_generation.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/bertrend_apps/newsletters/utils.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/bertrend_apps/prospective_demo/.streamlit/config.toml +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/bertrend_apps/prospective_demo/.streamlit/secrets.toml.example +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/bertrend_apps/prospective_demo/__init__.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/bertrend_apps/prospective_demo/app.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/bertrend_apps/prospective_demo/authentication.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/bertrend_apps/prospective_demo/automated_report_generation.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/bertrend_apps/prospective_demo/dashboard_analysis.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/bertrend_apps/prospective_demo/dashboard_common.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/bertrend_apps/prospective_demo/dashboard_comparative.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/bertrend_apps/prospective_demo/dashboard_signals.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/bertrend_apps/prospective_demo/data_model.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/bertrend_apps/prospective_demo/detailed_report_template.html +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/bertrend_apps/prospective_demo/feeds_common.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/bertrend_apps/prospective_demo/feeds_data.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/bertrend_apps/prospective_demo/i18n.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/bertrend_apps/prospective_demo/i18n_translations.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/bertrend_apps/prospective_demo/llm_utils.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/bertrend_apps/prospective_demo/models_info.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/bertrend_apps/prospective_demo/perf_utils.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/bertrend_apps/prospective_demo/process_new_data.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/bertrend_apps/prospective_demo/report_generation.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/bertrend_apps/prospective_demo/report_generation_utils.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/bertrend_apps/prospective_demo/start_demo.sh +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/bertrend_apps/prospective_demo/utils.py +0 -0
- {bertrend-0.4.0/bertrend/bertrend_apps/exploration/geolocalization/spacy → bertrend-0.4.2/bertrend/bertrend_apps/services}/__init__.py +0 -0
- {bertrend-0.4.0/bertrend/bertrend_apps/exploration/dataviz/pages → bertrend-0.4.2/bertrend/bertrend_apps/services/bertrend}/__init__.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/bertrend_apps/services/bertrend/bertrend_service.py +0 -0
- {bertrend-0.4.0/bertrend/bertrend_apps/services/bertrend → bertrend-0.4.2/bertrend/bertrend_apps/services/bertrend/config}/__init__.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/bertrend_apps/services/bertrend/config/default_config.toml +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/bertrend_apps/services/bertrend/config/settings.py +0 -0
- {bertrend-0.4.0/bertrend/bertrend_apps/newsletters → bertrend-0.4.2/bertrend/bertrend_apps/services/bertrend/models}/__init__.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/bertrend_apps/services/bertrend/models/bertrend_app_models.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/bertrend_apps/services/bertrend/models/newsletters_models.py +0 -0
- {bertrend-0.4.0/bertrend/bertrend_apps/services → bertrend-0.4.2/bertrend/bertrend_apps/services/bertrend/routers}/__init__.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/bertrend_apps/services/bertrend/routers/bertrend_app.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/bertrend_apps/services/bertrend/routers/data_provider.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/bertrend_apps/services/bertrend/routers/info.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/bertrend_apps/services/bertrend/routers/newsletters.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/bertrend_apps/services/bertrend/start.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/bertrend_apps/services/bertrend/status.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/bertrend_apps/services/bertrend/stop.py +0 -0
- {bertrend-0.4.0/bertrend/bertrend_apps/services/bertrend/config → bertrend-0.4.2/bertrend/bertrend_apps/services/queue_management}/__init__.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/bertrend_apps/services/queue_management/bertrend_worker.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/bertrend_apps/services/queue_management/client.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/bertrend_apps/services/queue_management/docker-compose-rabbitmq.yml +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/bertrend_apps/services/queue_management/queue_manager.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/bertrend_apps/services/queue_management/rabbitmq_config.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/bertrend_apps/services/queue_management/start_rabbitmq.sh +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/bertrend_apps/services/queue_monitor/.streamlit/config.toml +0 -0
- {bertrend-0.4.0/bertrend/bertrend_apps/services/queue_management → bertrend-0.4.2/bertrend/bertrend_apps/services/queue_monitor}/__init__.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/bertrend_apps/services/queue_monitor/app.py +0 -0
- {bertrend-0.4.0/bertrend/bertrend_apps/services/bertrend/models → bertrend-0.4.2/bertrend/bertrend_apps/services/utils}/__init__.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/bertrend_apps/services/utils/logging_utils.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/config/bertopic_default_config.toml +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/config/bertrend_default_config.toml +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/config/parameters.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/demos/__init__.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/demos/demos_utils/__init__.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/demos/demos_utils/data_loading_component.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/demos/demos_utils/embed_documents_component.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/demos/demos_utils/i18n.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/demos/demos_utils/i18n_translations.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/demos/demos_utils/icons.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/demos/demos_utils/parameters_component.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/demos/demos_utils/state_utils.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/demos/start_summarizer_app.sh +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/demos/start_topic_analysis_app.sh +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/demos/start_weak_signals_app.sh +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/demos/streamlit_components/__init__.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/demos/streamlit_components/clickable_df_component.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/demos/streamlit_components/input_with_pills_component.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/demos/summarization/__init__.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/demos/summarization/summarizer_app.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/demos/topic_analysis/.streamlit/config.toml +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/demos/topic_analysis/__init__.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/demos/topic_analysis/app.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/demos/topic_analysis/app_utils.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/demos/topic_analysis/demo_pages/__init__.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/demos/topic_analysis/demo_pages/explore_topics.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/demos/topic_analysis/demo_pages/newsletters_generation.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/demos/topic_analysis/demo_pages/temporal_visualizations.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/demos/topic_analysis/demo_pages/topic_visualizations.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/demos/topic_analysis/demo_pages/training_page.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/demos/topic_analysis/i18n_translations.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/demos/weak_signals/.streamlit/config.toml +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/demos/weak_signals/__init__.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/demos/weak_signals/app.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/demos/weak_signals/i18n_translations.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/demos/weak_signals/visualizations_utils.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/llm_utils/__init__.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/llm_utils/newsletter.css +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/llm_utils/newsletter_features.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/llm_utils/newsletter_model.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/llm_utils/newsletter_outlook_template.html +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/llm_utils/newsletter_template.html +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/llm_utils/openai_client.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/llm_utils/prompts.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/metrics/__init__.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/metrics/temporal_metrics_embedding.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/metrics/topic_metrics.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/resources/common_ngrams.json +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/resources/stopwords-en.json +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/resources/stopwords-fr.json +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/resources/stopwords-rte.json +0 -0
- {bertrend-0.4.0/bertrend/bertrend_apps/services/bertrend/routers → bertrend-0.4.2/bertrend/services}/__init__.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/services/authentication.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/services/cert.pem +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/services/embedding_client.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/services/embedding_server/README.md +0 -0
- {bertrend-0.4.0/bertrend/bertrend_apps/services/utils → bertrend-0.4.2/bertrend/services/embedding_server}/__init__.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/services/embedding_server/bertrend_client_registry.json +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/services/embedding_server/config/default_config.toml +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/services/embedding_server/config/settings.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/services/embedding_server/main.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/services/embedding_server/models.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/services/embedding_server/requirements.txt +0 -0
- {bertrend-0.4.0/bertrend/services → bertrend-0.4.2/bertrend/services/embedding_server/routers}/__init__.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/services/embedding_server/routers/authentication.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/services/embedding_server/routers/embeddings.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/services/embedding_server/routers/info.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/services/embedding_server/security.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/services/embedding_server/start.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/services/embedding_server/status.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/services/embedding_server/stop.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/services/embedding_service.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/services/key.pem +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/services/scheduling/.dockerignore +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/services/scheduling/Docker_setup.md +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/services/scheduling/Dockerfile +0 -0
- {bertrend-0.4.0/bertrend/services/embedding_server → bertrend-0.4.2/bertrend/services/scheduling}/__init__.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/services/scheduling/dashboard.html +0 -0
- {bertrend-0.4.0/bertrend/services/embedding_server/routers → bertrend-0.4.2/bertrend/services/scheduling/job_utils}/__init__.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/services/scheduling/job_utils/job_functions.py +0 -0
- {bertrend-0.4.0/bertrend/services/scheduling → bertrend-0.4.2/bertrend/services/scheduling/models}/__init__.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/services/scheduling/models/scheduling_models.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/services/scheduling/requirements.txt +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/services/scheduling/routers/info.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/services/scheduling/routers/scheduling.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/services/scheduling/scheduling_service.py +0 -0
- {bertrend-0.4.0/bertrend/services/scheduling/job_utils → bertrend-0.4.2/bertrend/services/summary}/__init__.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/services/summary/abstractive_summarizer.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/services/summary/chatgpt_summarizer.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/services/summary/extractive_summarizer.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/services/summary/lexrank.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/services/summary/prompts.py +0 -0
- {bertrend-0.4.0/bertrend/services/scheduling/models → bertrend-0.4.2/bertrend/services/summary_server}/__init__.py +0 -0
- {bertrend-0.4.0/bertrend/services/summary → bertrend-0.4.2/bertrend/services/summary_server/config}/__init__.py +0 -0
- {bertrend-0.4.0/bertrend/tests → bertrend-0.4.2/bertrend/services/summary_server/routers}/__init__.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/tests/README_integration_tests.md +0 -0
- {bertrend-0.4.0/bertrend/tests/apps → bertrend-0.4.2/bertrend/tests}/__init__.py +0 -0
- {bertrend-0.4.0/bertrend/tests/core → bertrend-0.4.2/bertrend/tests/apps}/__init__.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/tests/apps/test_bertrend_service.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/tests/apps/test_lazy_scheduler.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/tests/apps/test_models.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/tests/apps/test_multiworker.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/tests/apps/test_process_new_data.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/tests/apps/test_routers_bertrend_app.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/tests/apps/test_routers_info.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/tests/conftest.py +0 -0
- {bertrend-0.4.0/bertrend/tests/llm → bertrend-0.4.2/bertrend/tests/core}/__init__.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/tests/core/test_bertrend.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/tests/core/test_data_structure.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/tests/core/test_representative_docs.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/tests/core/test_temporal_metrics_embedding.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/tests/core/test_topic_description.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/tests/core/test_topic_metrics.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/tests/core/test_topic_model.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/tests/core/test_trend_analysis_data_structure.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/tests/core/test_trend_analysis_prompts.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/tests/core/test_trend_analysis_visualizations.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/tests/core/test_trend_analysis_weak_signals.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/tests/core/test_visualizations.py +0 -0
- {bertrend-0.4.0/bertrend/tests/services → bertrend-0.4.2/bertrend/tests/data_provider}/__init__.py +0 -0
- {bertrend-0.4.0/bertrend/tests/services/embedding → bertrend-0.4.2/bertrend/tests/llm}/__init__.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/tests/llm/test_agent_utils.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/tests/llm/test_article_scoring.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/tests/llm/test_article_scoring_models.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/tests/llm/test_newsletter_features.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/tests/llm/test_newsletter_model.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/tests/llm/test_openai_client.py +0 -0
- {bertrend-0.4.0/bertrend/tests/services/queue → bertrend-0.4.2/bertrend/tests/services}/__init__.py +0 -0
- {bertrend-0.4.0/bertrend/tests/services/scheduling → bertrend-0.4.2/bertrend/tests/services/embedding}/__init__.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/tests/services/embedding/test_embedding_client.py +0 -0
- {bertrend-0.4.0/bertrend/tests/services/summarizer → bertrend-0.4.2/bertrend/tests/services/queue}/__init__.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/tests/services/queue/test_bertrend_worker.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/tests/services/queue/test_client.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/tests/services/queue/test_queue_manager.py +0 -0
- {bertrend-0.4.0/bertrend/tests/utils → bertrend-0.4.2/bertrend/tests/services/scheduling}/__init__.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/tests/services/scheduling/test_job_exception_fix.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/tests/services/scheduling/test_scheduling_service.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/tests/services/scheduling/test_stress_test.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/tests/services/summarizer/test_abstractive_summarizer.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/tests/services/summarizer/test_chatgpt_summarizer.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/tests/services/summarizer/test_extractive_summarizer.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/tests/test_installation.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/tests/test_integration.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/tests/utils/test_apscheduler_utils.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/tests/utils/test_cache_utils.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/tests/utils/test_config_utils.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/tests/utils/test_crontab_utils.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/tests/utils/test_data_loading.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/tests/utils/test_date_utils.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/tests/utils/test_mail_utils.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/tests/utils/test_scheduler_utils.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/tests/utils/test_utils_init.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/topic_analysis/__init__.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/topic_analysis/data_structure.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/topic_analysis/prompts.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/topic_analysis/representative_docs.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/topic_analysis/topic_description.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/topic_analysis/visualizations.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/trend_analysis/__init__.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/trend_analysis/data_structure.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/trend_analysis/prompts.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/trend_analysis/signal_llm_template_en.html +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/trend_analysis/signal_llm_template_fr.html +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/trend_analysis/visualizations.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/trend_analysis/weak_signals.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/utils/__init__.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/utils/cache_utils.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/bertrend/utils/config_utils.py +0 -0
- {bertrend-0.4.0 → bertrend-0.4.2}/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.
|
|
3
|
+
Version: 0.4.2
|
|
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
|
|
@@ -25,10 +25,13 @@ Requires-Dist: cron-descriptor (>=1.4.5)
|
|
|
25
25
|
Requires-Dist: dask (==2024.12.0)
|
|
26
26
|
Requires-Dist: datamapplot (==0.3.0)
|
|
27
27
|
Requires-Dist: dateparser (>=1.2.0)
|
|
28
|
+
Requires-Dist: ddgs (>=9.10.0)
|
|
28
29
|
Requires-Dist: dill (>=0.4.0)
|
|
29
30
|
Requires-Dist: fastapi (>=0.120.0)
|
|
30
31
|
Requires-Dist: feedparser (>=6.0.11)
|
|
32
|
+
Requires-Dist: folium (>=0.20.0)
|
|
31
33
|
Requires-Dist: gensim (>=4.3.3)
|
|
34
|
+
Requires-Dist: geopy (>=2.4.1)
|
|
32
35
|
Requires-Dist: google-api-python-client (>=2.148.0)
|
|
33
36
|
Requires-Dist: google-auth-oauthlib (>=1.2.1)
|
|
34
37
|
Requires-Dist: googlenewsdecoder (>=0.1.7)
|
|
@@ -69,6 +72,7 @@ Requires-Dist: sentencepiece (>=0.2.0)
|
|
|
69
72
|
Requires-Dist: starlette (>=0.46.2)
|
|
70
73
|
Requires-Dist: streamlit (>=1.51.0)
|
|
71
74
|
Requires-Dist: streamlit-code-editor (>=0.1.22)
|
|
75
|
+
Requires-Dist: streamlit-folium (>=0.26.1)
|
|
72
76
|
Requires-Dist: supervisor (>=4.3.0)
|
|
73
77
|
Requires-Dist: thefuzz (>=0.22.1)
|
|
74
78
|
Requires-Dist: tiktoken (>=0.8.0)
|
|
@@ -27,8 +27,6 @@ NUM_RETRIES = 10
|
|
|
27
27
|
query = "cat:cs.AI"
|
|
28
28
|
max_results = float("inf")
|
|
29
29
|
|
|
30
|
-
### Parameters Semantic Scholar ###
|
|
31
|
-
SEMANTIC_SCHOLAR_API_KEY = os.getenv("SEMANTIC_SCHOLAR_API_KEY")
|
|
32
30
|
|
|
33
31
|
PAPERS_PER_REQUEST = 500 # should not be more than 500
|
|
34
32
|
SEMANTIC_SCHOLAR_FIELDS = "title,abstract,citationCount,publicationDate,url"
|
|
@@ -130,14 +128,15 @@ class ArxivProvider(DataProvider):
|
|
|
130
128
|
@wait(1)
|
|
131
129
|
def _request_semantic_scholar_chunk(self, chunk: list[dict]):
|
|
132
130
|
"""Get information from semantic scholar API per batch of articles IDs"""
|
|
133
|
-
|
|
131
|
+
api_key = os.getenv("SEMANTIC_SCHOLAR_API_KEY")
|
|
132
|
+
if not api_key:
|
|
134
133
|
raise ValueError("SEMANTIC_SCHOLAR_API_KEY environment variable is not set")
|
|
135
134
|
ids_list = ["URL:" + entry["id"] for entry in chunk]
|
|
136
135
|
with requests.post(
|
|
137
136
|
"https://api.semanticscholar.org/graph/v1/paper/batch",
|
|
138
137
|
params={"fields": SEMANTIC_SCHOLAR_FIELDS},
|
|
139
138
|
json={"ids": ids_list},
|
|
140
|
-
headers={"x-api-key":
|
|
139
|
+
headers={"x-api-key": api_key},
|
|
141
140
|
) as response:
|
|
142
141
|
return [item for item in response.json() if item is not None]
|
|
143
142
|
|
{bertrend-0.4.0 → bertrend-0.4.2}/bertrend/bertrend_apps/data_provider/data_provider_utils.py
RENAMED
|
@@ -14,6 +14,9 @@ from bertrend.bertrend_apps.common.date_utils import daterange
|
|
|
14
14
|
from bertrend.bertrend_apps.data_provider.arxiv_provider import ArxivProvider
|
|
15
15
|
from bertrend.bertrend_apps.data_provider.atom_feed_provider import ATOMFeedProvider
|
|
16
16
|
from bertrend.bertrend_apps.data_provider.bing_news_provider import BingNewsProvider
|
|
17
|
+
from bertrend.bertrend_apps.data_provider.deep_research_provider import (
|
|
18
|
+
DeepResearchProvider,
|
|
19
|
+
)
|
|
17
20
|
from bertrend.bertrend_apps.data_provider.google_news_provider import GoogleNewsProvider
|
|
18
21
|
from bertrend.bertrend_apps.data_provider.newscatcher_provider import (
|
|
19
22
|
NewsCatcherProvider,
|
|
@@ -27,6 +30,7 @@ PROVIDERS = {
|
|
|
27
30
|
"google": GoogleNewsProvider,
|
|
28
31
|
"bing": BingNewsProvider,
|
|
29
32
|
"newscatcher": NewsCatcherProvider,
|
|
33
|
+
"deep_research": DeepResearchProvider,
|
|
30
34
|
}
|
|
31
35
|
|
|
32
36
|
|
|
@@ -145,8 +149,11 @@ def scrape_feed_from_config(
|
|
|
145
149
|
|
|
146
150
|
# Generate a query file
|
|
147
151
|
with tempfile.NamedTemporaryFile() as query_file:
|
|
148
|
-
if (
|
|
149
|
-
|
|
152
|
+
if provider in (
|
|
153
|
+
"arxiv",
|
|
154
|
+
"atom",
|
|
155
|
+
"rss",
|
|
156
|
+
"deep_research",
|
|
150
157
|
): # already returns batches
|
|
151
158
|
scrape(
|
|
152
159
|
keywords=keywords,
|
|
@@ -0,0 +1,501 @@
|
|
|
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
|
+
import asyncio
|
|
7
|
+
import os
|
|
8
|
+
import time
|
|
9
|
+
from datetime import datetime
|
|
10
|
+
|
|
11
|
+
from agents import Runner, function_tool
|
|
12
|
+
from ddgs import DDGS
|
|
13
|
+
from dotenv import load_dotenv
|
|
14
|
+
from loguru import logger
|
|
15
|
+
from pydantic import BaseModel, Field
|
|
16
|
+
|
|
17
|
+
from bertrend.bertrend_apps.data_provider.data_provider import DataProvider
|
|
18
|
+
from bertrend.bertrend_apps.data_provider.utils import wait
|
|
19
|
+
from bertrend.llm_utils.agent_utils import BaseAgentFactory, run_config_no_tracing
|
|
20
|
+
|
|
21
|
+
load_dotenv(override=True)
|
|
22
|
+
|
|
23
|
+
DEFAULT_MODEL = os.getenv("OPENAI_DEFAULT_MODEL", "gpt-4.1-mini")
|
|
24
|
+
DEFAULT_MAX_SUB_QUERIES = 5
|
|
25
|
+
DEFAULT_SEARCH_DELAY = 3.0 # seconds between DuckDuckGo requests to avoid rate limiting
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
# ----------------------------------------------------------------------------
|
|
29
|
+
# Custom web search (to ensure it works both with openAI and
|
|
30
|
+
# and openAI compatible (ex. LiteLLM) environments)
|
|
31
|
+
# ----------------------------------------------------------------------------
|
|
32
|
+
def _make_news_search_tool(
|
|
33
|
+
collected_articles: list[dict],
|
|
34
|
+
language_code: str = "us-en",
|
|
35
|
+
):
|
|
36
|
+
"""Create a news_search function tool that also records every article found.
|
|
37
|
+
|
|
38
|
+
Parameters
|
|
39
|
+
----------
|
|
40
|
+
collected_articles : list[dict]
|
|
41
|
+
Mutable list where every raw article dict returned by DuckDuckGo
|
|
42
|
+
will be appended, so that no URL is lost even if the LLM agent
|
|
43
|
+
omits it from its structured output.
|
|
44
|
+
"""
|
|
45
|
+
|
|
46
|
+
@function_tool
|
|
47
|
+
def news_search(
|
|
48
|
+
query: str,
|
|
49
|
+
max_results: int = 10,
|
|
50
|
+
timelimit: str = None,
|
|
51
|
+
) -> list[dict]:
|
|
52
|
+
"""Search for recent news articles.
|
|
53
|
+
|
|
54
|
+
Args:
|
|
55
|
+
query: The search query string.
|
|
56
|
+
max_results: Maximum number of results to return.
|
|
57
|
+
timelimit: Time limit for results (e.g., 'd' for day, 'w' for week, 'm' for month).
|
|
58
|
+
|
|
59
|
+
Returns:
|
|
60
|
+
List of article dicts with title, url, date, source, body
|
|
61
|
+
"""
|
|
62
|
+
try:
|
|
63
|
+
time.sleep(DEFAULT_SEARCH_DELAY)
|
|
64
|
+
with DDGS() as ddgs:
|
|
65
|
+
kwargs = {"query": query, "max_results": max_results}
|
|
66
|
+
kwargs["region"] = language_code
|
|
67
|
+
|
|
68
|
+
if timelimit:
|
|
69
|
+
kwargs["timelimit"] = timelimit
|
|
70
|
+
news_results = list(ddgs.news(**kwargs))
|
|
71
|
+
|
|
72
|
+
if not news_results:
|
|
73
|
+
return []
|
|
74
|
+
|
|
75
|
+
articles = [
|
|
76
|
+
{
|
|
77
|
+
"title": article.get("title", "No title"),
|
|
78
|
+
"url": article.get("url", ""),
|
|
79
|
+
"date": article.get("date", ""),
|
|
80
|
+
"source": article.get("source", ""),
|
|
81
|
+
"body": article.get("body", ""),
|
|
82
|
+
}
|
|
83
|
+
for article in news_results
|
|
84
|
+
]
|
|
85
|
+
|
|
86
|
+
# Record every article so the aggregation step never loses URLs, avoiding duplicates
|
|
87
|
+
collected_articles.extend(articles)
|
|
88
|
+
|
|
89
|
+
return articles
|
|
90
|
+
except Exception as e:
|
|
91
|
+
logger.error(f"News search error: {str(e)}")
|
|
92
|
+
return []
|
|
93
|
+
|
|
94
|
+
return news_search
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
# ---------------------------------------------------------------------------
|
|
98
|
+
# Pydantic models for structured agent outputs
|
|
99
|
+
# ---------------------------------------------------------------------------
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
class ResearchPlan(BaseModel):
|
|
103
|
+
"""Output of the planning step: a list of targeted sub-questions."""
|
|
104
|
+
|
|
105
|
+
sub_queries: list[str] = Field(
|
|
106
|
+
description="List of specific, targeted sub-questions to research"
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
class SubQueryResult(BaseModel):
|
|
111
|
+
"""Output of a single research sub-query."""
|
|
112
|
+
|
|
113
|
+
sub_query: str = Field(description="The sub-question that was researched")
|
|
114
|
+
findings: str = Field(
|
|
115
|
+
description="Detailed findings from web research on this sub-question"
|
|
116
|
+
)
|
|
117
|
+
source_urls: list[str] = Field(
|
|
118
|
+
default_factory=list,
|
|
119
|
+
description="URLs of sources consulted for this sub-query",
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
# ---------------------------------------------------------------------------
|
|
124
|
+
# System prompts for each step
|
|
125
|
+
# ---------------------------------------------------------------------------
|
|
126
|
+
|
|
127
|
+
PLAN_PROMPT = """You are a research planning expert. Given a topic and date range, break it down into specific, targeted sub-questions that together will provide comprehensive coverage of the topic.
|
|
128
|
+
|
|
129
|
+
Each sub-question should:
|
|
130
|
+
- Be specific enough to yield focused search results
|
|
131
|
+
- Cover a different angle or aspect of the topic
|
|
132
|
+
- Be answerable through web research
|
|
133
|
+
|
|
134
|
+
Decide how many sub-questions are needed based on the complexity and breadth of the topic. Use fewer sub-questions for narrow or simple topics and more for broad or complex ones. You must return between 1 and {max_sub_queries} sub-questions (inclusive)."""
|
|
135
|
+
|
|
136
|
+
RESEARCH_PROMPT = """You are an Advanced Web Research Analyst. Your task is to investigate a specific user query, synthesize findings from high-quality sources, and provide a curated list of references.
|
|
137
|
+
|
|
138
|
+
**Search Constraints & Source Selection:**
|
|
139
|
+
1. **Accessibility First:** Do NOT cite sources that are behind strict paywalls, require user registration, or are likely to be broken (404).
|
|
140
|
+
2. **Quality Control:** Prioritize primary sources, official documentation, academic institutions, and reputable journalism. Strictly exclude SEO-spam, content farms, clickbait, and low-quality aggregators.
|
|
141
|
+
3. **Information Utility (No Redundancy):** Only include a new URL if it provides *unique* value (e.g., a distinct statistic, a counter-argument, or a primary account) not found in the other sources. Do not list multiple sources that merely regurgitate the exact same AP wire or press release.
|
|
142
|
+
|
|
143
|
+
**Research Guidelines:**
|
|
144
|
+
- **Scope:** Focus on information from the date range: {after} to {before}.
|
|
145
|
+
- **Depth:** Look for specific data points, hard statistics, exact dates, and key stakeholder names.
|
|
146
|
+
- **Nuance:** actively seek out and highlight conflicting information or distinct viewpoints between sources.
|
|
147
|
+
|
|
148
|
+
**Output Format:**
|
|
149
|
+
1. **Executive Summary:** A concise answer to the query.
|
|
150
|
+
2. **Curated Source List:**
|
|
151
|
+
- Format: [Title](URL)
|
|
152
|
+
- *Requirement:* For each URL you consulted, add a one-line note explaining *what unique information* this specific link contributes (e.g., "Contains the 2024 financial table" or "Provides the opposing legal argument").
|
|
153
|
+
|
|
154
|
+
"""
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
class DeepResearchProvider(DataProvider):
|
|
158
|
+
"""Data provider that uses an agent-based approach to perform
|
|
159
|
+
multi-step deep web research and return individual news articles
|
|
160
|
+
with their URLs.
|
|
161
|
+
|
|
162
|
+
The research process follows two steps:
|
|
163
|
+
1. PLAN — Break the query into targeted sub-questions
|
|
164
|
+
2. RESEARCH — Search the web for each sub-question independently (in parallel)
|
|
165
|
+
|
|
166
|
+
URLs are deduplicated across all sub-queries and each unique URL is
|
|
167
|
+
returned as a separate article entry.
|
|
168
|
+
"""
|
|
169
|
+
|
|
170
|
+
def __init__(
|
|
171
|
+
self,
|
|
172
|
+
model: str = None,
|
|
173
|
+
max_sub_queries: int = DEFAULT_MAX_SUB_QUERIES,
|
|
174
|
+
parallel_research: bool = True,
|
|
175
|
+
):
|
|
176
|
+
# Do NOT call super().__init__() — we don't need Goose3 article parser
|
|
177
|
+
# since text is generated by the agent, not scraped from URLs.
|
|
178
|
+
self.model = model or DEFAULT_MODEL
|
|
179
|
+
self.max_sub_queries = max_sub_queries
|
|
180
|
+
self.parallel_research = parallel_research
|
|
181
|
+
self._factory = BaseAgentFactory(model_name=self.model)
|
|
182
|
+
|
|
183
|
+
# ------------------------------------------------------------------
|
|
184
|
+
# Step 1: PLAN
|
|
185
|
+
# ------------------------------------------------------------------
|
|
186
|
+
|
|
187
|
+
def _plan(
|
|
188
|
+
self, query: str, after: str, before: str, language: str = None
|
|
189
|
+
) -> list[str]:
|
|
190
|
+
"""Break the research query into targeted sub-questions."""
|
|
191
|
+
agent = self._factory.create_agent(
|
|
192
|
+
name="research_planner",
|
|
193
|
+
instructions=PLAN_PROMPT.format(max_sub_queries=self.max_sub_queries),
|
|
194
|
+
output_type=ResearchPlan,
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
prompt = (
|
|
198
|
+
f"Topic: {query}\n"
|
|
199
|
+
f"Date range: {after} to {before}\n"
|
|
200
|
+
f"Current date: {datetime.now().strftime('%Y-%m-%d')}"
|
|
201
|
+
)
|
|
202
|
+
if language:
|
|
203
|
+
prompt += f"\nGenerate sub-questions in {language}."
|
|
204
|
+
|
|
205
|
+
result = Runner.run_sync(agent, input=prompt, run_config=run_config_no_tracing)
|
|
206
|
+
plan: ResearchPlan = result.final_output
|
|
207
|
+
return plan.sub_queries
|
|
208
|
+
|
|
209
|
+
# ------------------------------------------------------------------
|
|
210
|
+
# Step 2: RESEARCH each sub-question
|
|
211
|
+
# ------------------------------------------------------------------
|
|
212
|
+
|
|
213
|
+
def _research_sub_query(
|
|
214
|
+
self,
|
|
215
|
+
sub_query: str,
|
|
216
|
+
after: str,
|
|
217
|
+
before: str,
|
|
218
|
+
collected_articles: list[dict] | None = None,
|
|
219
|
+
language_code: str = "us-en",
|
|
220
|
+
) -> SubQueryResult:
|
|
221
|
+
"""Search the web for a single sub-question and return findings."""
|
|
222
|
+
if collected_articles is None:
|
|
223
|
+
collected_articles = []
|
|
224
|
+
tool = _make_news_search_tool(collected_articles, language_code)
|
|
225
|
+
agent = self._factory.create_agent(
|
|
226
|
+
name="web_researcher",
|
|
227
|
+
instructions=RESEARCH_PROMPT.format(after=after, before=before),
|
|
228
|
+
tools=[tool],
|
|
229
|
+
output_type=SubQueryResult,
|
|
230
|
+
)
|
|
231
|
+
|
|
232
|
+
result = Runner.run_sync(
|
|
233
|
+
agent, input=sub_query, run_config=run_config_no_tracing
|
|
234
|
+
)
|
|
235
|
+
return result.final_output
|
|
236
|
+
|
|
237
|
+
async def _research_sub_query_async(
|
|
238
|
+
self,
|
|
239
|
+
sub_query: str,
|
|
240
|
+
after: str,
|
|
241
|
+
before: str,
|
|
242
|
+
collected_articles: list[dict] | None = None,
|
|
243
|
+
language_code: str = "us-en",
|
|
244
|
+
) -> SubQueryResult:
|
|
245
|
+
"""Async version of _research_sub_query for parallel execution."""
|
|
246
|
+
if collected_articles is None:
|
|
247
|
+
collected_articles = []
|
|
248
|
+
tool = _make_news_search_tool(collected_articles, language_code)
|
|
249
|
+
agent = self._factory.create_agent(
|
|
250
|
+
name="web_researcher",
|
|
251
|
+
instructions=RESEARCH_PROMPT.format(after=after, before=before),
|
|
252
|
+
tools=[tool],
|
|
253
|
+
output_type=SubQueryResult,
|
|
254
|
+
)
|
|
255
|
+
|
|
256
|
+
result = await Runner.run(
|
|
257
|
+
agent, input=sub_query, run_config=run_config_no_tracing
|
|
258
|
+
)
|
|
259
|
+
return result.final_output
|
|
260
|
+
|
|
261
|
+
async def _research_all_async(
|
|
262
|
+
self,
|
|
263
|
+
sub_queries: list[str],
|
|
264
|
+
after: str,
|
|
265
|
+
before: str,
|
|
266
|
+
collected_articles: list[dict] | None = None,
|
|
267
|
+
language_code: str = "us-en",
|
|
268
|
+
) -> list[SubQueryResult]:
|
|
269
|
+
"""Research all sub-queries concurrently and return successful results."""
|
|
270
|
+
if collected_articles is None:
|
|
271
|
+
collected_articles = []
|
|
272
|
+
tasks = [
|
|
273
|
+
self._research_sub_query_async(
|
|
274
|
+
sq, after, before, collected_articles, language_code
|
|
275
|
+
)
|
|
276
|
+
for sq in sub_queries
|
|
277
|
+
]
|
|
278
|
+
results = await asyncio.gather(*tasks, return_exceptions=True)
|
|
279
|
+
|
|
280
|
+
findings: list[SubQueryResult] = []
|
|
281
|
+
for i, result in enumerate(results):
|
|
282
|
+
if isinstance(result, Exception):
|
|
283
|
+
logger.warning(f" → Sub-query {i + 1} failed: {result} (skipping)")
|
|
284
|
+
else:
|
|
285
|
+
n_sources = len(result.source_urls)
|
|
286
|
+
logger.info(
|
|
287
|
+
f" → Sub-query {i + 1}/{len(sub_queries)} complete: "
|
|
288
|
+
f"{n_sources} source(s)"
|
|
289
|
+
)
|
|
290
|
+
findings.append(result)
|
|
291
|
+
return findings
|
|
292
|
+
|
|
293
|
+
# ------------------------------------------------------------------
|
|
294
|
+
# Main entry point (DataProvider interface)
|
|
295
|
+
# ------------------------------------------------------------------
|
|
296
|
+
|
|
297
|
+
@wait(2)
|
|
298
|
+
def get_articles(
|
|
299
|
+
self,
|
|
300
|
+
query: str,
|
|
301
|
+
after: str,
|
|
302
|
+
before: str,
|
|
303
|
+
max_results: int,
|
|
304
|
+
language: str = None,
|
|
305
|
+
) -> list[dict]:
|
|
306
|
+
"""Perform multi-step deep web research and return individual articles.
|
|
307
|
+
|
|
308
|
+
The process follows two steps:
|
|
309
|
+
1. PLAN — Break the query into targeted sub-questions
|
|
310
|
+
2. RESEARCH — Search the web for each sub-question (in parallel)
|
|
311
|
+
|
|
312
|
+
URLs are deduplicated across all sub-queries and each unique URL is
|
|
313
|
+
returned as a separate article entry.
|
|
314
|
+
|
|
315
|
+
Parameters
|
|
316
|
+
----------
|
|
317
|
+
query : str
|
|
318
|
+
Keywords or topic to research.
|
|
319
|
+
after : str
|
|
320
|
+
Start date formatted as YYYY-MM-DD.
|
|
321
|
+
before : str
|
|
322
|
+
End date formatted as YYYY-MM-DD.
|
|
323
|
+
max_results : int
|
|
324
|
+
Maximum number of articles to return.
|
|
325
|
+
language : str, optional
|
|
326
|
+
Language hint (unused, kept for interface compatibility).
|
|
327
|
+
|
|
328
|
+
Returns
|
|
329
|
+
-------
|
|
330
|
+
list[dict]
|
|
331
|
+
A list of article dicts with keys: title, text, summary, url,
|
|
332
|
+
link, timestamp.
|
|
333
|
+
"""
|
|
334
|
+
logger.info(
|
|
335
|
+
f"DeepResearchProvider: starting multi-step research for '{query}' "
|
|
336
|
+
f"(date range: {after} to {before}, max_results: {max_results})"
|
|
337
|
+
)
|
|
338
|
+
articles = []
|
|
339
|
+
try:
|
|
340
|
+
result = self._run_research_pipeline(query, after, before, language)
|
|
341
|
+
if result is not None:
|
|
342
|
+
articles = result
|
|
343
|
+
except Exception as e:
|
|
344
|
+
logger.error(
|
|
345
|
+
f"DeepResearchProvider: error in research pipeline "
|
|
346
|
+
f"for query '{query}': {e}"
|
|
347
|
+
)
|
|
348
|
+
|
|
349
|
+
# Respect max_results
|
|
350
|
+
articles = articles[:max_results]
|
|
351
|
+
|
|
352
|
+
logger.info(
|
|
353
|
+
f"DeepResearchProvider: completed with {len(articles)} article(s) "
|
|
354
|
+
f"for query '{query}'"
|
|
355
|
+
)
|
|
356
|
+
return articles
|
|
357
|
+
|
|
358
|
+
def _run_research_pipeline(
|
|
359
|
+
self, query: str, after: str, before: str, language: str = None
|
|
360
|
+
) -> list[dict] | None:
|
|
361
|
+
"""Execute the full PLAN → RESEARCH → AGGREGATE pipeline.
|
|
362
|
+
|
|
363
|
+
Returns a list of article dicts with deduplicated URLs from all
|
|
364
|
+
sub-query findings.
|
|
365
|
+
"""
|
|
366
|
+
language_code = "fr-fr" if language == "fr" else "us-en"
|
|
367
|
+
|
|
368
|
+
# Step 1: PLAN
|
|
369
|
+
logger.info("[PLAN] Breaking query into sub-questions...")
|
|
370
|
+
sub_queries = self._plan(query, after, before, language)
|
|
371
|
+
for j, sq in enumerate(sub_queries, 1):
|
|
372
|
+
logger.info(f" → Sub-question {j}/{len(sub_queries)}: {sq}")
|
|
373
|
+
|
|
374
|
+
# Step 2: RESEARCH each sub-question
|
|
375
|
+
use_async = self.parallel_research
|
|
376
|
+
if use_async:
|
|
377
|
+
try:
|
|
378
|
+
loop = asyncio.get_running_loop()
|
|
379
|
+
if loop.is_running():
|
|
380
|
+
# Already inside an async context — fall back to sequential
|
|
381
|
+
use_async = False
|
|
382
|
+
except RuntimeError:
|
|
383
|
+
pass
|
|
384
|
+
|
|
385
|
+
# collected_articles accumulates every raw article dict returned by
|
|
386
|
+
# the news_search tool, so that no URL is lost even if the LLM agent
|
|
387
|
+
# omits it from its structured SubQueryResult.source_urls.
|
|
388
|
+
collected_articles: list[dict] = []
|
|
389
|
+
|
|
390
|
+
if use_async:
|
|
391
|
+
logger.info(
|
|
392
|
+
f"[RESEARCH] Searching {len(sub_queries)} sub-queries in parallel..."
|
|
393
|
+
)
|
|
394
|
+
findings = asyncio.run(
|
|
395
|
+
self._research_all_async(
|
|
396
|
+
sub_queries, after, before, collected_articles, language_code
|
|
397
|
+
)
|
|
398
|
+
)
|
|
399
|
+
else:
|
|
400
|
+
logger.info(
|
|
401
|
+
f"[RESEARCH] Searching {len(sub_queries)} sub-queries sequentially..."
|
|
402
|
+
)
|
|
403
|
+
findings: list[SubQueryResult] = []
|
|
404
|
+
for j, sq in enumerate(sub_queries, 1):
|
|
405
|
+
logger.info(f'[RESEARCH {j}/{len(sub_queries)}] Searching: "{sq}"')
|
|
406
|
+
try:
|
|
407
|
+
sub_result = self._research_sub_query(
|
|
408
|
+
sq, after, before, collected_articles, language_code
|
|
409
|
+
)
|
|
410
|
+
findings.append(sub_result)
|
|
411
|
+
n_sources = len(sub_result.source_urls)
|
|
412
|
+
logger.info(f" → Found {n_sources} source(s)")
|
|
413
|
+
except Exception as e:
|
|
414
|
+
logger.warning(f" → Sub-query {j} failed: {e} (skipping)")
|
|
415
|
+
continue
|
|
416
|
+
|
|
417
|
+
if not findings and not collected_articles:
|
|
418
|
+
logger.error("DeepResearchProvider: all sub-queries failed, no findings")
|
|
419
|
+
return None
|
|
420
|
+
|
|
421
|
+
# Step 3: AGGREGATE deduplicated URLs
|
|
422
|
+
logger.info(
|
|
423
|
+
f"[AGGREGATE] Deduplicating URLs from {len(findings)} sub-query results "
|
|
424
|
+
f"and {len(collected_articles)} raw search result(s)..."
|
|
425
|
+
)
|
|
426
|
+
return self._aggregate_articles(findings, collected_articles)
|
|
427
|
+
|
|
428
|
+
def _parse_entry(self, entry: dict) -> dict | None:
|
|
429
|
+
"""Not used — aggregation is handled by _aggregate_articles."""
|
|
430
|
+
return entry
|
|
431
|
+
|
|
432
|
+
def _aggregate_articles(
|
|
433
|
+
self,
|
|
434
|
+
findings: list[SubQueryResult],
|
|
435
|
+
collected_articles: list[dict] | None = None,
|
|
436
|
+
) -> list[dict]:
|
|
437
|
+
"""Aggregate and deduplicate URLs from all sub-query findings and
|
|
438
|
+
raw search results.
|
|
439
|
+
|
|
440
|
+
Each unique URL becomes a separate article entry. The raw
|
|
441
|
+
``collected_articles`` (captured directly from the news_search tool)
|
|
442
|
+
are the primary source of truth so that no URL is lost even when the
|
|
443
|
+
LLM agent omits it from its structured output.
|
|
444
|
+
|
|
445
|
+
Parameters
|
|
446
|
+
----------
|
|
447
|
+
findings : list[SubQueryResult]
|
|
448
|
+
Results from all sub-query research steps.
|
|
449
|
+
collected_articles : list[dict] | None
|
|
450
|
+
Raw article dicts captured directly from the news_search tool.
|
|
451
|
+
|
|
452
|
+
Returns
|
|
453
|
+
-------
|
|
454
|
+
list[dict]
|
|
455
|
+
A list of article dicts with keys: title, text, summary, url,
|
|
456
|
+
link, timestamp.
|
|
457
|
+
"""
|
|
458
|
+
articles = []
|
|
459
|
+
seen_urls: set[str] = set()
|
|
460
|
+
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
|
461
|
+
|
|
462
|
+
# First, add every article captured directly from the search tool
|
|
463
|
+
# (this is the authoritative source — no URL can be lost here).
|
|
464
|
+
for raw in collected_articles or []:
|
|
465
|
+
url = raw.get("url", "")
|
|
466
|
+
if url and url not in seen_urls:
|
|
467
|
+
seen_urls.add(url)
|
|
468
|
+
# title = raw.get("title", "")
|
|
469
|
+
# body = raw.get("body", "")
|
|
470
|
+
text, title = self._get_text(url=url)
|
|
471
|
+
if text:
|
|
472
|
+
articles.append(
|
|
473
|
+
{
|
|
474
|
+
"title": title,
|
|
475
|
+
"text": text,
|
|
476
|
+
"summary": text[:200] if text else "",
|
|
477
|
+
"url": url,
|
|
478
|
+
"link": url,
|
|
479
|
+
"timestamp": timestamp,
|
|
480
|
+
}
|
|
481
|
+
)
|
|
482
|
+
|
|
483
|
+
# Then, add any URLs from the agent's structured output that the
|
|
484
|
+
# search tool might not have returned (e.g. URLs the agent found
|
|
485
|
+
# in page content).
|
|
486
|
+
for finding in findings:
|
|
487
|
+
for url in finding.source_urls:
|
|
488
|
+
if url and url not in seen_urls:
|
|
489
|
+
seen_urls.add(url)
|
|
490
|
+
articles.append(
|
|
491
|
+
{
|
|
492
|
+
"title": finding.sub_query,
|
|
493
|
+
"text": finding.findings,
|
|
494
|
+
"summary": finding.findings[:200],
|
|
495
|
+
"url": url,
|
|
496
|
+
"link": url,
|
|
497
|
+
"timestamp": timestamp,
|
|
498
|
+
}
|
|
499
|
+
)
|
|
500
|
+
|
|
501
|
+
return articles
|