forecasting-tools 0.3.3__tar.gz → 0.3.5__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.
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/PKG-INFO +3 -5
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/README.md +1 -3
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/data_models/multiple_choice_report.py +60 -22
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/data_models/numeric_report.py +11 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/helpers/metaculus_client.py +169 -26
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/util/misc.py +13 -1
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/pyproject.toml +2 -2
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/LICENSE +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/__init__.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/agents_and_tools/__init__.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/agents_and_tools/ai_congress_v2/__init__.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/agents_and_tools/ai_congress_v2/congress_member_agent.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/agents_and_tools/ai_congress_v2/congress_orchestrator.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/agents_and_tools/ai_congress_v2/data_models.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/agents_and_tools/ai_congress_v2/member_profiles.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/agents_and_tools/ai_congress_v2/tools.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/agents_and_tools/base_rates/base_rate_researcher.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/agents_and_tools/base_rates/deduplicator.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/agents_and_tools/base_rates/estimator.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/agents_and_tools/base_rates/niche_list_researcher.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/agents_and_tools/deprecated/configured_llms.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/agents_and_tools/deprecated/general_researcher.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/agents_and_tools/deprecated/question_generator.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/agents_and_tools/deprecated/question_responder.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/agents_and_tools/deprecated/question_router.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/agents_and_tools/deprecated/research_coordinator.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/agents_and_tools/minor_tools.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/agents_and_tools/other/data_analyzer.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/agents_and_tools/other/hosted_file.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/agents_and_tools/question_generators/generated_question.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/agents_and_tools/question_generators/harmful_question_identifier.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/agents_and_tools/question_generators/q3_q4_quarterly_questions.json +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/agents_and_tools/question_generators/question_decomposer.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/agents_and_tools/question_generators/question_operationalizer.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/agents_and_tools/question_generators/simple_question.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/agents_and_tools/question_generators/topic_generator.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/agents_and_tools/research/computer_use.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/agents_and_tools/research/find_a_dataset.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/agents_and_tools/research/key_factors_researcher.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/agents_and_tools/research/smart_searcher.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/agents_and_tools/situation_simulator/__init__.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/agents_and_tools/situation_simulator/agent_runner.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/agents_and_tools/situation_simulator/data_models.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/agents_and_tools/situation_simulator/effect_engine.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/agents_and_tools/situation_simulator/example_situations/industrial_basin.json +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/agents_and_tools/situation_simulator/example_situations/milbrook.json +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/agents_and_tools/situation_simulator/example_situations/simple_business.json +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/agents_and_tools/situation_simulator/example_situations/simple_dnd.json +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/agents_and_tools/situation_simulator/example_situations/simple_election.json +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/agents_and_tools/situation_simulator/example_situations/willowbrook.json +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/agents_and_tools/situation_simulator/example_situations/z_broken_mafia.json +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/agents_and_tools/situation_simulator/intervention_testing/__init__.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/agents_and_tools/situation_simulator/intervention_testing/data_models.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/agents_and_tools/situation_simulator/intervention_testing/forecast_resolver.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/agents_and_tools/situation_simulator/intervention_testing/intervention_policy_agent.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/agents_and_tools/situation_simulator/intervention_testing/intervention_runner.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/agents_and_tools/situation_simulator/intervention_testing/intervention_storage.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/agents_and_tools/situation_simulator/simulator.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/agents_and_tools/situation_simulator/situation_generator.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/agents_and_tools/source_archive/README.md +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/agents_and_tools/source_archive/__init__.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/agents_and_tools/source_archive/benchmark.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/agents_and_tools/source_archive/canonicalize.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/agents_and_tools/source_archive/catalog.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/agents_and_tools/source_archive/cli.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/agents_and_tools/source_archive/config.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/agents_and_tools/source_archive/content_store.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/agents_and_tools/source_archive/cost.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/agents_and_tools/source_archive/coverage.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/agents_and_tools/source_archive/fetchers/__init__.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/agents_and_tools/source_archive/fetchers/base.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/agents_and_tools/source_archive/fetchers/cloakbrowser_fetcher.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/agents_and_tools/source_archive/fetchers/firecrawl_fetcher.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/agents_and_tools/source_archive/fetchers/hyperbrowser_fetcher.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/agents_and_tools/source_archive/fetchers/pdf_fetcher.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/agents_and_tools/source_archive/fetchers/playwright_fetcher.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/agents_and_tools/source_archive/fetchers/tiered.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/agents_and_tools/source_archive/ingest/__init__.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/agents_and_tools/source_archive/ingest/trace_extraction.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/agents_and_tools/source_archive/ingest/url_extraction.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/agents_and_tools/source_archive/layout.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/agents_and_tools/source_archive/manifest.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/agents_and_tools/source_archive/models.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/agents_and_tools/source_archive/pipeline.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/agents_and_tools/source_archive/quality.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/agents_and_tools/source_archive/reindex.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/agents_and_tools/source_archive/reports.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/agents_and_tools/source_archive/storage/__init__.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/agents_and_tools/source_archive/storage/blob_store.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/agents_and_tools/source_archive/storage/local_store.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/agents_and_tools/source_archive/storage/s3_store.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/agents_and_tools/source_archive/viewer.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/ai_models/__init__.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/ai_models/agent_wrappers.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/ai_models/ai_utils/__init__.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/ai_models/ai_utils/openai_utils.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/ai_models/ai_utils/response_types.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/ai_models/deprecated_model_classes/README.md +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/ai_models/deprecated_model_classes/claude35sonnet.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/ai_models/deprecated_model_classes/deepseek_r1.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/ai_models/deprecated_model_classes/gpt4o.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/ai_models/deprecated_model_classes/gpt4ovision.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/ai_models/deprecated_model_classes/gpto1.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/ai_models/deprecated_model_classes/gpto1preview.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/ai_models/deprecated_model_classes/metaculus4o.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/ai_models/deprecated_model_classes/perplexity.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/ai_models/exa_searcher.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/ai_models/general_llm.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/ai_models/model_interfaces/__init__.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/ai_models/model_interfaces/ai_model.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/ai_models/model_interfaces/combined_llm_archetype.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/ai_models/model_interfaces/incurs_cost.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/ai_models/model_interfaces/named_model.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/ai_models/model_interfaces/outputs_text.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/ai_models/model_interfaces/priced_per_request.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/ai_models/model_interfaces/request_limited_model.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/ai_models/model_interfaces/retryable_model.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/ai_models/model_interfaces/time_limited_model.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/ai_models/model_interfaces/token_limited_model.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/ai_models/model_interfaces/tokens_are_calculatable.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/ai_models/model_interfaces/tokens_incur_cost.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/ai_models/model_tracker.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/ai_models/resource_managers/__init__.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/ai_models/resource_managers/hard_limit_manager.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/ai_models/resource_managers/monetary_cost_manager.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/ai_models/resource_managers/refreshing_bucket_rate_limiter.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/auto_optimizers/bot_evaluator.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/auto_optimizers/bot_optimizer.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/auto_optimizers/control_prompt.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/auto_optimizers/customizable_bot.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/auto_optimizers/prompt_data_models.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/auto_optimizers/prompt_optimizer.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/auto_optimizers/question_plus_research.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/calibration_adjustment/.gitignore +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/calibration_adjustment/__init__.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/calibration_adjustment/_training_data.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/calibration_adjustment/benchmark.ipynb +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/calibration_adjustment/calibration_adjuster.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/calibration_adjustment/constant_shift_adjuster.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/calibration_adjustment/decision_tree_adjuster.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/calibration_adjustment/k_means_adjuster.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/calibration_adjustment/logistic_recalibration_adjuster.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/calibration_adjustment/step_adjuster.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/cp_benchmarking/benchmark_displayer.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/cp_benchmarking/benchmark_for_bot.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/cp_benchmarking/benchmarker.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/data_models/__init__.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/data_models/binary_report.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/data_models/coherence_link.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/data_models/conditional_models.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/data_models/conditional_report.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/data_models/data_organizer.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/data_models/forecast_report.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/data_models/markdown_tree.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/data_models/questions.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/data_models/timestamped_predictions.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/data_models/user_response.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/forecast_bots/__init__.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/forecast_bots/bot_lists.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/forecast_bots/experiments/__init__.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/forecast_bots/experiments/q1_veritas_bot.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/forecast_bots/experiments/q1t_w_convert_to_binary.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/forecast_bots/experiments/q1t_w_personas_and_exa.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/forecast_bots/experiments/q2t_w_decomposition.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/forecast_bots/experiments/q3t_w_asknews.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/forecast_bots/experiments/q3t_w_exa.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/forecast_bots/experiments/q3t_w_q4vbinary.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/forecast_bots/experiments/q4_veritas_bot.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/forecast_bots/experiments/q4v_w_exa.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/forecast_bots/experiments/q4v_w_exa_and_dseekr1.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/forecast_bots/experiments/q4v_w_exa_and_o1_preview.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/forecast_bots/forecast_bot.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/forecast_bots/main_bot.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/forecast_bots/official_bots/__init__.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/forecast_bots/official_bots/gpt_4_1_optimized_bot.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/forecast_bots/official_bots/no_research_one_shot_bot.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/forecast_bots/official_bots/q1_template_bot.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/forecast_bots/official_bots/q2_template_bot.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/forecast_bots/official_bots/q3_template_bot.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/forecast_bots/official_bots/q4_template_bot.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/forecast_bots/official_bots/research_only_bot_2025_fall.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/forecast_bots/official_bots/template_bot_2025_fall.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/forecast_bots/official_bots/template_bot_2026_fall.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/forecast_bots/official_bots/template_bot_2026_spring.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/forecast_bots/official_bots/template_bot_2026_summer.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/forecast_bots/official_bots/uniform_probability_bot.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/forecast_bots/template_bot.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/front_end/Home.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/front_end/__init__.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/front_end/app_pages/base_rate_page.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/front_end/app_pages/benchmark_page.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/front_end/app_pages/chat_page.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/front_end/app_pages/congress_v2_page.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/front_end/app_pages/csv_agent.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/front_end/app_pages/estimator_page.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/front_end/app_pages/forecaster_page.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/front_end/app_pages/intervention_leaderboard_page.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/front_end/app_pages/key_factors_page.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/front_end/app_pages/niche_list_researcher_page.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/front_end/app_pages/simulator_page.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/front_end/example_outputs/base_rate_page_examples.json +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/front_end/example_outputs/congress_page_example.json +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/front_end/example_outputs/congress_v2_page_example.json +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/front_end/example_outputs/estimator_page_examples.json +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/front_end/example_outputs/forecast_page_examples.json +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/front_end/example_outputs/key_factors_page_examples.json +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/front_end/example_outputs/niche_list_page_examples.json +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/front_end/example_outputs/question_generator_page_examples.json +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/front_end/helpers/app_page.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/front_end/helpers/custom_auth.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/front_end/helpers/report_displayer.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/front_end/helpers/tool_page.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/helpers/__init__.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/helpers/asknews_cache.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/helpers/asknews_searcher.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/helpers/forecast_database_manager.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/helpers/metaculus_api.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/helpers/prediction_extractor.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/helpers/structure_output.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/helpers/works_cited_creator.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/util/__init__.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/util/async_batching.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/util/coda_utils.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/util/custom_logger.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/util/file_manipulation.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/util/jsonable.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/util/optional_imports.py +0 -0
- {forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/util/stats.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: forecasting-tools
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.5
|
|
4
4
|
Summary: AI forecasting and research tools to help humans reason about and forecast the future
|
|
5
5
|
License: MIT
|
|
6
6
|
License-File: LICENSE
|
|
@@ -35,7 +35,7 @@ Requires-Dist: exceptiongroup (>=1.2.2,<2.0.0)
|
|
|
35
35
|
Requires-Dist: faker (>=37.0.0,<41.0.0) ; extra == "agents" or extra == "front-end" or extra == "all"
|
|
36
36
|
Requires-Dist: firecrawl-py (>=4.0,<5.0.0) ; extra == "source-archive" or extra == "all"
|
|
37
37
|
Requires-Dist: hyperbrowser (>=0.53.0,<2.0.0) ; extra == "source-archive" or extra == "agents" or extra == "front-end" or extra == "all"
|
|
38
|
-
Requires-Dist: litellm (>=1.59.1,<2.0.0,!=1.76.*,!=1.77.*,!=1.82.7,!=1.82.8)
|
|
38
|
+
Requires-Dist: litellm (>=1.59.1,<2.0.0,!=1.76.*,!=1.77.*,!=1.82.7,!=1.82.8,!=1.107.*,!=1.108.*,!=1.109.*)
|
|
39
39
|
Requires-Dist: nest-asyncio (>=1.5.8,<2.0.0)
|
|
40
40
|
Requires-Dist: numpy (>=1.26.0,<3.0.0)
|
|
41
41
|
Requires-Dist: openai (>=1.51.0,<4.0.0)
|
|
@@ -71,8 +71,6 @@ Description-Content-Type: text/markdown
|
|
|
71
71
|
# Quick Start
|
|
72
72
|
Install this package with `pip install forecasting-tools`
|
|
73
73
|
|
|
74
|
-
Demo website: https://forecasting-tools.streamlit.app/
|
|
75
|
-
|
|
76
74
|
Demo repo (get a Metaculus bot running in 30min): https://github.com/Metaculus/metac-bot-template
|
|
77
75
|
|
|
78
76
|
# Overview
|
|
@@ -87,7 +85,7 @@ Here are the tools most likely to be useful to you:
|
|
|
87
85
|
Here are some other features of the project (not all are documented yet):
|
|
88
86
|
- **General LLM Wrapper:** A unified interface around litellm with retry logic, the Metaculus proxy, structured outputs, and cost tracking
|
|
89
87
|
- **Monetary Cost Manager:** for tracking AI and API expenses
|
|
90
|
-
- **Other experimental tools:** See the
|
|
88
|
+
- **Other experimental tools:** See the Streamlit front end in `forecasting_tools/front_end/` (see "Running the Front End" below) for other AI forecasting tools that this project supports (not all are documented). Also see the `scripts` folder for other common workflows and entry points into the code.
|
|
91
89
|
|
|
92
90
|
All the examples below are in a Jupyter Notebook called `README.ipynb` which you can run locally to test the package (make sure to run the first cell though).
|
|
93
91
|
|
|
@@ -9,8 +9,6 @@
|
|
|
9
9
|
# Quick Start
|
|
10
10
|
Install this package with `pip install forecasting-tools`
|
|
11
11
|
|
|
12
|
-
Demo website: https://forecasting-tools.streamlit.app/
|
|
13
|
-
|
|
14
12
|
Demo repo (get a Metaculus bot running in 30min): https://github.com/Metaculus/metac-bot-template
|
|
15
13
|
|
|
16
14
|
# Overview
|
|
@@ -25,7 +23,7 @@ Here are the tools most likely to be useful to you:
|
|
|
25
23
|
Here are some other features of the project (not all are documented yet):
|
|
26
24
|
- **General LLM Wrapper:** A unified interface around litellm with retry logic, the Metaculus proxy, structured outputs, and cost tracking
|
|
27
25
|
- **Monetary Cost Manager:** for tracking AI and API expenses
|
|
28
|
-
- **Other experimental tools:** See the
|
|
26
|
+
- **Other experimental tools:** See the Streamlit front end in `forecasting_tools/front_end/` (see "Running the Front End" below) for other AI forecasting tools that this project supports (not all are documented). Also see the `scripts` folder for other common workflows and entry points into the code.
|
|
29
27
|
|
|
30
28
|
All the examples below are in a Jupyter Notebook called `README.ipynb` which you can run locally to test the package (make sure to run the first cell though).
|
|
31
29
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import logging
|
|
4
|
+
import math
|
|
4
5
|
from typing import TYPE_CHECKING
|
|
5
6
|
|
|
6
7
|
from pydantic import BaseModel, Field, model_validator
|
|
@@ -33,29 +34,16 @@ class PredictedOptionList(BaseModel):
|
|
|
33
34
|
"too far from 1 to be confident that normalization will deliver an "
|
|
34
35
|
"intended prediction."
|
|
35
36
|
)
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
# Step 1: Clamp values
|
|
42
|
-
max_clamped_prob = 0.99
|
|
43
|
-
min_clamped_prob = 0.01
|
|
44
|
-
clamped_list = [
|
|
45
|
-
max(min(x.probability, max_clamped_prob), min_clamped_prob)
|
|
46
|
-
for x in self.predicted_options
|
|
47
|
-
]
|
|
48
|
-
|
|
49
|
-
# Step 2: Calculate the sum of all elements
|
|
50
|
-
total_sum_decimal = sum(clamped_list)
|
|
51
|
-
|
|
52
|
-
# Step 3: Normalize the list so that all elements add up to 1
|
|
53
|
-
normalized_list = [x / total_sum_decimal for x in clamped_list]
|
|
37
|
+
if not math.isclose(sum_of_probabilities, 1):
|
|
38
|
+
logger.warning(
|
|
39
|
+
f"Sum of option probabilities {sum_of_probabilities} is not 1, but is close to 1. "
|
|
40
|
+
"Normalizing the probabilities."
|
|
41
|
+
)
|
|
54
42
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
43
|
+
normalized_option_probabilities = self._normalize_with_minimum(
|
|
44
|
+
[option.probability for option in self.predicted_options],
|
|
45
|
+
minimum_probability=0.001,
|
|
46
|
+
)
|
|
59
47
|
|
|
60
48
|
new_sum = sum(normalized_option_probabilities)
|
|
61
49
|
assert (
|
|
@@ -81,6 +69,56 @@ class PredictedOptionList(BaseModel):
|
|
|
81
69
|
]
|
|
82
70
|
return self
|
|
83
71
|
|
|
72
|
+
@staticmethod
|
|
73
|
+
def _normalize_with_minimum(
|
|
74
|
+
probabilities: list[float], minimum_probability: float
|
|
75
|
+
) -> list[float]:
|
|
76
|
+
"""
|
|
77
|
+
Normalizes to sum to 1 while keeping every option at or above the minimum.
|
|
78
|
+
Options below the minimum are pinned to it and the excess is taken
|
|
79
|
+
proportionally from the remaining options, so with 2+ options no option
|
|
80
|
+
can exceed 1 - minimum_probability.
|
|
81
|
+
"""
|
|
82
|
+
if minimum_probability * len(probabilities) >= 1:
|
|
83
|
+
raise ValueError(
|
|
84
|
+
f"Cannot give all {len(probabilities)} options a probability of at least {minimum_probability}"
|
|
85
|
+
)
|
|
86
|
+
total = sum(probabilities)
|
|
87
|
+
unadjusted = [probability / total for probability in probabilities]
|
|
88
|
+
normalized = unadjusted
|
|
89
|
+
pinned_indices: set[int] = set()
|
|
90
|
+
while True:
|
|
91
|
+
newly_pinned_indices = {
|
|
92
|
+
index
|
|
93
|
+
for index, probability in enumerate(normalized)
|
|
94
|
+
if probability < minimum_probability and index not in pinned_indices
|
|
95
|
+
}
|
|
96
|
+
if not newly_pinned_indices:
|
|
97
|
+
break
|
|
98
|
+
pinned_indices |= newly_pinned_indices
|
|
99
|
+
unpinned_mass = 1 - minimum_probability * len(pinned_indices)
|
|
100
|
+
unpinned_total = sum(
|
|
101
|
+
probability
|
|
102
|
+
for index, probability in enumerate(unadjusted)
|
|
103
|
+
if index not in pinned_indices
|
|
104
|
+
)
|
|
105
|
+
normalized = [
|
|
106
|
+
(
|
|
107
|
+
minimum_probability
|
|
108
|
+
if index in pinned_indices
|
|
109
|
+
else probability * unpinned_mass / unpinned_total
|
|
110
|
+
)
|
|
111
|
+
for index, probability in enumerate(unadjusted)
|
|
112
|
+
]
|
|
113
|
+
|
|
114
|
+
largest_index = normalized.index(max(normalized))
|
|
115
|
+
normalized[largest_index] = 1.0 - sum(
|
|
116
|
+
probability
|
|
117
|
+
for index, probability in enumerate(normalized)
|
|
118
|
+
if index != largest_index
|
|
119
|
+
)
|
|
120
|
+
return normalized
|
|
121
|
+
|
|
84
122
|
def to_dict(self) -> dict[str, float]:
|
|
85
123
|
return {
|
|
86
124
|
option.option_name: option.probability for option in self.predicted_options
|
{forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/data_models/numeric_report.py
RENAMED
|
@@ -585,9 +585,20 @@ class NumericDistribution(BaseModel):
|
|
|
585
585
|
return float(cap_pmf(scale).sum())
|
|
586
586
|
|
|
587
587
|
# find the appropriate scale search space
|
|
588
|
+
# An increasing CDF needs at most ~46 expansions (scale <= cap / min bin = 3800), so hitting
|
|
589
|
+
# the limit means no scale works (e.g. negative bins from a non-increasing CDF)
|
|
590
|
+
max_scale_expansions = 100
|
|
588
591
|
lo = hi = scale = 1.0
|
|
592
|
+
expansions = 0
|
|
589
593
|
while capped_sum(hi) < 1.0:
|
|
594
|
+
if expansions >= max_scale_expansions:
|
|
595
|
+
raise ValueError(
|
|
596
|
+
"Could not standardize CDF: no scale makes the capped probability mass sum to 1. "
|
|
597
|
+
f"Smallest in-range bin probability is {pmf[1:-1].min():.3g} "
|
|
598
|
+
"(negative means the CDF is not increasing)."
|
|
599
|
+
)
|
|
590
600
|
hi *= 1.2
|
|
601
|
+
expansions += 1
|
|
591
602
|
# hone in on scale value that makes capped sum 1
|
|
592
603
|
for _ in range(100):
|
|
593
604
|
scale = 0.5 * (lo + hi)
|
{forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/helpers/metaculus_client.py
RENAMED
|
@@ -108,6 +108,16 @@ class AuthorMismatchError(ValueError):
|
|
|
108
108
|
class MetaculusClient:
|
|
109
109
|
"""
|
|
110
110
|
Documentation for the API can be found at https://www.metaculus.com/api/
|
|
111
|
+
|
|
112
|
+
Setting acting_user (a username or user id) makes the client forecast,
|
|
113
|
+
comment, create questions, and read "my forecasts" as that account while
|
|
114
|
+
authenticating with the token. The token must belong to the acting user's
|
|
115
|
+
bot owner, an account allowed to act as metac bots, or a superuser.
|
|
116
|
+
Getting a question by post id is done fully as the acting user, but
|
|
117
|
+
question lists are still fetched as the token's account, so a question only
|
|
118
|
+
the acting user can see will not be listed. Public comments and other
|
|
119
|
+
endpoints (e.g. coherence links, bot management) are not supported for the
|
|
120
|
+
acting user and act as the token's account or raise.
|
|
111
121
|
"""
|
|
112
122
|
|
|
113
123
|
# NOTE: The tourament slug can be used for ID as well (e.g. "aibq2" or "quarterly-cup" instead of 32721 or 32630)
|
|
@@ -174,6 +184,7 @@ class MetaculusClient:
|
|
|
174
184
|
sleep_seconds_between_requests: float = 3.5,
|
|
175
185
|
sleep_jitter_seconds: float = 1,
|
|
176
186
|
token: str | None = None,
|
|
187
|
+
acting_user: str | int | None = None,
|
|
177
188
|
):
|
|
178
189
|
default_base_url = "https://www.metaculus.com/api"
|
|
179
190
|
self.base_url = (
|
|
@@ -188,6 +199,7 @@ class MetaculusClient:
|
|
|
188
199
|
logger.warning(
|
|
189
200
|
"METACULUS_TOKEN environment variable and/or token field not set"
|
|
190
201
|
)
|
|
202
|
+
self.acting_user = acting_user
|
|
191
203
|
|
|
192
204
|
@retry_with_exponential_backoff()
|
|
193
205
|
def get_user_bots(self) -> list[UserResponse]:
|
|
@@ -250,15 +262,27 @@ class MetaculusClient:
|
|
|
250
262
|
is_private: bool = True,
|
|
251
263
|
included_forecast: bool = True,
|
|
252
264
|
) -> None:
|
|
265
|
+
comment = {
|
|
266
|
+
"on_post": post_id,
|
|
267
|
+
"text": comment_text,
|
|
268
|
+
"is_private": is_private,
|
|
269
|
+
"included_forecast": included_forecast,
|
|
270
|
+
}
|
|
271
|
+
if self.acting_user is not None:
|
|
272
|
+
if not is_private:
|
|
273
|
+
raise ValueError(
|
|
274
|
+
"Public comments cannot be posted with acting_user set: the "
|
|
275
|
+
"bulk endpoint that supports acting_user only accepts private "
|
|
276
|
+
"comments."
|
|
277
|
+
)
|
|
278
|
+
self._post_bulk_forecasts_and_comments(comments=[comment])
|
|
279
|
+
logger.info(f"Posted comment on post {post_id} as {self.acting_user}")
|
|
280
|
+
return
|
|
281
|
+
|
|
253
282
|
self._sleep_between_requests()
|
|
254
283
|
response = requests.post(
|
|
255
284
|
f"{self.base_url}/comments/create/",
|
|
256
|
-
json=
|
|
257
|
-
"on_post": post_id,
|
|
258
|
-
"text": comment_text,
|
|
259
|
-
"is_private": is_private,
|
|
260
|
-
"included_forecast": included_forecast,
|
|
261
|
-
},
|
|
285
|
+
json=comment,
|
|
262
286
|
**self._get_auth_headers(), # type: ignore
|
|
263
287
|
timeout=self.timeout,
|
|
264
288
|
)
|
|
@@ -371,6 +395,7 @@ class MetaculusClient:
|
|
|
371
395
|
headers = self._get_auth_headers()
|
|
372
396
|
response = requests.get(
|
|
373
397
|
url,
|
|
398
|
+
params={"acting_user": self.acting_user},
|
|
374
399
|
**headers, # type: ignore
|
|
375
400
|
timeout=self.timeout,
|
|
376
401
|
)
|
|
@@ -614,6 +639,15 @@ class MetaculusClient:
|
|
|
614
639
|
def _post_question_prediction(
|
|
615
640
|
self, question_id: int, forecast_payload: dict
|
|
616
641
|
) -> None:
|
|
642
|
+
if self.acting_user is not None:
|
|
643
|
+
self._post_bulk_forecasts_and_comments(
|
|
644
|
+
forecasts=[{"question": question_id, **forecast_payload}]
|
|
645
|
+
)
|
|
646
|
+
logger.info(
|
|
647
|
+
f"Posted prediction on question {question_id} as {self.acting_user}"
|
|
648
|
+
)
|
|
649
|
+
return
|
|
650
|
+
|
|
617
651
|
url = f"{self.base_url}/questions/forecast/"
|
|
618
652
|
self._sleep_between_requests()
|
|
619
653
|
response = requests.post(
|
|
@@ -631,6 +665,96 @@ class MetaculusClient:
|
|
|
631
665
|
logger.info(f"Posted prediction on question {question_id}")
|
|
632
666
|
raise_for_status_with_additional_info(response)
|
|
633
667
|
|
|
668
|
+
def _post_bulk_forecasts_and_comments(
|
|
669
|
+
self,
|
|
670
|
+
forecasts: list[dict] | None = None,
|
|
671
|
+
comments: list[dict] | None = None,
|
|
672
|
+
) -> None:
|
|
673
|
+
"""
|
|
674
|
+
Not retried here since callers are already wrapped in
|
|
675
|
+
retry_with_exponential_backoff.
|
|
676
|
+
"""
|
|
677
|
+
self._sleep_between_requests()
|
|
678
|
+
response = requests.post(
|
|
679
|
+
f"{self.base_url}/questions/bulk-forecast-comment/",
|
|
680
|
+
json={
|
|
681
|
+
"acting_user": self.acting_user,
|
|
682
|
+
"forecasts": forecasts or [],
|
|
683
|
+
"comments": comments or [],
|
|
684
|
+
},
|
|
685
|
+
**self._get_auth_headers(), # type: ignore
|
|
686
|
+
timeout=self.timeout,
|
|
687
|
+
)
|
|
688
|
+
raise_for_status_with_additional_info(response)
|
|
689
|
+
|
|
690
|
+
def _get_acting_user_forecasts(
|
|
691
|
+
self, question_ids: list[int]
|
|
692
|
+
) -> dict[int, list[dict]]:
|
|
693
|
+
"""
|
|
694
|
+
Returns the acting user's forecasts (oldest first) keyed by question id.
|
|
695
|
+
Questions the acting user cannot view are left out. Not retried here
|
|
696
|
+
since callers are already wrapped in retry_with_exponential_backoff.
|
|
697
|
+
"""
|
|
698
|
+
self._sleep_between_requests()
|
|
699
|
+
response = requests.post(
|
|
700
|
+
f"{self.base_url}/questions/bulk-forecast-read/",
|
|
701
|
+
json={"acting_user": self.acting_user, "question_ids": question_ids},
|
|
702
|
+
**self._get_auth_headers(), # type: ignore
|
|
703
|
+
timeout=self.timeout,
|
|
704
|
+
)
|
|
705
|
+
raise_for_status_with_additional_info(response)
|
|
706
|
+
results: list[dict] = json.loads(response.content)["results"]
|
|
707
|
+
return {result["question_id"]: result["forecasts"] for result in results}
|
|
708
|
+
|
|
709
|
+
def _set_my_forecasts_field_to_acting_users(self, post_jsons: list[dict]) -> None:
|
|
710
|
+
"""
|
|
711
|
+
The posts list endpoint fills the "my_forecasts" field with the token
|
|
712
|
+
account's forecasts and, unlike the post detail endpoint, doesn't take
|
|
713
|
+
acting_user. With an acting user, this overwrites that field in the
|
|
714
|
+
given jsons with the acting user's forecasts so that
|
|
715
|
+
already_forecasted, previous_forecasts, etc. describe the acting user.
|
|
716
|
+
Score data is not available this way and is left empty.
|
|
717
|
+
"""
|
|
718
|
+
if self.acting_user is None:
|
|
719
|
+
return
|
|
720
|
+
question_jsons = [
|
|
721
|
+
question_json
|
|
722
|
+
for post_json in post_jsons
|
|
723
|
+
for question_json in self._get_question_jsons_in_post(post_json)
|
|
724
|
+
]
|
|
725
|
+
if not question_jsons:
|
|
726
|
+
return
|
|
727
|
+
forecasts_by_question = self._get_acting_user_forecasts(
|
|
728
|
+
sorted({question_json["id"] for question_json in question_jsons})
|
|
729
|
+
)
|
|
730
|
+
for question_json in question_jsons:
|
|
731
|
+
history = forecasts_by_question.get(question_json["id"], [])
|
|
732
|
+
question_json["my_forecasts"] = {
|
|
733
|
+
"history": history,
|
|
734
|
+
"latest": history[-1] if history else None,
|
|
735
|
+
"score_data": {},
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
@staticmethod
|
|
739
|
+
def _get_question_jsons_in_post(post_json: dict) -> list[dict]:
|
|
740
|
+
if "group_of_questions" in post_json:
|
|
741
|
+
return list(post_json["group_of_questions"]["questions"])
|
|
742
|
+
if "conditional" in post_json:
|
|
743
|
+
conditional: dict = post_json["conditional"]
|
|
744
|
+
return [
|
|
745
|
+
conditional[key]
|
|
746
|
+
for key in (
|
|
747
|
+
"condition",
|
|
748
|
+
"condition_child",
|
|
749
|
+
"question_yes",
|
|
750
|
+
"question_no",
|
|
751
|
+
)
|
|
752
|
+
if conditional.get(key)
|
|
753
|
+
]
|
|
754
|
+
if post_json.get("question"):
|
|
755
|
+
return [post_json["question"]]
|
|
756
|
+
return []
|
|
757
|
+
|
|
634
758
|
@retry_with_exponential_backoff()
|
|
635
759
|
def _get_questions_from_api(
|
|
636
760
|
self, params: dict[str, Any], group_question_mode: GroupQuestionMode
|
|
@@ -653,6 +777,7 @@ class MetaculusClient:
|
|
|
653
777
|
f"Removed {len(removed_posts)} posts that "
|
|
654
778
|
"are not supported (e.g. notebook or conditional question)"
|
|
655
779
|
)
|
|
780
|
+
self._set_my_forecasts_field_to_acting_users(supported_posts)
|
|
656
781
|
|
|
657
782
|
questions: list[MetaculusQuestion] = []
|
|
658
783
|
for q in supported_posts:
|
|
@@ -885,7 +1010,9 @@ class MetaculusClient:
|
|
|
885
1010
|
if api_filter.allowed_tournaments:
|
|
886
1011
|
url_params["tournaments"] = api_filter.allowed_tournaments
|
|
887
1012
|
|
|
888
|
-
|
|
1013
|
+
# The server only allows forecaster_id to be the token's own account,
|
|
1014
|
+
# so for an acting user this is filtered locally instead
|
|
1015
|
+
if api_filter.is_previously_forecasted_by_user and self.acting_user is None:
|
|
889
1016
|
user_id = self.get_current_user_id()
|
|
890
1017
|
if user_id:
|
|
891
1018
|
url_params["forecaster_id"] = user_id
|
|
@@ -961,6 +1088,11 @@ class MetaculusClient:
|
|
|
961
1088
|
api_filter.cp_reveal_time_lt,
|
|
962
1089
|
)
|
|
963
1090
|
|
|
1091
|
+
if api_filter.is_previously_forecasted_by_user and self.acting_user is not None:
|
|
1092
|
+
questions = [
|
|
1093
|
+
question for question in questions if question.already_forecasted
|
|
1094
|
+
]
|
|
1095
|
+
|
|
964
1096
|
return questions
|
|
965
1097
|
|
|
966
1098
|
@staticmethod
|
|
@@ -1217,13 +1349,16 @@ class MetaculusClient:
|
|
|
1217
1349
|
|
|
1218
1350
|
@retry_with_exponential_backoff()
|
|
1219
1351
|
def create_question(
|
|
1220
|
-
self, question: MetaculusQuestion,
|
|
1352
|
+
self, question: MetaculusQuestion, acting_user: str | int | None = None
|
|
1221
1353
|
) -> MetaculusQuestion:
|
|
1222
1354
|
"""
|
|
1223
|
-
Setting
|
|
1224
|
-
|
|
1355
|
+
Setting acting_user (a username or user id) creates the post with that user
|
|
1356
|
+
as author. The API token must own that bot, be allowed to act as metac bots,
|
|
1357
|
+
or belong to a superuser. Defaults to the client's acting_user.
|
|
1225
1358
|
"""
|
|
1226
|
-
|
|
1359
|
+
if acting_user is None:
|
|
1360
|
+
acting_user = self.acting_user
|
|
1361
|
+
question_data = self._get_post_create_data(question, acting_user)
|
|
1227
1362
|
self._sleep_between_requests()
|
|
1228
1363
|
response = requests.post(
|
|
1229
1364
|
f"{self.base_url}/posts/create/",
|
|
@@ -1251,15 +1386,8 @@ class MetaculusClient:
|
|
|
1251
1386
|
single_created_question.id_of_post
|
|
1252
1387
|
)
|
|
1253
1388
|
|
|
1254
|
-
if
|
|
1255
|
-
|
|
1256
|
-
if actual_author != author_username:
|
|
1257
|
-
raise AuthorMismatchError(
|
|
1258
|
-
f"Post {full_created_question.id_of_post} was created with author "
|
|
1259
|
-
f"{actual_author!r} instead of {author_username!r} and has not been "
|
|
1260
|
-
"approved. Either the API token is not a superuser or the server "
|
|
1261
|
-
"ignored the author override."
|
|
1262
|
-
)
|
|
1389
|
+
if acting_user is not None:
|
|
1390
|
+
self._raise_if_author_is_not_acting_user(full_created_question, acting_user)
|
|
1263
1391
|
|
|
1264
1392
|
return full_created_question
|
|
1265
1393
|
|
|
@@ -1305,19 +1433,34 @@ class MetaculusClient:
|
|
|
1305
1433
|
raise_for_status_with_additional_info(response)
|
|
1306
1434
|
logger.info(f"Approved question {question.page_url}")
|
|
1307
1435
|
|
|
1436
|
+
@staticmethod
|
|
1437
|
+
def _raise_if_author_is_not_acting_user(
|
|
1438
|
+
question: MetaculusQuestion, acting_user: str | int
|
|
1439
|
+
) -> None:
|
|
1440
|
+
if str(acting_user).isdigit():
|
|
1441
|
+
actual_author = question.api_json.get("author_id")
|
|
1442
|
+
expected_author = int(acting_user)
|
|
1443
|
+
else:
|
|
1444
|
+
actual_author = question.api_json.get("author_username")
|
|
1445
|
+
expected_author = acting_user
|
|
1446
|
+
if actual_author != expected_author:
|
|
1447
|
+
raise AuthorMismatchError(
|
|
1448
|
+
f"Post {question.id_of_post} was created with author "
|
|
1449
|
+
f"{actual_author!r} instead of {expected_author!r} and has not been "
|
|
1450
|
+
"approved. The server likely ignored the acting_user field."
|
|
1451
|
+
)
|
|
1452
|
+
|
|
1308
1453
|
@staticmethod
|
|
1309
1454
|
def _get_post_create_data(
|
|
1310
|
-
question: MetaculusQuestion,
|
|
1455
|
+
question: MetaculusQuestion, acting_user: str | int | None = None
|
|
1311
1456
|
) -> dict:
|
|
1312
1457
|
if question.published_time is None:
|
|
1313
1458
|
publish_time = question.open_time
|
|
1314
1459
|
else:
|
|
1315
1460
|
publish_time = question.published_time
|
|
1316
1461
|
|
|
1317
|
-
|
|
1318
|
-
{"
|
|
1319
|
-
if author_username is not None
|
|
1320
|
-
else {}
|
|
1462
|
+
acting_user_fields = (
|
|
1463
|
+
{"acting_user": acting_user} if acting_user is not None else {}
|
|
1321
1464
|
)
|
|
1322
1465
|
|
|
1323
1466
|
return {
|
|
@@ -1383,5 +1526,5 @@ class MetaculusClient:
|
|
|
1383
1526
|
"fine_print": question.fine_print or "",
|
|
1384
1527
|
"group_rank": None, # only group questions
|
|
1385
1528
|
},
|
|
1386
|
-
**
|
|
1529
|
+
**acting_user_fields,
|
|
1387
1530
|
}
|
|
@@ -46,7 +46,14 @@ def raise_for_status_with_additional_info(
|
|
|
46
46
|
f"Response JSON: {response_json}."
|
|
47
47
|
)
|
|
48
48
|
logger.error(error_message)
|
|
49
|
-
raise requests.exceptions.HTTPError(error_message) from e
|
|
49
|
+
raise requests.exceptions.HTTPError(error_message, response=response) from e
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def _is_permanent_client_error(error: Exception) -> bool:
|
|
53
|
+
if not isinstance(error, requests.exceptions.HTTPError) or error.response is None:
|
|
54
|
+
return False
|
|
55
|
+
status_code = error.response.status_code
|
|
56
|
+
return 400 <= status_code < 500 and status_code != 429
|
|
50
57
|
|
|
51
58
|
|
|
52
59
|
def retry_with_exponential_backoff(
|
|
@@ -82,6 +89,11 @@ def retry_with_exponential_backoff(
|
|
|
82
89
|
try:
|
|
83
90
|
return func(*args, **kwargs)
|
|
84
91
|
except retry_on_exceptions as e:
|
|
92
|
+
if _is_permanent_client_error(e):
|
|
93
|
+
logger.warning(
|
|
94
|
+
f"Not retrying {func.__name__} since 4xx errors (other than 429) are not transient. Error: {e}"
|
|
95
|
+
)
|
|
96
|
+
raise
|
|
85
97
|
retries += 1
|
|
86
98
|
if retries > max_retries:
|
|
87
99
|
logger.error(
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "forecasting-tools"
|
|
3
|
-
version = "0.3.
|
|
3
|
+
version = "0.3.5"
|
|
4
4
|
description = "AI forecasting and research tools to help humans reason about and forecast the future"
|
|
5
5
|
authors = ["Benjamin Wilson <ben@metaculus.com>"]
|
|
6
6
|
license = "MIT"
|
|
@@ -38,7 +38,7 @@ pillow = ">=9.0.0,<13.0.0"
|
|
|
38
38
|
typeguard = "^4.3.0"
|
|
39
39
|
scikit-learn = {version = "^1.5.2", optional = true}
|
|
40
40
|
scipy = {version = ">=1.10.0,<2.0.0", optional = true}
|
|
41
|
-
litellm = ">=1.59.1,<2.0.0,!=1.76.*,!=1.77.*,!=1.82.7,!=1.82.8"
|
|
41
|
+
litellm = ">=1.59.1,<2.0.0,!=1.76.*,!=1.77.*,!=1.82.7,!=1.82.8,!=1.107.*,!=1.108.*,!=1.109.*"
|
|
42
42
|
numpy = ">=1.26.0,<3.0.0"
|
|
43
43
|
exceptiongroup = "^1.2.2"
|
|
44
44
|
faker = {version = ">=37.0.0,<41.0.0", optional = true}
|
|
File without changes
|
|
File without changes
|
{forecasting_tools-0.3.3 → forecasting_tools-0.3.5}/forecasting_tools/agents_and_tools/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|