briefloop 0.11.12__py3-none-any.whl
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.
- briefloop-0.11.12.dist-info/METADATA +473 -0
- briefloop-0.11.12.dist-info/RECORD +424 -0
- briefloop-0.11.12.dist-info/WHEEL +5 -0
- briefloop-0.11.12.dist-info/entry_points.txt +3 -0
- briefloop-0.11.12.dist-info/licenses/LICENSE +22 -0
- briefloop-0.11.12.dist-info/top_level.txt +1 -0
- multi_agent_brief/__init__.py +36 -0
- multi_agent_brief/analysis_blocks/__init__.py +11 -0
- multi_agent_brief/analysis_blocks/builder.py +206 -0
- multi_agent_brief/analysis_blocks/renderer.py +237 -0
- multi_agent_brief/analysis_blocks/schemas.py +52 -0
- multi_agent_brief/analysis_modules/__init__.py +8 -0
- multi_agent_brief/analysis_modules/base.py +87 -0
- multi_agent_brief/analysis_modules/market_competitor/__init__.py +68 -0
- multi_agent_brief/analysis_modules/market_competitor/auditor.py +290 -0
- multi_agent_brief/analysis_modules/market_competitor/config.py +187 -0
- multi_agent_brief/analysis_modules/market_competitor/event_builder.py +226 -0
- multi_agent_brief/analysis_modules/market_competitor/renderer.py +199 -0
- multi_agent_brief/analysis_modules/market_competitor/schemas.py +518 -0
- multi_agent_brief/analysis_modules/policy_regulatory/__init__.py +7 -0
- multi_agent_brief/analysis_modules/policy_regulatory/audit.py +195 -0
- multi_agent_brief/analysis_modules/policy_regulatory/module.py +387 -0
- multi_agent_brief/analysis_modules/policy_regulatory/schemas.py +129 -0
- multi_agent_brief/analysis_modules/registry.py +73 -0
- multi_agent_brief/audience/__init__.py +19 -0
- multi_agent_brief/audience/profiles.py +246 -0
- multi_agent_brief/audience_memory/__init__.py +23 -0
- multi_agent_brief/audience_memory/profile.py +358 -0
- multi_agent_brief/audit/__init__.py +36 -0
- multi_agent_brief/audit/case_applicability.py +181 -0
- multi_agent_brief/audit/deterministic.py +471 -0
- multi_agent_brief/audit/editorial_governance.py +404 -0
- multi_agent_brief/audit/final_quality.py +764 -0
- multi_agent_brief/audit/harness.py +242 -0
- multi_agent_brief/audit/interfaces.py +96 -0
- multi_agent_brief/audit/limitation_hygiene.py +238 -0
- multi_agent_brief/audit/proposal_boundary.py +34 -0
- multi_agent_brief/audit/redaction.py +27 -0
- multi_agent_brief/audit/rule_packs.py +96 -0
- multi_agent_brief/audit/semantic.py +435 -0
- multi_agent_brief/capabilities/__init__.py +22 -0
- multi_agent_brief/capabilities/catalog.py +215 -0
- multi_agent_brief/capabilities/detect.py +199 -0
- multi_agent_brief/capabilities/models.py +60 -0
- multi_agent_brief/capabilities/recommend.py +206 -0
- multi_agent_brief/cli/__init__.py +2 -0
- multi_agent_brief/cli/analysis_commands.py +140 -0
- multi_agent_brief/cli/approval_commands.py +107 -0
- multi_agent_brief/cli/audit_commands.py +59 -0
- multi_agent_brief/cli/capability_commands.py +548 -0
- multi_agent_brief/cli/claude_commands.py +159 -0
- multi_agent_brief/cli/competitors_commands.py +182 -0
- multi_agent_brief/cli/controls_commands.py +137 -0
- multi_agent_brief/cli/deliver_commands.py +942 -0
- multi_agent_brief/cli/eval_cases_commands.py +99 -0
- multi_agent_brief/cli/experiments_commands.py +395 -0
- multi_agent_brief/cli/feedback_commands.py +192 -0
- multi_agent_brief/cli/finalize_commands.py +153 -0
- multi_agent_brief/cli/gates_commands.py +174 -0
- multi_agent_brief/cli/hermes_commands.py +356 -0
- multi_agent_brief/cli/improve_commands.py +224 -0
- multi_agent_brief/cli/init_commands.py +564 -0
- multi_agent_brief/cli/init_wizard.py +1473 -0
- multi_agent_brief/cli/input_commands.py +237 -0
- multi_agent_brief/cli/main.py +288 -0
- multi_agent_brief/cli/onboard_commands.py +193 -0
- multi_agent_brief/cli/product_commands.py +1449 -0
- multi_agent_brief/cli/provenance_commands.py +100 -0
- multi_agent_brief/cli/release_commands.py +68 -0
- multi_agent_brief/cli/repair_commands.py +169 -0
- multi_agent_brief/cli/run_commands.py +292 -0
- multi_agent_brief/cli/runtime_commands.py +81 -0
- multi_agent_brief/cli/secrets_commands.py +232 -0
- multi_agent_brief/cli/semantic_support_commands.py +80 -0
- multi_agent_brief/cli/sources_commands.py +647 -0
- multi_agent_brief/cli/start_commands.py +8 -0
- multi_agent_brief/cli/state_commands.py +446 -0
- multi_agent_brief/cli/status_commands.py +26 -0
- multi_agent_brief/cli/workbuddy_commands.py +103 -0
- multi_agent_brief/configs/artifact_contracts.yaml +514 -0
- multi_agent_brief/configs/orchestrator_contract.yaml +181 -0
- multi_agent_brief/configs/policy_packs/default.yaml +65 -0
- multi_agent_brief/configs/policy_profiles/evidence_extract_default.yaml +58 -0
- multi_agent_brief/configs/policy_profiles/finance_default.yaml +44 -0
- multi_agent_brief/configs/policy_profiles/internet_default.yaml +44 -0
- multi_agent_brief/configs/policy_profiles/manufacturing_default.yaml +33 -0
- multi_agent_brief/configs/policy_profiles/solar_manufacturing_default.yaml +64 -0
- multi_agent_brief/configs/report_packs/evidence_extract.yaml +48 -0
- multi_agent_brief/configs/report_packs/management_monthly.yaml +36 -0
- multi_agent_brief/configs/report_packs/market_weekly.yaml +36 -0
- multi_agent_brief/configs/report_packs/solar_industry_periodic.yaml +50 -0
- multi_agent_brief/configs/report_templates/evidence_extract.yaml +55 -0
- multi_agent_brief/configs/report_templates/management_monthly.yaml +48 -0
- multi_agent_brief/configs/report_templates/market_weekly.yaml +49 -0
- multi_agent_brief/configs/report_templates/solar_industry_periodic.yaml +75 -0
- multi_agent_brief/configs/stage_specs.yaml +180 -0
- multi_agent_brief/contracts/__init__.py +41 -0
- multi_agent_brief/contracts/base.py +99 -0
- multi_agent_brief/contracts/errors.py +41 -0
- multi_agent_brief/contracts/migrations/__init__.py +5 -0
- multi_agent_brief/contracts/migrations/claim_v1_to_v2.py +33 -0
- multi_agent_brief/contracts/registry.py +159 -0
- multi_agent_brief/contracts/role_topology.py +25 -0
- multi_agent_brief/contracts/schemas/__init__.py +35 -0
- multi_agent_brief/contracts/schemas/analysis_pack.py +135 -0
- multi_agent_brief/contracts/schemas/atomic_claim_graph.py +304 -0
- multi_agent_brief/contracts/schemas/audit_report.py +80 -0
- multi_agent_brief/contracts/schemas/candidate_item.py +51 -0
- multi_agent_brief/contracts/schemas/claim.py +197 -0
- multi_agent_brief/contracts/schemas/claim_draft.py +386 -0
- multi_agent_brief/contracts/schemas/claim_support_matrix.py +334 -0
- multi_agent_brief/contracts/schemas/evidence_span_registry.py +270 -0
- multi_agent_brief/contracts/schemas/policy_profile.py +247 -0
- multi_agent_brief/contracts/schemas/report_spec.py +257 -0
- multi_agent_brief/contracts/schemas/semantic_assessment_report.py +480 -0
- multi_agent_brief/contracts/schemas/source_evidence_pack_manifest.py +233 -0
- multi_agent_brief/contracts/schemas/source_item.py +96 -0
- multi_agent_brief/contracts/semantic_assessment_status.py +12 -0
- multi_agent_brief/contracts/source_metadata.py +252 -0
- multi_agent_brief/contracts/target_contract.py +460 -0
- multi_agent_brief/contracts/validator.py +225 -0
- multi_agent_brief/controls/__init__.py +23 -0
- multi_agent_brief/controls/contract.py +106 -0
- multi_agent_brief/controls/switchboard.py +1076 -0
- multi_agent_brief/core/__init__.py +2 -0
- multi_agent_brief/core/citations.py +14 -0
- multi_agent_brief/core/claim_ledger.py +94 -0
- multi_agent_brief/core/config.py +191 -0
- multi_agent_brief/core/env.py +69 -0
- multi_agent_brief/core/previous.py +83 -0
- multi_agent_brief/core/schemas.py +181 -0
- multi_agent_brief/core/selection.py +189 -0
- multi_agent_brief/delivery/__init__.py +6 -0
- multi_agent_brief/delivery/base.py +36 -0
- multi_agent_brief/delivery/feishu.py +204 -0
- multi_agent_brief/delivery/gws.py +237 -0
- multi_agent_brief/evaluation_cases/__init__.py +15 -0
- multi_agent_brief/evaluation_cases/contract.py +429 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/approved_guidance_materialized/workspace/config.yaml +18 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/approved_guidance_materialized/workspace/improvement/ledger.jsonl +2 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/approved_guidance_materialized/workspace/sources.yaml +2 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/approved_guidance_materialized/workspace/user.md +4 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/company_event_missing_latest_official_check/workspace/config.yaml +13 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/company_event_missing_latest_official_check/workspace/sources.yaml +9 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/company_event_missing_latest_official_check/workspace/user.md +4 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/control_switchboard_selection_is_not_execution/workspace/config.yaml +12 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/control_switchboard_selection_is_not_execution/workspace/sources.yaml +6 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/control_switchboard_selection_is_not_execution/workspace/user.md +4 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/feedback_triage_required/workspace/config.yaml +6 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/feedback_triage_required/workspace/input/human_feedback.md +1 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/feedback_triage_required/workspace/sources.yaml +2 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/feedback_triage_required/workspace/user.md +3 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/final_abstract_quality_warning_surface/workspace/config.yaml +8 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/final_abstract_quality_warning_surface/workspace/output/brief.md +14 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/final_abstract_quality_warning_surface/workspace/output/intermediate/audit_report.json +1 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/final_abstract_quality_warning_surface/workspace/output/intermediate/audited_brief.md +5 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/final_abstract_quality_warning_surface/workspace/output/intermediate/candidate_claims.json +1 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/final_abstract_quality_warning_surface/workspace/output/intermediate/claim_ledger.json +1 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/final_abstract_quality_warning_surface/workspace/output/intermediate/screened_candidates.json +1 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/final_abstract_quality_warning_surface/workspace/sources.yaml +2 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/final_abstract_quality_warning_surface/workspace/user.md +3 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/formal_release_missing_human_approval/workspace/config.yaml +13 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/formal_release_missing_human_approval/workspace/sources.yaml +9 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/formal_release_missing_human_approval/workspace/user.md +4 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/guidance_manifestation_not_observable/workspace/config.yaml +4 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/guidance_manifestation_not_observable/workspace/sources.yaml +2 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/guidance_manifestation_not_observable/workspace/user.md +3 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/media_only_legal_policy_blocks_research_review/workspace/config.yaml +13 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/media_only_legal_policy_blocks_research_review/workspace/sources.yaml +9 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/media_only_legal_policy_blocks_research_review/workspace/user.md +4 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/mixed_metric_scope_support_blocker/workspace/config.yaml +13 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/mixed_metric_scope_support_blocker/workspace/sources.yaml +9 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/mixed_metric_scope_support_blocker/workspace/user.md +5 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/planned_blocking_issue_cannot_continue/workspace/config.yaml +6 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/planned_blocking_issue_cannot_continue/workspace/output/intermediate/audited_brief.md +3 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/planned_blocking_issue_cannot_continue/workspace/output/intermediate/candidate_claims.json +1 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/planned_blocking_issue_cannot_continue/workspace/output/intermediate/claim_ledger.json +1 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/planned_blocking_issue_cannot_continue/workspace/output/intermediate/feedback_issues.json +25 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/planned_blocking_issue_cannot_continue/workspace/output/intermediate/repair_plan.json +27 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/planned_blocking_issue_cannot_continue/workspace/output/intermediate/screened_candidates.json +1 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/planned_blocking_issue_cannot_continue/workspace/sources.yaml +2 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/planned_blocking_issue_cannot_continue/workspace/user.md +3 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/provenance_projection_minimal/workspace/config.yaml +8 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/provenance_projection_minimal/workspace/output/intermediate/audit_report.json +1 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/provenance_projection_minimal/workspace/output/intermediate/audited_brief.md +3 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/provenance_projection_minimal/workspace/output/intermediate/candidate_claims.json +1 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/provenance_projection_minimal/workspace/output/intermediate/claim_ledger.json +11 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/provenance_projection_minimal/workspace/output/intermediate/quality_gate_report.json +22 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/provenance_projection_minimal/workspace/output/intermediate/screened_candidates.json +1 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/provenance_projection_minimal/workspace/sources.yaml +2 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/provenance_projection_minimal/workspace/user.md +3 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/reader_facing_source_appendix/workspace/config.yaml +13 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/reader_facing_source_appendix/workspace/output/intermediate/audit_report.json +1 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/reader_facing_source_appendix/workspace/output/intermediate/audited_brief.md +5 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/reader_facing_source_appendix/workspace/output/intermediate/candidate_claims.json +1 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/reader_facing_source_appendix/workspace/output/intermediate/claim_ledger.json +40 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/reader_facing_source_appendix/workspace/output/intermediate/screened_candidates.json +1 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/reader_facing_source_appendix/workspace/sources.yaml +3 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/reader_facing_source_appendix/workspace/user.md +3 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/reader_facing_target_relevance/workspace/config.yaml +8 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/reader_facing_target_relevance/workspace/output/brief.md +3 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/reader_facing_target_relevance/workspace/output/intermediate/audit_report.json +1 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/reader_facing_target_relevance/workspace/output/intermediate/audited_brief.md +3 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/reader_facing_target_relevance/workspace/output/intermediate/candidate_claims.json +1 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/reader_facing_target_relevance/workspace/output/intermediate/claim_ledger.json +13 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/reader_facing_target_relevance/workspace/output/intermediate/screened_candidates.json +1 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/reader_facing_target_relevance/workspace/sources.yaml +2 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/reader_facing_target_relevance/workspace/user.md +3 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/release_readiness_forged_event_blocker/workspace/config.yaml +13 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/release_readiness_forged_event_blocker/workspace/output/intermediate/release_readiness_report.json +31 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/release_readiness_forged_event_blocker/workspace/sources.yaml +9 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/release_readiness_forged_event_blocker/workspace/user.md +4 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/reverted_entry_removed_from_next_snapshot/workspace/config.yaml +18 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/reverted_entry_removed_from_next_snapshot/workspace/improvement/ledger.jsonl +3 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/reverted_entry_removed_from_next_snapshot/workspace/improvement/memory.md +3 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/reverted_entry_removed_from_next_snapshot/workspace/output/intermediate/improvement_memory_snapshot.md +3 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/reverted_entry_removed_from_next_snapshot/workspace/sources.yaml +2 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/reverted_entry_removed_from_next_snapshot/workspace/user.md +4 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/same_evidence_reader_quality_regression/workspace/config.yaml +12 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/same_evidence_reader_quality_regression/workspace/input/sources/source-001.txt +1 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/same_evidence_reader_quality_regression/workspace/output/brief.md +37 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/same_evidence_reader_quality_regression/workspace/output/delivery/brief.md +37 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/same_evidence_reader_quality_regression/workspace/output/intermediate/atomic_claim_graph.json +17 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/same_evidence_reader_quality_regression/workspace/output/intermediate/audit_report.json +5 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/same_evidence_reader_quality_regression/workspace/output/intermediate/audited_brief.md +37 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/same_evidence_reader_quality_regression/workspace/output/intermediate/claim_ledger.json +20 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/same_evidence_reader_quality_regression/workspace/output/intermediate/evidence_span_registry.json +20 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/same_evidence_reader_quality_regression/workspace/output/intermediate/finalize_report.json +40 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/same_evidence_reader_quality_regression/workspace/output/intermediate/gates/auditor_quality_gate_report.json +42 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/same_evidence_reader_quality_regression/workspace/output/intermediate/gates/finalize_quality_gate_report.json +42 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/same_evidence_reader_quality_regression/workspace/output/intermediate/screened_candidates.json +33 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/same_evidence_reader_quality_regression/workspace/output/source_appendix.md +12 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/same_evidence_reader_quality_regression/workspace/output/source_appendix_trace.md +10 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/same_evidence_reader_quality_regression/workspace/report_spec.yaml +24 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/same_evidence_reader_quality_regression/workspace/sources.yaml +4 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/same_evidence_reader_quality_regression/workspace/user.md +3 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/source_evidence_pack_blocks_non_evidence_file/workspace/config.yaml +13 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/source_evidence_pack_blocks_non_evidence_file/workspace/input/sources/README.md +4 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/source_evidence_pack_blocks_non_evidence_file/workspace/output/intermediate/source_evidence_pack_manifest.json +26 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/source_evidence_pack_blocks_non_evidence_file/workspace/sources.yaml +9 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/source_evidence_pack_blocks_non_evidence_file/workspace/user.md +4 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/stale_current_claim/workspace/config.yaml +9 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/stale_current_claim/workspace/output/intermediate/audit_report.json +1 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/stale_current_claim/workspace/output/intermediate/audited_brief.md +3 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/stale_current_claim/workspace/output/intermediate/candidate_claims.json +1 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/stale_current_claim/workspace/output/intermediate/claim_ledger.json +13 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/stale_current_claim/workspace/output/intermediate/screened_candidates.json +1 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/stale_current_claim/workspace/sources.yaml +2 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/stale_current_claim/workspace/user.md +3 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/third_party_price_snapshot_formal_block/workspace/config.yaml +13 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/third_party_price_snapshot_formal_block/workspace/sources.yaml +9 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/third_party_price_snapshot_formal_block/workspace/user.md +4 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/trajectory_retry_budget_exhausted/workspace/config.yaml +4 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/trajectory_retry_budget_exhausted/workspace/sources.yaml +2 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/trajectory_retry_budget_exhausted/workspace/user.md +3 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/unapproved_entry_not_materialized/workspace/config.yaml +18 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/unapproved_entry_not_materialized/workspace/improvement/ledger.jsonl +1 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/unapproved_entry_not_materialized/workspace/sources.yaml +2 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/unapproved_entry_not_materialized/workspace/user.md +4 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/unauthorized_institution_branding_blocks_release/workspace/config.yaml +18 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/unauthorized_institution_branding_blocks_release/workspace/sources.yaml +9 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/unauthorized_institution_branding_blocks_release/workspace/user.md +5 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/unsupported_material_fact/workspace/config.yaml +8 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/unsupported_material_fact/workspace/output/intermediate/audit_report.json +1 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/unsupported_material_fact/workspace/output/intermediate/audited_brief.md +8 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/unsupported_material_fact/workspace/output/intermediate/candidate_claims.json +1 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/unsupported_material_fact/workspace/output/intermediate/claim_ledger.json +1 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/unsupported_material_fact/workspace/output/intermediate/screened_candidates.json +1 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/unsupported_material_fact/workspace/sources.yaml +2 -0
- multi_agent_brief/evaluation_cases/fixtures/cases/unsupported_material_fact/workspace/user.md +3 -0
- multi_agent_brief/evaluation_cases/fixtures/manifest.yaml +861 -0
- multi_agent_brief/evaluation_cases/fixtures.py +20 -0
- multi_agent_brief/evaluation_cases/runner.py +1603 -0
- multi_agent_brief/experiments/__init__.py +45 -0
- multi_agent_brief/experiments/experiment_080.py +5835 -0
- multi_agent_brief/experiments/schemas.py +63 -0
- multi_agent_brief/experiments/target_contract.py +10 -0
- multi_agent_brief/feedback/__init__.py +17 -0
- multi_agent_brief/feedback/feedback_contract.py +482 -0
- multi_agent_brief/feedback/feedback_state.py +898 -0
- multi_agent_brief/hermes/__init__.py +25 -0
- multi_agent_brief/hermes/adapter.py +832 -0
- multi_agent_brief/improvement/__init__.py +83 -0
- multi_agent_brief/improvement/contract.py +1114 -0
- multi_agent_brief/improvement/memory.py +451 -0
- multi_agent_brief/improvement/product_definition.py +228 -0
- multi_agent_brief/improvement/state.py +961 -0
- multi_agent_brief/inputs/__init__.py +2 -0
- multi_agent_brief/inputs/classifier.py +322 -0
- multi_agent_brief/inputs/contracts.py +59 -0
- multi_agent_brief/inputs/extractor.py +310 -0
- multi_agent_brief/install/__init__.py +2 -0
- multi_agent_brief/install/writer.py +97 -0
- multi_agent_brief/onboarding/__init__.py +11 -0
- multi_agent_brief/onboarding/io.py +103 -0
- multi_agent_brief/onboarding/mapper.py +484 -0
- multi_agent_brief/onboarding/schema.py +46 -0
- multi_agent_brief/orchestrator/__init__.py +2 -0
- multi_agent_brief/orchestrator/fact_layer_import.py +352 -0
- multi_agent_brief/orchestrator/handoff.py +2209 -0
- multi_agent_brief/orchestrator/role_topology.py +42 -0
- multi_agent_brief/orchestrator/run_archive.py +888 -0
- multi_agent_brief/orchestrator/run_integrity.py +346 -0
- multi_agent_brief/orchestrator/runtime_state/__init__.py +70 -0
- multi_agent_brief/orchestrator/runtime_state/_io.py +167 -0
- multi_agent_brief/orchestrator/runtime_state/_transactions.py +298 -0
- multi_agent_brief/orchestrator/runtime_state/artifact_registry.py +1825 -0
- multi_agent_brief/orchestrator/runtime_state/atomic_claim_graph.py +96 -0
- multi_agent_brief/orchestrator/runtime_state/claim_ledger_freeze.py +510 -0
- multi_agent_brief/orchestrator/runtime_state/claim_metadata_enrichment.py +836 -0
- multi_agent_brief/orchestrator/runtime_state/claim_support_matrix.py +582 -0
- multi_agent_brief/orchestrator/runtime_state/completion_gates.py +672 -0
- multi_agent_brief/orchestrator/runtime_state/contracts_loader.py +66 -0
- multi_agent_brief/orchestrator/runtime_state/decisions.py +239 -0
- multi_agent_brief/orchestrator/runtime_state/errors.py +61 -0
- multi_agent_brief/orchestrator/runtime_state/event_log.py +256 -0
- multi_agent_brief/orchestrator/runtime_state/evidence_span_registry.py +233 -0
- multi_agent_brief/orchestrator/runtime_state/fact_layer.py +755 -0
- multi_agent_brief/orchestrator/runtime_state/identity.py +90 -0
- multi_agent_brief/orchestrator/runtime_state/lifecycle.py +675 -0
- multi_agent_brief/orchestrator/runtime_state/manifest.py +82 -0
- multi_agent_brief/orchestrator/runtime_state/operations.py +224 -0
- multi_agent_brief/orchestrator/runtime_state/paths.py +38 -0
- multi_agent_brief/orchestrator/runtime_state/repair.py +984 -0
- multi_agent_brief/orchestrator/runtime_state/safety_surfaces.py +147 -0
- multi_agent_brief/orchestrator/runtime_state/semantic_assessment_report.py +692 -0
- multi_agent_brief/orchestrator/runtime_state/semantic_support_acceptance.py +683 -0
- multi_agent_brief/orchestrator/runtime_state/source_evidence_pack.py +131 -0
- multi_agent_brief/orchestrator/runtime_state/stage_completion.py +1320 -0
- multi_agent_brief/orchestrator/runtime_state/trajectory.py +200 -0
- multi_agent_brief/orchestrator/runtime_state/workflow.py +350 -0
- multi_agent_brief/orchestrator/source_evidence.py +43 -0
- multi_agent_brief/orchestrator/timing.py +410 -0
- multi_agent_brief/orchestrator_contract.py +96 -0
- multi_agent_brief/outputs/__init__.py +2 -0
- multi_agent_brief/outputs/atomic_reader_projection.py +288 -0
- multi_agent_brief/outputs/docx.py +32 -0
- multi_agent_brief/outputs/finalize.py +1036 -0
- multi_agent_brief/outputs/ib_docx.py +983 -0
- multi_agent_brief/outputs/naming.py +38 -0
- multi_agent_brief/outputs/pdf.py +20 -0
- multi_agent_brief/outputs/reader_final_gate.py +509 -0
- multi_agent_brief/outputs/reader_projection.py +397 -0
- multi_agent_brief/outputs/source_appendix.py +753 -0
- multi_agent_brief/outputs/templates/__init__.py +112 -0
- multi_agent_brief/product/__init__.py +49 -0
- multi_agent_brief/product/bundle_projection.py +686 -0
- multi_agent_brief/product/citation_profile.py +145 -0
- multi_agent_brief/product/guidance_manifestation.py +421 -0
- multi_agent_brief/product/materiality_selection.py +472 -0
- multi_agent_brief/product/policy_gate_adapter.py +116 -0
- multi_agent_brief/product/policy_profile.py +39 -0
- multi_agent_brief/product/policy_projection.py +161 -0
- multi_agent_brief/product/policy_registry.py +83 -0
- multi_agent_brief/product/policy_resolver.py +225 -0
- multi_agent_brief/product/quality_closeout.py +188 -0
- multi_agent_brief/product/quality_panel.py +2217 -0
- multi_agent_brief/product/release_approval.py +1034 -0
- multi_agent_brief/product/report_pack.py +130 -0
- multi_agent_brief/product/report_pack_aliases.py +68 -0
- multi_agent_brief/product/report_registry.py +101 -0
- multi_agent_brief/product/report_spec.py +181 -0
- multi_agent_brief/product/support_wording.py +525 -0
- multi_agent_brief/product/template_conformance.py +516 -0
- multi_agent_brief/product/template_projection.py +123 -0
- multi_agent_brief/product/template_registry.py +244 -0
- multi_agent_brief/product/template_render_plan.py +315 -0
- multi_agent_brief/product/template_renderer.py +224 -0
- multi_agent_brief/product/trajectory_regulation.py +441 -0
- multi_agent_brief/provenance/__init__.py +5 -0
- multi_agent_brief/provenance/builder.py +841 -0
- multi_agent_brief/provenance/contract.py +21 -0
- multi_agent_brief/provenance/io.py +144 -0
- multi_agent_brief/provenance/model.py +110 -0
- multi_agent_brief/provenance/references.py +43 -0
- multi_agent_brief/provenance/validator.py +135 -0
- multi_agent_brief/quality_gates/__init__.py +19 -0
- multi_agent_brief/quality_gates/contract.py +581 -0
- multi_agent_brief/quality_gates/state.py +2498 -0
- multi_agent_brief/repair/__init__.py +5 -0
- multi_agent_brief/repair/router.py +842 -0
- multi_agent_brief/runtime_assets.py +431 -0
- multi_agent_brief/sources/__init__.py +16 -0
- multi_agent_brief/sources/api_filings.py +236 -0
- multi_agent_brief/sources/api_news.py +174 -0
- multi_agent_brief/sources/base.py +142 -0
- multi_agent_brief/sources/cached_package.py +119 -0
- multi_agent_brief/sources/cli_provider.py +263 -0
- multi_agent_brief/sources/coverage.py +336 -0
- multi_agent_brief/sources/decider.py +756 -0
- multi_agent_brief/sources/doctor.py +373 -0
- multi_agent_brief/sources/evidence_pack.py +364 -0
- multi_agent_brief/sources/feishu_provider.py +326 -0
- multi_agent_brief/sources/filing_resolver.py +400 -0
- multi_agent_brief/sources/industry_packs.py +130 -0
- multi_agent_brief/sources/join.py +206 -0
- multi_agent_brief/sources/local_signal.py +81 -0
- multi_agent_brief/sources/local_signal_planner.py +636 -0
- multi_agent_brief/sources/manual.py +259 -0
- multi_agent_brief/sources/mcp_provider.py +309 -0
- multi_agent_brief/sources/mineru_provider.py +601 -0
- multi_agent_brief/sources/normalizer.py +95 -0
- multi_agent_brief/sources/opencli_provider.py +282 -0
- multi_agent_brief/sources/planner.py +178 -0
- multi_agent_brief/sources/registry.py +367 -0
- multi_agent_brief/sources/rss.py +190 -0
- multi_agent_brief/sources/search_backends/__init__.py +31 -0
- multi_agent_brief/sources/search_backends/base.py +51 -0
- multi_agent_brief/sources/search_backends/brave.py +192 -0
- multi_agent_brief/sources/search_backends/capabilities.py +94 -0
- multi_agent_brief/sources/search_backends/exa.py +180 -0
- multi_agent_brief/sources/search_backends/firecrawl.py +164 -0
- multi_agent_brief/sources/search_backends/serper.py +241 -0
- multi_agent_brief/sources/search_backends/tavily.py +124 -0
- multi_agent_brief/sources/sourcehub.py +505 -0
- multi_agent_brief/sources/web_search.py +267 -0
- multi_agent_brief/status.py +966 -0
- multi_agent_brief/tools/__init__.py +3 -0
- multi_agent_brief/tools/draft_cleanup.py +198 -0
- multi_agent_brief/workbuddy/__init__.py +1 -0
- multi_agent_brief/workbuddy/diagnose.py +569 -0
- multi_agent_brief/workbuddy/skill_pack.py +309 -0
- multi_agent_brief/workspace/__init__.py +1 -0
- multi_agent_brief/workspace/init_profile.py +45 -0
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
"""Shared safe-write primitives for installer commands."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class InstallWriteError(RuntimeError):
|
|
10
|
+
"""Raised when planned installer writes are unsafe or refused."""
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@dataclass(frozen=True)
|
|
14
|
+
class PlannedWrite:
|
|
15
|
+
"""A fully rendered file write planned by an installer."""
|
|
16
|
+
|
|
17
|
+
destination: Path
|
|
18
|
+
content: str
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def apply_planned_writes(
|
|
22
|
+
*,
|
|
23
|
+
writes: list[PlannedWrite],
|
|
24
|
+
root: Path,
|
|
25
|
+
force: bool = False,
|
|
26
|
+
dry_run: bool = False,
|
|
27
|
+
generated_markers: tuple[str, ...] = (),
|
|
28
|
+
) -> list[Path]:
|
|
29
|
+
"""Validate and optionally apply planned writes under root."""
|
|
30
|
+
root = root.expanduser().resolve()
|
|
31
|
+
_validate_destinations(root=root, writes=writes)
|
|
32
|
+
_check_overwrites(
|
|
33
|
+
writes=writes,
|
|
34
|
+
force=force,
|
|
35
|
+
generated_markers=generated_markers,
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
written: list[Path] = []
|
|
39
|
+
if dry_run:
|
|
40
|
+
return [write.destination for write in writes]
|
|
41
|
+
|
|
42
|
+
for write in writes:
|
|
43
|
+
write.destination.parent.mkdir(parents=True, exist_ok=True)
|
|
44
|
+
write.destination.write_text(write.content, encoding="utf-8")
|
|
45
|
+
written.append(write.destination)
|
|
46
|
+
return written
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def _validate_destinations(*, root: Path, writes: list[PlannedWrite]) -> None:
|
|
50
|
+
for write in writes:
|
|
51
|
+
resolved = write.destination.resolve()
|
|
52
|
+
try:
|
|
53
|
+
resolved.relative_to(root)
|
|
54
|
+
except ValueError as exc:
|
|
55
|
+
raise InstallWriteError(
|
|
56
|
+
f"Installer write resolves outside target root: {write.destination}"
|
|
57
|
+
) from exc
|
|
58
|
+
if write.destination.is_symlink():
|
|
59
|
+
raise InstallWriteError(
|
|
60
|
+
f"Refusing to overwrite symlink during install: {write.destination}"
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def _check_overwrites(
|
|
65
|
+
*,
|
|
66
|
+
writes: list[PlannedWrite],
|
|
67
|
+
force: bool,
|
|
68
|
+
generated_markers: tuple[str, ...],
|
|
69
|
+
) -> None:
|
|
70
|
+
for write in writes:
|
|
71
|
+
dst = write.destination
|
|
72
|
+
if not dst.exists():
|
|
73
|
+
continue
|
|
74
|
+
existing_text = _read_existing_text(dst)
|
|
75
|
+
if force:
|
|
76
|
+
continue
|
|
77
|
+
if existing_text is not None and existing_text == write.content:
|
|
78
|
+
continue
|
|
79
|
+
if existing_text is not None and _text_has_generated_marker(
|
|
80
|
+
existing_text,
|
|
81
|
+
generated_markers,
|
|
82
|
+
):
|
|
83
|
+
continue
|
|
84
|
+
raise InstallWriteError(
|
|
85
|
+
f"Refusing to overwrite existing non-generated file without --force: {dst}"
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def _read_existing_text(path: Path) -> str | None:
|
|
90
|
+
try:
|
|
91
|
+
return path.read_text(encoding="utf-8")
|
|
92
|
+
except (OSError, UnicodeDecodeError):
|
|
93
|
+
return None
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def _text_has_generated_marker(text: str, markers: tuple[str, ...]) -> bool:
|
|
97
|
+
return any(marker and marker in text for marker in markers)
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"""Conversational onboarding protocol for multi-agent-brief-workflow."""
|
|
2
|
+
from multi_agent_brief.onboarding.schema import OnboardingResult
|
|
3
|
+
from multi_agent_brief.onboarding.io import load_onboarding_result, save_onboarding_result
|
|
4
|
+
from multi_agent_brief.onboarding.mapper import map_onboarding_to_profile
|
|
5
|
+
|
|
6
|
+
__all__ = [
|
|
7
|
+
"OnboardingResult",
|
|
8
|
+
"load_onboarding_result",
|
|
9
|
+
"save_onboarding_result",
|
|
10
|
+
"map_onboarding_to_profile",
|
|
11
|
+
]
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
"""OnboardingResult I/O: JSON load/save with tolerance for unknown or missing fields."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import json
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from typing import Any
|
|
7
|
+
from dataclasses import fields
|
|
8
|
+
|
|
9
|
+
from multi_agent_brief.onboarding.schema import OnboardingResult
|
|
10
|
+
|
|
11
|
+
_DATACLASS_FIELDS = {f.name for f in fields(OnboardingResult)}
|
|
12
|
+
_LIST_FIELDS = {"forbidden_sources", "must_watch", "missing"}
|
|
13
|
+
_INT_FIELDS = {"max_items_per_brief", "source_age_days"}
|
|
14
|
+
_BOOL_FIELDS = {"tavily_enabled"}
|
|
15
|
+
_DICT_FIELDS = {"market_scope", "competitor_preferences"}
|
|
16
|
+
|
|
17
|
+
# Agent-generated onboarding.json often uses simplified field names.
|
|
18
|
+
# Map them to canonical OnboardingResult field names.
|
|
19
|
+
_FIELD_ALIASES: dict[str, str] = {
|
|
20
|
+
"company": "company_or_org",
|
|
21
|
+
"industry": "industry_or_theme",
|
|
22
|
+
"title": "brief_title",
|
|
23
|
+
"audience": "audience_plain",
|
|
24
|
+
"language": "language_plain",
|
|
25
|
+
"output_language": "language_plain",
|
|
26
|
+
"cadence": "cadence_plain",
|
|
27
|
+
"source_style": "source_style_plain",
|
|
28
|
+
"output_style": "output_style_plain",
|
|
29
|
+
"focus_areas": "must_watch",
|
|
30
|
+
"monitoring_scope": "must_watch",
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def load_onboarding_result(path: str | Path) -> OnboardingResult:
|
|
35
|
+
"""Load an OnboardingResult from a JSON file.
|
|
36
|
+
|
|
37
|
+
Unknown fields are silently ignored.
|
|
38
|
+
Missing fields use dataclass defaults.
|
|
39
|
+
"""
|
|
40
|
+
text = Path(path).read_text(encoding="utf-8")
|
|
41
|
+
data = json.loads(text)
|
|
42
|
+
if not isinstance(data, dict):
|
|
43
|
+
raise ValueError(
|
|
44
|
+
f"Onboarding JSON must be a JSON object, got {type(data).__name__}."
|
|
45
|
+
)
|
|
46
|
+
# Apply field aliases (agent-generated JSON often uses short names)
|
|
47
|
+
aliased: dict[str, Any] = {}
|
|
48
|
+
for k, v in data.items():
|
|
49
|
+
canonical = _FIELD_ALIASES.get(k, k)
|
|
50
|
+
if canonical in aliased:
|
|
51
|
+
if k == canonical:
|
|
52
|
+
aliased[canonical] = v
|
|
53
|
+
continue # canonical keys override aliases; aliases otherwise keep first write
|
|
54
|
+
aliased[canonical] = v
|
|
55
|
+
|
|
56
|
+
# Keep only known fields; ignore unknown keys.
|
|
57
|
+
known = {k: _normalize_field(k, v) for k, v in aliased.items() if k in _DATACLASS_FIELDS}
|
|
58
|
+
return OnboardingResult(**known)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def _normalize_field(name: str, value: Any) -> Any:
|
|
62
|
+
if name in _LIST_FIELDS:
|
|
63
|
+
if value is None:
|
|
64
|
+
return []
|
|
65
|
+
if isinstance(value, list):
|
|
66
|
+
return [str(item).strip() for item in value if str(item).strip()]
|
|
67
|
+
if isinstance(value, str):
|
|
68
|
+
return [value.strip()] if value.strip() else []
|
|
69
|
+
raise ValueError(f"onboarding.{name} must be a string or list of strings")
|
|
70
|
+
if name in _INT_FIELDS:
|
|
71
|
+
if value in (None, ""):
|
|
72
|
+
return 0
|
|
73
|
+
try:
|
|
74
|
+
return int(value)
|
|
75
|
+
except (TypeError, ValueError) as exc:
|
|
76
|
+
raise ValueError(f"onboarding.{name} must be an integer") from exc
|
|
77
|
+
if name in _BOOL_FIELDS:
|
|
78
|
+
if isinstance(value, bool):
|
|
79
|
+
return value
|
|
80
|
+
if isinstance(value, str):
|
|
81
|
+
return value.strip().lower() in {"1", "true", "yes", "y", "on"}
|
|
82
|
+
return bool(value)
|
|
83
|
+
if name in _DICT_FIELDS:
|
|
84
|
+
if value is None:
|
|
85
|
+
return {}
|
|
86
|
+
if isinstance(value, dict):
|
|
87
|
+
return value
|
|
88
|
+
raise ValueError(f"onboarding.{name} must be a JSON object")
|
|
89
|
+
if isinstance(value, (str, int, float, bool)) or value is None:
|
|
90
|
+
return "" if value is None else str(value) if not isinstance(value, bool) else value
|
|
91
|
+
raise ValueError(f"onboarding.{name} must be a scalar value")
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def save_onboarding_result(result: OnboardingResult, path: str | Path) -> None:
|
|
95
|
+
"""Save an OnboardingResult to a JSON file (UTF-8, pretty-printed)."""
|
|
96
|
+
from dataclasses import asdict
|
|
97
|
+
|
|
98
|
+
dest = Path(path)
|
|
99
|
+
dest.parent.mkdir(parents=True, exist_ok=True)
|
|
100
|
+
dest.write_text(
|
|
101
|
+
json.dumps(asdict(result), ensure_ascii=False, indent=2) + "\n",
|
|
102
|
+
encoding="utf-8",
|
|
103
|
+
)
|
|
@@ -0,0 +1,484 @@
|
|
|
1
|
+
"""Map OnboardingResult to InitProfile.
|
|
2
|
+
|
|
3
|
+
All mapping is business-language → internal fields.
|
|
4
|
+
Users never see source_profile, selector_max_items, etc.
|
|
5
|
+
|
|
6
|
+
Architecture principle:
|
|
7
|
+
- user.md = rich user profile and task context (primary semantic layer)
|
|
8
|
+
- config.yaml = operational run settings only
|
|
9
|
+
- sources.yaml = approved or discovered sources only
|
|
10
|
+
- source.mode = llm_decide by default
|
|
11
|
+
- industry packs = optional seed packs, not the source of truth
|
|
12
|
+
"""
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
from multi_agent_brief.onboarding.schema import OnboardingResult
|
|
16
|
+
from multi_agent_brief.sources.industry_packs import INDUSTRY_PACKS
|
|
17
|
+
from multi_agent_brief.audience.profiles import map_audience_to_profile
|
|
18
|
+
from multi_agent_brief.workspace.init_profile import InitProfile
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
# ── language mapping ────────────────────────────────────────────────
|
|
22
|
+
|
|
23
|
+
_LANG_MAP: dict[str, str] = {
|
|
24
|
+
"zh": "zh-CN",
|
|
25
|
+
"zh-cn": "zh-CN",
|
|
26
|
+
"zh_cn": "zh-CN",
|
|
27
|
+
"中文": "zh-CN",
|
|
28
|
+
"chinese": "zh-CN",
|
|
29
|
+
"en": "en-US",
|
|
30
|
+
"en-us": "en-US",
|
|
31
|
+
"en_us": "en-US",
|
|
32
|
+
"english": "en-US",
|
|
33
|
+
"英文": "en-US",
|
|
34
|
+
"ja": "ja-JP",
|
|
35
|
+
"ja-jp": "ja-JP",
|
|
36
|
+
"ja_jp": "ja-JP",
|
|
37
|
+
"japanese": "ja-JP",
|
|
38
|
+
"日文": "ja-JP",
|
|
39
|
+
"bilingual": "bilingual",
|
|
40
|
+
"dual language": "bilingual",
|
|
41
|
+
"中英": "bilingual",
|
|
42
|
+
"双语": "bilingual",
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
_DEFAULT_LANG = "en-US"
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def normalize_language(text: str) -> str:
|
|
49
|
+
t = text.strip().lower()
|
|
50
|
+
if not t:
|
|
51
|
+
return _DEFAULT_LANG
|
|
52
|
+
return _LANG_MAP.get(t, t)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
# ── cadence mapping ────────────────────────────────────────────────
|
|
56
|
+
|
|
57
|
+
_CADENCE_MAP: dict[str, str] = {
|
|
58
|
+
"daily": "daily",
|
|
59
|
+
"day": "daily",
|
|
60
|
+
"每日": "daily",
|
|
61
|
+
"weekly": "weekly",
|
|
62
|
+
"week": "weekly",
|
|
63
|
+
"weekly brief": "weekly",
|
|
64
|
+
"周报": "weekly",
|
|
65
|
+
"每周": "weekly",
|
|
66
|
+
"monthly": "monthly",
|
|
67
|
+
"month": "monthly",
|
|
68
|
+
"monthly brief": "monthly",
|
|
69
|
+
"月报": "monthly",
|
|
70
|
+
"每月": "monthly",
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
_DEFAULT_CADENCE = "weekly"
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def normalize_cadence(text: str) -> str:
|
|
77
|
+
t = text.strip().lower()
|
|
78
|
+
if not t:
|
|
79
|
+
return _DEFAULT_CADENCE
|
|
80
|
+
if t in _CADENCE_MAP:
|
|
81
|
+
return _CADENCE_MAP[t]
|
|
82
|
+
if any(k in t for k in ("daily", "每日", "every day")):
|
|
83
|
+
return "daily"
|
|
84
|
+
if any(k in t for k in ("monthly", "月报", "每月", "every month")):
|
|
85
|
+
return "monthly"
|
|
86
|
+
if any(k in t for k in ("weekly", "week", "周报", "每周", "every week")):
|
|
87
|
+
return "weekly"
|
|
88
|
+
return t
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
# ── audience mapping ───────────────────────────────────────────────
|
|
92
|
+
|
|
93
|
+
_AUDIENCE_MAP: dict[str, str] = {
|
|
94
|
+
"management": "management",
|
|
95
|
+
"executive": "management",
|
|
96
|
+
"ceo office": "management",
|
|
97
|
+
"ceo": "management",
|
|
98
|
+
"leadership": "management",
|
|
99
|
+
"管理层": "management",
|
|
100
|
+
"management team": "management",
|
|
101
|
+
"总裁办": "management",
|
|
102
|
+
"boss": "management",
|
|
103
|
+
"investment": "investment",
|
|
104
|
+
"portfolio": "investment",
|
|
105
|
+
"fund": "investment",
|
|
106
|
+
"investor": "investment",
|
|
107
|
+
"投资": "investment",
|
|
108
|
+
"持仓": "investment",
|
|
109
|
+
"基金": "investment",
|
|
110
|
+
"ir": "investor_relations",
|
|
111
|
+
"investor relations": "investor_relations",
|
|
112
|
+
"disclosure": "investor_relations",
|
|
113
|
+
"投关": "investor_relations",
|
|
114
|
+
"披露": "investor_relations",
|
|
115
|
+
"research": "research",
|
|
116
|
+
"analyst": "research",
|
|
117
|
+
"研究员": "research",
|
|
118
|
+
"legal": "compliance",
|
|
119
|
+
"compliance": "compliance",
|
|
120
|
+
"法务": "compliance",
|
|
121
|
+
"合规": "compliance",
|
|
122
|
+
"business": "business",
|
|
123
|
+
"operations": "business",
|
|
124
|
+
"sales": "business",
|
|
125
|
+
"业务": "business",
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
_DEFAULT_AUDIENCE = "management"
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def normalize_audience(text: str) -> str:
|
|
132
|
+
t = text.strip().lower()
|
|
133
|
+
if not t:
|
|
134
|
+
return _DEFAULT_AUDIENCE
|
|
135
|
+
if t in _AUDIENCE_MAP:
|
|
136
|
+
return _AUDIENCE_MAP[t]
|
|
137
|
+
if any(k in t for k in ("investor relations", "ir", "投关", "披露")):
|
|
138
|
+
return "investor_relations"
|
|
139
|
+
if any(k in t for k in ("investment", "portfolio", "fund", "investor", "投资", "持仓", "基金")):
|
|
140
|
+
return "investment"
|
|
141
|
+
if any(k in t for k in ("management", "executive", "ceo", "leadership", "管理层", "总裁", "boss")):
|
|
142
|
+
return "management"
|
|
143
|
+
if any(k in t for k in ("research", "analyst", "研究员")):
|
|
144
|
+
return "research"
|
|
145
|
+
if any(k in t for k in ("legal", "compliance", "法务", "合规")):
|
|
146
|
+
return "compliance"
|
|
147
|
+
if any(k in t for k in ("business", "operations", "sales", "业务")):
|
|
148
|
+
return "business"
|
|
149
|
+
return t
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
# ── source_profile mapping ─────────────────────────────────────────
|
|
153
|
+
|
|
154
|
+
_SOURCE_STYLE_MAP: dict[str, str] = {
|
|
155
|
+
"conservative": "conservative",
|
|
156
|
+
"official": "conservative",
|
|
157
|
+
"filing": "conservative",
|
|
158
|
+
"announcement": "conservative",
|
|
159
|
+
"官方": "conservative",
|
|
160
|
+
"公告": "conservative",
|
|
161
|
+
"research": "research",
|
|
162
|
+
"reliable research": "research",
|
|
163
|
+
"industry media": "research",
|
|
164
|
+
"研究": "research",
|
|
165
|
+
"行业媒体": "research",
|
|
166
|
+
"aggressive_signal": "aggressive_signal",
|
|
167
|
+
"radar": "aggressive_signal",
|
|
168
|
+
"broad scan": "aggressive_signal",
|
|
169
|
+
"social signals": "aggressive_signal",
|
|
170
|
+
"社媒": "aggressive_signal",
|
|
171
|
+
"信号": "aggressive_signal",
|
|
172
|
+
"llm_decide": "llm_decide",
|
|
173
|
+
"ai decide": "llm_decide",
|
|
174
|
+
"ai决定": "llm_decide",
|
|
175
|
+
"让ai决定": "llm_decide",
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
_DEFAULT_SOURCE_PROFILE = "llm_decide"
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def normalize_source_profile(text: str) -> str:
|
|
182
|
+
t = text.strip().lower()
|
|
183
|
+
if not t:
|
|
184
|
+
return _DEFAULT_SOURCE_PROFILE
|
|
185
|
+
if t in _SOURCE_STYLE_MAP:
|
|
186
|
+
return _SOURCE_STYLE_MAP[t]
|
|
187
|
+
if "official" in t or "filing" in t or "announcement" in t or "公告" in t:
|
|
188
|
+
return "conservative"
|
|
189
|
+
if "social" in t or "github" in t or "radar" in t or "broad" in t or "社媒" in t or "信号" in t:
|
|
190
|
+
return "aggressive_signal"
|
|
191
|
+
# Default to llm_decide for vague or unknown source preferences
|
|
192
|
+
return "llm_decide"
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
# ── search backend mapping ─────────────────────────────────────────
|
|
196
|
+
|
|
197
|
+
_SEARCH_BACKEND_MAP: dict[str, str] = {
|
|
198
|
+
"tavily": "tavily",
|
|
199
|
+
"exa": "exa",
|
|
200
|
+
"brave": "brave",
|
|
201
|
+
"firecrawl": "firecrawl",
|
|
202
|
+
"serper": "serper",
|
|
203
|
+
"runtime_websearch": "runtime_websearch",
|
|
204
|
+
"configure_later": "configure_later",
|
|
205
|
+
"稍后配置": "configure_later",
|
|
206
|
+
"稍后": "configure_later",
|
|
207
|
+
"none": "none",
|
|
208
|
+
"无": "none",
|
|
209
|
+
"不启用": "none",
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
_DEFAULT_SEARCH_BACKEND = ""
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
def normalize_search_backend(text: str) -> str:
|
|
216
|
+
t = text.strip().lower()
|
|
217
|
+
if not t:
|
|
218
|
+
return _DEFAULT_SEARCH_BACKEND
|
|
219
|
+
if t in _SEARCH_BACKEND_MAP:
|
|
220
|
+
return _SEARCH_BACKEND_MAP[t]
|
|
221
|
+
# Check for partial matches
|
|
222
|
+
for key, value in _SEARCH_BACKEND_MAP.items():
|
|
223
|
+
if key in t:
|
|
224
|
+
return value
|
|
225
|
+
return _DEFAULT_SEARCH_BACKEND
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
# ── role mapping ───────────────────────────────────────────────────
|
|
229
|
+
|
|
230
|
+
_ROLE_MAP: dict[str, str] = {
|
|
231
|
+
"strategy": "strategy_office",
|
|
232
|
+
"strategy office": "strategy_office",
|
|
233
|
+
"president office": "strategy_office",
|
|
234
|
+
"总裁办": "strategy_office",
|
|
235
|
+
"战略": "strategy_office",
|
|
236
|
+
"战略研究": "strategy_office",
|
|
237
|
+
"research": "research_analyst",
|
|
238
|
+
"research analyst": "research_analyst",
|
|
239
|
+
"行业研究": "research_analyst",
|
|
240
|
+
"研究员": "research_analyst",
|
|
241
|
+
"ir": "investor_relations",
|
|
242
|
+
"investor relations": "investor_relations",
|
|
243
|
+
"投资者关系": "investor_relations",
|
|
244
|
+
"投关": "investor_relations",
|
|
245
|
+
"policy": "policy_analyst",
|
|
246
|
+
"policy analyst": "policy_analyst",
|
|
247
|
+
"政策研究": "policy_analyst",
|
|
248
|
+
"政策": "policy_analyst",
|
|
249
|
+
"management": "management_support",
|
|
250
|
+
"management support": "management_support",
|
|
251
|
+
"管理层支持": "management_support",
|
|
252
|
+
"管理": "management_support",
|
|
253
|
+
"marketing": "management_support",
|
|
254
|
+
"市场": "management_support",
|
|
255
|
+
"市场部": "management_support",
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
_DEFAULT_ROLE = "strategy_office"
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
def normalize_role(text: str) -> str:
|
|
262
|
+
t = text.strip().lower()
|
|
263
|
+
if not t:
|
|
264
|
+
return _DEFAULT_ROLE
|
|
265
|
+
if t in _ROLE_MAP:
|
|
266
|
+
return _ROLE_MAP[t]
|
|
267
|
+
# Check for partial matches
|
|
268
|
+
for key, value in _ROLE_MAP.items():
|
|
269
|
+
if key in t:
|
|
270
|
+
return value
|
|
271
|
+
return _DEFAULT_ROLE
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
# ── audit strictness mapping ───────────────────────────────────────
|
|
275
|
+
|
|
276
|
+
_AUDIT_STRICTNESS_MAP: dict[str, str] = {
|
|
277
|
+
"standard": "standard",
|
|
278
|
+
"strict": "strict",
|
|
279
|
+
"lenient": "lenient",
|
|
280
|
+
"标准": "standard",
|
|
281
|
+
"严格": "strict",
|
|
282
|
+
"宽松": "lenient",
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
_DEFAULT_AUDIT_STRICTNESS = "standard"
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
def normalize_audit_strictness(text: str) -> str:
|
|
289
|
+
t = text.strip().lower()
|
|
290
|
+
if not t:
|
|
291
|
+
return _DEFAULT_AUDIT_STRICTNESS
|
|
292
|
+
if t in _AUDIT_STRICTNESS_MAP:
|
|
293
|
+
return _AUDIT_STRICTNESS_MAP[t]
|
|
294
|
+
if "strict" in t or "严格" in t:
|
|
295
|
+
return "strict"
|
|
296
|
+
if "lenient" in t or "宽松" in t:
|
|
297
|
+
return "lenient"
|
|
298
|
+
return _DEFAULT_AUDIT_STRICTNESS
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
# ── industry matching (optional seed pack only) ────────────────────
|
|
302
|
+
|
|
303
|
+
_REGISTERED_PACK_KEYS = set(INDUSTRY_PACKS.keys())
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
def _try_match_seed_pack(text: str) -> str:
|
|
307
|
+
"""Try to match user text to a registered industry pack key.
|
|
308
|
+
|
|
309
|
+
Returns the pack key if clearly matched, empty string otherwise.
|
|
310
|
+
Does NOT guess or invent unsupported slugs.
|
|
311
|
+
"""
|
|
312
|
+
t = text.strip().lower()
|
|
313
|
+
if not t:
|
|
314
|
+
return ""
|
|
315
|
+
# Exact match against registered pack keys
|
|
316
|
+
if t in _REGISTERED_PACK_KEYS:
|
|
317
|
+
return t
|
|
318
|
+
# Substring match against registered pack keys
|
|
319
|
+
for key in _REGISTERED_PACK_KEYS:
|
|
320
|
+
if key in t:
|
|
321
|
+
return key
|
|
322
|
+
return ""
|
|
323
|
+
|
|
324
|
+
|
|
325
|
+
def normalize_industry(text: str) -> str:
|
|
326
|
+
"""Normalize industry text to a registered pack key or empty string.
|
|
327
|
+
|
|
328
|
+
Returns a registered pack key if clearly matched, empty string otherwise.
|
|
329
|
+
Does NOT guess or invent unsupported slugs.
|
|
330
|
+
"""
|
|
331
|
+
return _try_match_seed_pack(text)
|
|
332
|
+
|
|
333
|
+
|
|
334
|
+
# ── selector_max_items ─────────────────────────────────────────────
|
|
335
|
+
|
|
336
|
+
_SELECTOR_MAP: dict[str, int] = {
|
|
337
|
+
"conservative": 20,
|
|
338
|
+
"research": 20,
|
|
339
|
+
"aggressive_signal": 28,
|
|
340
|
+
"llm_decide": 20,
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
|
|
344
|
+
# ── main mapper ────────────────────────────────────────────────────
|
|
345
|
+
|
|
346
|
+
def map_onboarding_to_profile(result: OnboardingResult) -> InitProfile:
|
|
347
|
+
"""Convert business-language OnboardingResult into an InitProfile.
|
|
348
|
+
|
|
349
|
+
Preserves raw user text in user.md-facing fields.
|
|
350
|
+
Only maps to registered pack keys when clearly matched.
|
|
351
|
+
Defaults to llm_decide mode for source discovery.
|
|
352
|
+
"""
|
|
353
|
+
profile = InitProfile()
|
|
354
|
+
|
|
355
|
+
language = normalize_language(result.language_plain)
|
|
356
|
+
profile.interface_language = language
|
|
357
|
+
profile.output_language = language
|
|
358
|
+
|
|
359
|
+
profile.company = result.company_or_org.strip()
|
|
360
|
+
|
|
361
|
+
# Role mapping
|
|
362
|
+
role_raw = getattr(result, "role_plain", "").strip()
|
|
363
|
+
if role_raw:
|
|
364
|
+
profile.role = normalize_role(role_raw)
|
|
365
|
+
|
|
366
|
+
# Preserve raw industry text for user.md
|
|
367
|
+
industry_raw = result.industry_or_theme.strip()
|
|
368
|
+
profile.industry_text = industry_raw
|
|
369
|
+
# Only set industry slug if clearly matches a registered pack
|
|
370
|
+
profile.industry = _try_match_seed_pack(industry_raw)
|
|
371
|
+
profile.optional_seed_pack = profile.industry # same as industry if matched
|
|
372
|
+
|
|
373
|
+
profile.audience = normalize_audience(result.audience_plain)
|
|
374
|
+
profile.audience_profile = map_audience_to_profile(result.audience_plain)
|
|
375
|
+
profile.cadence = normalize_cadence(result.cadence_plain)
|
|
376
|
+
profile.source_profile = normalize_source_profile(result.source_style_plain)
|
|
377
|
+
profile.selector_max_items = _SELECTOR_MAP.get(profile.source_profile, 20)
|
|
378
|
+
|
|
379
|
+
# Brief title: user-specified overrides auto-generation.
|
|
380
|
+
# task_objective is descriptive context, not a title or filename stem.
|
|
381
|
+
user_title = ""
|
|
382
|
+
if hasattr(result, "brief_title") and result.brief_title.strip():
|
|
383
|
+
user_title = result.brief_title.strip()
|
|
384
|
+
|
|
385
|
+
if user_title:
|
|
386
|
+
profile.brief_title = user_title
|
|
387
|
+
else:
|
|
388
|
+
industry_display = industry_raw or "Industry"
|
|
389
|
+
company = profile.company
|
|
390
|
+
cadence_word = profile.cadence.capitalize()
|
|
391
|
+
if language == "zh-CN" and company and company != "Sample Company":
|
|
392
|
+
profile.brief_title = f"{company} {industry_display}周报"
|
|
393
|
+
elif company and company != "Sample Company":
|
|
394
|
+
profile.brief_title = f"{company} {cadence_word} Brief"
|
|
395
|
+
else:
|
|
396
|
+
profile.brief_title = "Multi-Agent Brief"
|
|
397
|
+
|
|
398
|
+
# Focus areas: preserve raw must_watch items
|
|
399
|
+
base_focus = ["company", "industry", "policy", "competitors", "risk_events"]
|
|
400
|
+
seen: set[str] = set()
|
|
401
|
+
focus: list[str] = []
|
|
402
|
+
for item in base_focus:
|
|
403
|
+
if item not in seen:
|
|
404
|
+
focus.append(item)
|
|
405
|
+
seen.add(item)
|
|
406
|
+
for item in result.must_watch:
|
|
407
|
+
key = item.strip()
|
|
408
|
+
if key and key.lower() not in seen:
|
|
409
|
+
focus.append(key)
|
|
410
|
+
seen.add(key.lower())
|
|
411
|
+
profile.focus_areas = focus
|
|
412
|
+
|
|
413
|
+
# Store task objective and forbidden sources
|
|
414
|
+
if hasattr(result, "task_objective") and result.task_objective:
|
|
415
|
+
profile.task_objective = result.task_objective
|
|
416
|
+
if hasattr(result, "forbidden_sources") and result.forbidden_sources:
|
|
417
|
+
profile.forbidden_sources = list(result.forbidden_sources)
|
|
418
|
+
|
|
419
|
+
# Output formats: standard artifact set
|
|
420
|
+
profile.output_formats = ["markdown", "docx", "claim_ledger", "audit_report", "source_appendix"]
|
|
421
|
+
|
|
422
|
+
# If output_style_plain requests docx/word, include it
|
|
423
|
+
style_lower = (getattr(result, "output_style_plain", "") or "").lower()
|
|
424
|
+
if "docx" not in profile.output_formats and any(
|
|
425
|
+
k in style_lower for k in ("docx", "word", "docx格式", "word格式")
|
|
426
|
+
):
|
|
427
|
+
profile.output_formats.append("docx")
|
|
428
|
+
|
|
429
|
+
# New fields from extended onboarding
|
|
430
|
+
if hasattr(result, "max_items_per_brief") and result.max_items_per_brief:
|
|
431
|
+
profile.selector_max_items = result.max_items_per_brief
|
|
432
|
+
|
|
433
|
+
if hasattr(result, "source_age_days") and result.source_age_days:
|
|
434
|
+
profile.max_source_age_days = result.source_age_days
|
|
435
|
+
|
|
436
|
+
# Web search: handle search backend selection
|
|
437
|
+
search_backend = getattr(result, "search_backend_plain", "").strip()
|
|
438
|
+
if search_backend:
|
|
439
|
+
search_backend = normalize_search_backend(search_backend)
|
|
440
|
+
if search_backend == "tavily":
|
|
441
|
+
profile.tavily_enabled = True
|
|
442
|
+
profile.web_search_enabled = True
|
|
443
|
+
profile.web_search_mode = "external_api"
|
|
444
|
+
profile.search_backend = "tavily"
|
|
445
|
+
elif search_backend == "none":
|
|
446
|
+
profile.tavily_enabled = False
|
|
447
|
+
profile.web_search_enabled = False
|
|
448
|
+
profile.web_search_mode = "disabled"
|
|
449
|
+
profile.search_backend = ""
|
|
450
|
+
elif search_backend == "runtime_websearch":
|
|
451
|
+
profile.tavily_enabled = False
|
|
452
|
+
profile.web_search_enabled = True
|
|
453
|
+
profile.web_search_mode = "runtime_tool"
|
|
454
|
+
profile.search_backend = ""
|
|
455
|
+
elif search_backend == "configure_later":
|
|
456
|
+
profile.tavily_enabled = False
|
|
457
|
+
profile.web_search_enabled = True
|
|
458
|
+
profile.web_search_mode = "configure_later"
|
|
459
|
+
profile.search_backend = ""
|
|
460
|
+
else:
|
|
461
|
+
# Other backends (exa, brave, firecrawl, serper)
|
|
462
|
+
profile.tavily_enabled = False
|
|
463
|
+
profile.web_search_enabled = True
|
|
464
|
+
profile.web_search_mode = "external_api"
|
|
465
|
+
profile.search_backend = search_backend
|
|
466
|
+
|
|
467
|
+
# Also check the legacy tavily_enabled field
|
|
468
|
+
if getattr(result, "tavily_enabled", False):
|
|
469
|
+
profile.tavily_enabled = True
|
|
470
|
+
profile.web_search_enabled = True
|
|
471
|
+
if not profile.web_search_mode or profile.web_search_mode == "disabled":
|
|
472
|
+
profile.web_search_mode = "external_api"
|
|
473
|
+
if not profile.search_backend:
|
|
474
|
+
profile.search_backend = "tavily"
|
|
475
|
+
|
|
476
|
+
# Competitor monitoring
|
|
477
|
+
comp_prefs = getattr(result, "competitor_preferences", None) or {}
|
|
478
|
+
if comp_prefs.get("enabled"):
|
|
479
|
+
profile.competitor_module_enabled = True
|
|
480
|
+
names = comp_prefs.get("names", [])
|
|
481
|
+
if isinstance(names, list):
|
|
482
|
+
profile.competitor_names = [n.strip() for n in names if isinstance(n, str) and n.strip()]
|
|
483
|
+
|
|
484
|
+
return profile
|