local-deep-research 0.4.4__py3-none-any.whl → 0.5.2__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.
- local_deep_research/__init__.py +7 -0
- local_deep_research/__version__.py +1 -1
- local_deep_research/advanced_search_system/answer_decoding/__init__.py +5 -0
- local_deep_research/advanced_search_system/answer_decoding/browsecomp_answer_decoder.py +421 -0
- local_deep_research/advanced_search_system/candidate_exploration/README.md +219 -0
- local_deep_research/advanced_search_system/candidate_exploration/__init__.py +25 -0
- local_deep_research/advanced_search_system/candidate_exploration/adaptive_explorer.py +329 -0
- local_deep_research/advanced_search_system/candidate_exploration/base_explorer.py +341 -0
- local_deep_research/advanced_search_system/candidate_exploration/constraint_guided_explorer.py +436 -0
- local_deep_research/advanced_search_system/candidate_exploration/diversity_explorer.py +457 -0
- local_deep_research/advanced_search_system/candidate_exploration/parallel_explorer.py +250 -0
- local_deep_research/advanced_search_system/candidate_exploration/progressive_explorer.py +255 -0
- local_deep_research/advanced_search_system/candidates/__init__.py +5 -0
- local_deep_research/advanced_search_system/candidates/base_candidate.py +59 -0
- local_deep_research/advanced_search_system/constraint_checking/README.md +150 -0
- local_deep_research/advanced_search_system/constraint_checking/__init__.py +35 -0
- local_deep_research/advanced_search_system/constraint_checking/base_constraint_checker.py +122 -0
- local_deep_research/advanced_search_system/constraint_checking/constraint_checker.py +223 -0
- local_deep_research/advanced_search_system/constraint_checking/constraint_satisfaction_tracker.py +387 -0
- local_deep_research/advanced_search_system/constraint_checking/dual_confidence_checker.py +424 -0
- local_deep_research/advanced_search_system/constraint_checking/evidence_analyzer.py +174 -0
- local_deep_research/advanced_search_system/constraint_checking/intelligent_constraint_relaxer.py +503 -0
- local_deep_research/advanced_search_system/constraint_checking/rejection_engine.py +143 -0
- local_deep_research/advanced_search_system/constraint_checking/strict_checker.py +259 -0
- local_deep_research/advanced_search_system/constraint_checking/threshold_checker.py +213 -0
- local_deep_research/advanced_search_system/constraints/__init__.py +6 -0
- local_deep_research/advanced_search_system/constraints/base_constraint.py +58 -0
- local_deep_research/advanced_search_system/constraints/constraint_analyzer.py +143 -0
- local_deep_research/advanced_search_system/evidence/__init__.py +12 -0
- local_deep_research/advanced_search_system/evidence/base_evidence.py +57 -0
- local_deep_research/advanced_search_system/evidence/evaluator.py +159 -0
- local_deep_research/advanced_search_system/evidence/requirements.py +122 -0
- local_deep_research/advanced_search_system/filters/base_filter.py +3 -1
- local_deep_research/advanced_search_system/filters/cross_engine_filter.py +8 -2
- local_deep_research/advanced_search_system/filters/journal_reputation_filter.py +43 -29
- local_deep_research/advanced_search_system/findings/repository.py +54 -17
- local_deep_research/advanced_search_system/knowledge/standard_knowledge.py +3 -1
- local_deep_research/advanced_search_system/query_generation/adaptive_query_generator.py +405 -0
- local_deep_research/advanced_search_system/questions/__init__.py +16 -0
- local_deep_research/advanced_search_system/questions/atomic_fact_question.py +171 -0
- local_deep_research/advanced_search_system/questions/browsecomp_question.py +287 -0
- local_deep_research/advanced_search_system/questions/decomposition_question.py +13 -4
- local_deep_research/advanced_search_system/questions/entity_aware_question.py +184 -0
- local_deep_research/advanced_search_system/questions/standard_question.py +9 -3
- local_deep_research/advanced_search_system/search_optimization/cross_constraint_manager.py +624 -0
- local_deep_research/advanced_search_system/source_management/diversity_manager.py +613 -0
- local_deep_research/advanced_search_system/strategies/__init__.py +42 -0
- local_deep_research/advanced_search_system/strategies/adaptive_decomposition_strategy.py +564 -0
- local_deep_research/advanced_search_system/strategies/base_strategy.py +4 -4
- local_deep_research/advanced_search_system/strategies/browsecomp_entity_strategy.py +1031 -0
- local_deep_research/advanced_search_system/strategies/browsecomp_optimized_strategy.py +778 -0
- local_deep_research/advanced_search_system/strategies/concurrent_dual_confidence_strategy.py +446 -0
- local_deep_research/advanced_search_system/strategies/constrained_search_strategy.py +1348 -0
- local_deep_research/advanced_search_system/strategies/constraint_parallel_strategy.py +522 -0
- local_deep_research/advanced_search_system/strategies/direct_search_strategy.py +217 -0
- local_deep_research/advanced_search_system/strategies/dual_confidence_strategy.py +320 -0
- local_deep_research/advanced_search_system/strategies/dual_confidence_with_rejection.py +219 -0
- local_deep_research/advanced_search_system/strategies/early_stop_constrained_strategy.py +369 -0
- local_deep_research/advanced_search_system/strategies/entity_aware_source_strategy.py +140 -0
- local_deep_research/advanced_search_system/strategies/evidence_based_strategy.py +1248 -0
- local_deep_research/advanced_search_system/strategies/evidence_based_strategy_v2.py +1337 -0
- local_deep_research/advanced_search_system/strategies/focused_iteration_strategy.py +537 -0
- local_deep_research/advanced_search_system/strategies/improved_evidence_based_strategy.py +782 -0
- local_deep_research/advanced_search_system/strategies/iterative_reasoning_strategy.py +760 -0
- local_deep_research/advanced_search_system/strategies/iterdrag_strategy.py +55 -21
- local_deep_research/advanced_search_system/strategies/llm_driven_modular_strategy.py +865 -0
- local_deep_research/advanced_search_system/strategies/modular_strategy.py +1142 -0
- local_deep_research/advanced_search_system/strategies/parallel_constrained_strategy.py +506 -0
- local_deep_research/advanced_search_system/strategies/parallel_search_strategy.py +34 -16
- local_deep_research/advanced_search_system/strategies/rapid_search_strategy.py +29 -9
- local_deep_research/advanced_search_system/strategies/recursive_decomposition_strategy.py +492 -0
- local_deep_research/advanced_search_system/strategies/smart_decomposition_strategy.py +284 -0
- local_deep_research/advanced_search_system/strategies/smart_query_strategy.py +515 -0
- local_deep_research/advanced_search_system/strategies/source_based_strategy.py +48 -24
- local_deep_research/advanced_search_system/strategies/standard_strategy.py +34 -14
- local_deep_research/advanced_search_system/tools/base_tool.py +7 -2
- local_deep_research/api/benchmark_functions.py +6 -2
- local_deep_research/api/research_functions.py +10 -4
- local_deep_research/benchmarks/__init__.py +9 -7
- local_deep_research/benchmarks/benchmark_functions.py +6 -2
- local_deep_research/benchmarks/cli/benchmark_commands.py +27 -10
- local_deep_research/benchmarks/cli.py +38 -13
- local_deep_research/benchmarks/comparison/__init__.py +4 -2
- local_deep_research/benchmarks/comparison/evaluator.py +316 -239
- local_deep_research/benchmarks/datasets/__init__.py +1 -1
- local_deep_research/benchmarks/datasets/base.py +91 -72
- local_deep_research/benchmarks/datasets/browsecomp.py +54 -33
- local_deep_research/benchmarks/datasets/custom_dataset_template.py +19 -19
- local_deep_research/benchmarks/datasets/simpleqa.py +14 -14
- local_deep_research/benchmarks/datasets/utils.py +48 -29
- local_deep_research/benchmarks/datasets.py +4 -11
- local_deep_research/benchmarks/efficiency/__init__.py +8 -4
- local_deep_research/benchmarks/efficiency/resource_monitor.py +223 -171
- local_deep_research/benchmarks/efficiency/speed_profiler.py +62 -48
- local_deep_research/benchmarks/evaluators/browsecomp.py +3 -1
- local_deep_research/benchmarks/evaluators/composite.py +6 -2
- local_deep_research/benchmarks/evaluators/simpleqa.py +36 -13
- local_deep_research/benchmarks/graders.py +32 -10
- local_deep_research/benchmarks/metrics/README.md +1 -1
- local_deep_research/benchmarks/metrics/calculation.py +25 -10
- local_deep_research/benchmarks/metrics/reporting.py +7 -3
- local_deep_research/benchmarks/metrics/visualization.py +42 -23
- local_deep_research/benchmarks/metrics.py +1 -1
- local_deep_research/benchmarks/optimization/__init__.py +3 -1
- local_deep_research/benchmarks/optimization/api.py +7 -1
- local_deep_research/benchmarks/optimization/optuna_optimizer.py +75 -26
- local_deep_research/benchmarks/runners.py +48 -15
- local_deep_research/citation_handler.py +65 -92
- local_deep_research/citation_handlers/__init__.py +15 -0
- local_deep_research/citation_handlers/base_citation_handler.py +70 -0
- local_deep_research/citation_handlers/forced_answer_citation_handler.py +179 -0
- local_deep_research/citation_handlers/precision_extraction_handler.py +550 -0
- local_deep_research/citation_handlers/standard_citation_handler.py +80 -0
- local_deep_research/config/llm_config.py +271 -169
- local_deep_research/config/search_config.py +14 -5
- local_deep_research/defaults/__init__.py +0 -1
- local_deep_research/metrics/__init__.py +13 -0
- local_deep_research/metrics/database.py +58 -0
- local_deep_research/metrics/db_models.py +115 -0
- local_deep_research/metrics/migrate_add_provider_to_token_usage.py +148 -0
- local_deep_research/metrics/migrate_call_stack_tracking.py +105 -0
- local_deep_research/metrics/migrate_enhanced_tracking.py +75 -0
- local_deep_research/metrics/migrate_research_ratings.py +31 -0
- local_deep_research/metrics/models.py +61 -0
- local_deep_research/metrics/pricing/__init__.py +12 -0
- local_deep_research/metrics/pricing/cost_calculator.py +237 -0
- local_deep_research/metrics/pricing/pricing_cache.py +143 -0
- local_deep_research/metrics/pricing/pricing_fetcher.py +240 -0
- local_deep_research/metrics/query_utils.py +51 -0
- local_deep_research/metrics/search_tracker.py +380 -0
- local_deep_research/metrics/token_counter.py +1078 -0
- local_deep_research/migrate_db.py +3 -1
- local_deep_research/report_generator.py +22 -8
- local_deep_research/search_system.py +390 -9
- local_deep_research/test_migration.py +15 -5
- local_deep_research/utilities/db_utils.py +7 -4
- local_deep_research/utilities/es_utils.py +115 -104
- local_deep_research/utilities/llm_utils.py +15 -5
- local_deep_research/utilities/log_utils.py +151 -0
- local_deep_research/utilities/search_cache.py +387 -0
- local_deep_research/utilities/search_utilities.py +14 -6
- local_deep_research/utilities/threading_utils.py +92 -0
- local_deep_research/utilities/url_utils.py +6 -0
- local_deep_research/web/api.py +347 -0
- local_deep_research/web/app.py +13 -17
- local_deep_research/web/app_factory.py +71 -66
- local_deep_research/web/database/migrate_to_ldr_db.py +12 -4
- local_deep_research/web/database/migrations.py +20 -3
- local_deep_research/web/database/models.py +74 -25
- local_deep_research/web/database/schema_upgrade.py +49 -29
- local_deep_research/web/models/database.py +63 -83
- local_deep_research/web/routes/api_routes.py +56 -22
- local_deep_research/web/routes/benchmark_routes.py +4 -1
- local_deep_research/web/routes/globals.py +22 -0
- local_deep_research/web/routes/history_routes.py +71 -46
- local_deep_research/web/routes/metrics_routes.py +1155 -0
- local_deep_research/web/routes/research_routes.py +192 -54
- local_deep_research/web/routes/settings_routes.py +156 -55
- local_deep_research/web/services/research_service.py +412 -251
- local_deep_research/web/services/resource_service.py +36 -11
- local_deep_research/web/services/settings_manager.py +55 -17
- local_deep_research/web/services/settings_service.py +12 -4
- local_deep_research/web/services/socket_service.py +295 -188
- local_deep_research/web/static/css/custom_dropdown.css +180 -0
- local_deep_research/web/static/css/styles.css +39 -1
- local_deep_research/web/static/js/components/detail.js +633 -267
- local_deep_research/web/static/js/components/details.js +751 -0
- local_deep_research/web/static/js/components/fallback/formatting.js +11 -11
- local_deep_research/web/static/js/components/fallback/ui.js +23 -23
- local_deep_research/web/static/js/components/history.js +76 -76
- local_deep_research/web/static/js/components/logpanel.js +61 -13
- local_deep_research/web/static/js/components/progress.js +13 -2
- local_deep_research/web/static/js/components/research.js +99 -12
- local_deep_research/web/static/js/components/results.js +239 -106
- local_deep_research/web/static/js/main.js +40 -40
- local_deep_research/web/static/js/services/audio.js +1 -1
- local_deep_research/web/static/js/services/formatting.js +11 -11
- local_deep_research/web/static/js/services/keyboard.js +157 -0
- local_deep_research/web/static/js/services/pdf.js +80 -80
- local_deep_research/web/static/sounds/README.md +1 -1
- local_deep_research/web/templates/base.html +1 -0
- local_deep_research/web/templates/components/log_panel.html +7 -1
- local_deep_research/web/templates/components/mobile_nav.html +1 -1
- local_deep_research/web/templates/components/sidebar.html +3 -0
- local_deep_research/web/templates/pages/cost_analytics.html +1245 -0
- local_deep_research/web/templates/pages/details.html +325 -24
- local_deep_research/web/templates/pages/history.html +1 -1
- local_deep_research/web/templates/pages/metrics.html +1929 -0
- local_deep_research/web/templates/pages/progress.html +2 -2
- local_deep_research/web/templates/pages/research.html +53 -17
- local_deep_research/web/templates/pages/results.html +12 -1
- local_deep_research/web/templates/pages/star_reviews.html +803 -0
- local_deep_research/web/utils/formatters.py +9 -3
- local_deep_research/web_search_engines/default_search_engines.py +5 -3
- local_deep_research/web_search_engines/engines/full_search.py +8 -2
- local_deep_research/web_search_engines/engines/meta_search_engine.py +59 -20
- local_deep_research/web_search_engines/engines/search_engine_arxiv.py +19 -6
- local_deep_research/web_search_engines/engines/search_engine_brave.py +6 -2
- local_deep_research/web_search_engines/engines/search_engine_ddg.py +3 -1
- local_deep_research/web_search_engines/engines/search_engine_elasticsearch.py +81 -58
- local_deep_research/web_search_engines/engines/search_engine_github.py +46 -15
- local_deep_research/web_search_engines/engines/search_engine_google_pse.py +16 -6
- local_deep_research/web_search_engines/engines/search_engine_guardian.py +39 -15
- local_deep_research/web_search_engines/engines/search_engine_local.py +58 -25
- local_deep_research/web_search_engines/engines/search_engine_local_all.py +15 -5
- local_deep_research/web_search_engines/engines/search_engine_pubmed.py +63 -21
- local_deep_research/web_search_engines/engines/search_engine_searxng.py +37 -11
- local_deep_research/web_search_engines/engines/search_engine_semantic_scholar.py +27 -9
- local_deep_research/web_search_engines/engines/search_engine_serpapi.py +12 -4
- local_deep_research/web_search_engines/engines/search_engine_wayback.py +31 -10
- local_deep_research/web_search_engines/engines/search_engine_wikipedia.py +12 -3
- local_deep_research/web_search_engines/search_engine_base.py +83 -35
- local_deep_research/web_search_engines/search_engine_factory.py +25 -8
- local_deep_research/web_search_engines/search_engines_config.py +9 -3
- {local_deep_research-0.4.4.dist-info → local_deep_research-0.5.2.dist-info}/METADATA +7 -1
- local_deep_research-0.5.2.dist-info/RECORD +265 -0
- local_deep_research-0.4.4.dist-info/RECORD +0 -177
- {local_deep_research-0.4.4.dist-info → local_deep_research-0.5.2.dist-info}/WHEEL +0 -0
- {local_deep_research-0.4.4.dist-info → local_deep_research-0.5.2.dist-info}/entry_points.txt +0 -0
- {local_deep_research-0.4.4.dist-info → local_deep_research-0.5.2.dist-info}/licenses/LICENSE +0 -0
local_deep_research/advanced_search_system/candidate_exploration/constraint_guided_explorer.py
ADDED
@@ -0,0 +1,436 @@
|
|
1
|
+
"""
|
2
|
+
Constraint-guided candidate explorer implementation.
|
3
|
+
|
4
|
+
This explorer uses constraints to guide the search process, prioritizing
|
5
|
+
searches that are likely to find candidates satisfying the constraints.
|
6
|
+
"""
|
7
|
+
|
8
|
+
import time
|
9
|
+
from typing import List, Optional
|
10
|
+
|
11
|
+
from loguru import logger
|
12
|
+
|
13
|
+
from ..candidates.base_candidate import Candidate
|
14
|
+
from ..constraints.base_constraint import Constraint, ConstraintType
|
15
|
+
from .base_explorer import (
|
16
|
+
BaseCandidateExplorer,
|
17
|
+
ExplorationResult,
|
18
|
+
ExplorationStrategy,
|
19
|
+
)
|
20
|
+
|
21
|
+
|
22
|
+
class ConstraintGuidedExplorer(BaseCandidateExplorer):
|
23
|
+
"""
|
24
|
+
Constraint-guided candidate explorer.
|
25
|
+
|
26
|
+
This explorer:
|
27
|
+
1. Prioritizes searches based on constraint importance
|
28
|
+
2. Uses constraint-specific search strategies
|
29
|
+
3. Validates candidates against constraints early
|
30
|
+
4. Focuses on constraint satisfaction over quantity
|
31
|
+
"""
|
32
|
+
|
33
|
+
def __init__(
|
34
|
+
self,
|
35
|
+
*args,
|
36
|
+
constraint_weight_threshold: float = 0.7, # Focus on constraints above this weight
|
37
|
+
early_validation: bool = True, # Validate candidates during search
|
38
|
+
**kwargs,
|
39
|
+
):
|
40
|
+
"""
|
41
|
+
Initialize constraint-guided explorer.
|
42
|
+
|
43
|
+
Args:
|
44
|
+
constraint_weight_threshold: Focus on constraints above this weight
|
45
|
+
early_validation: Whether to validate candidates during exploration
|
46
|
+
"""
|
47
|
+
super().__init__(*args, **kwargs)
|
48
|
+
self.constraint_weight_threshold = constraint_weight_threshold
|
49
|
+
self.early_validation = early_validation
|
50
|
+
|
51
|
+
def explore(
|
52
|
+
self,
|
53
|
+
initial_query: str,
|
54
|
+
constraints: Optional[List[Constraint]] = None,
|
55
|
+
entity_type: Optional[str] = None,
|
56
|
+
) -> ExplorationResult:
|
57
|
+
"""Explore candidates using constraint-guided strategy."""
|
58
|
+
start_time = time.time()
|
59
|
+
logger.info(
|
60
|
+
f"Starting constraint-guided exploration for: {initial_query}"
|
61
|
+
)
|
62
|
+
|
63
|
+
if not constraints:
|
64
|
+
logger.warning(
|
65
|
+
"No constraints provided - falling back to basic search"
|
66
|
+
)
|
67
|
+
return self._basic_exploration(
|
68
|
+
initial_query, entity_type, start_time
|
69
|
+
)
|
70
|
+
|
71
|
+
all_candidates = []
|
72
|
+
exploration_paths = []
|
73
|
+
total_searched = 0
|
74
|
+
|
75
|
+
# Prioritize constraints by weight and type
|
76
|
+
prioritized_constraints = self._prioritize_constraints(constraints)
|
77
|
+
|
78
|
+
# Search for each constraint
|
79
|
+
for i, constraint in enumerate(prioritized_constraints):
|
80
|
+
if not self._should_continue_exploration(
|
81
|
+
start_time, len(all_candidates)
|
82
|
+
):
|
83
|
+
break
|
84
|
+
|
85
|
+
logger.info(
|
86
|
+
f"Exploring constraint {i + 1}/{len(prioritized_constraints)}: {constraint.value}"
|
87
|
+
)
|
88
|
+
|
89
|
+
# Generate constraint-specific queries
|
90
|
+
constraint_queries = self._generate_constraint_queries(
|
91
|
+
constraint, initial_query, entity_type
|
92
|
+
)
|
93
|
+
|
94
|
+
constraint_candidates = []
|
95
|
+
|
96
|
+
for query in constraint_queries[:3]: # Limit queries per constraint
|
97
|
+
if query.lower() in self.explored_queries:
|
98
|
+
continue
|
99
|
+
|
100
|
+
results = self._execute_search(query)
|
101
|
+
candidates = self._extract_candidates_from_results(
|
102
|
+
results, entity_type
|
103
|
+
)
|
104
|
+
|
105
|
+
# Early validation if enabled
|
106
|
+
if self.early_validation:
|
107
|
+
validated_candidates = self._early_validate_candidates(
|
108
|
+
candidates, constraint
|
109
|
+
)
|
110
|
+
constraint_candidates.extend(validated_candidates)
|
111
|
+
else:
|
112
|
+
constraint_candidates.extend(candidates)
|
113
|
+
|
114
|
+
total_searched += 1
|
115
|
+
exploration_paths.append(
|
116
|
+
f"Constraint '{constraint.value}': {query} -> {len(candidates)} candidates"
|
117
|
+
)
|
118
|
+
|
119
|
+
all_candidates.extend(constraint_candidates)
|
120
|
+
logger.info(
|
121
|
+
f"Found {len(constraint_candidates)} candidates for constraint: {constraint.value}"
|
122
|
+
)
|
123
|
+
|
124
|
+
# Cross-constraint exploration
|
125
|
+
if len(prioritized_constraints) > 1:
|
126
|
+
cross_candidates = self._cross_constraint_exploration(
|
127
|
+
prioritized_constraints[:2], initial_query, entity_type
|
128
|
+
)
|
129
|
+
all_candidates.extend(cross_candidates)
|
130
|
+
exploration_paths.append(
|
131
|
+
f"Cross-constraint search -> {len(cross_candidates)} candidates"
|
132
|
+
)
|
133
|
+
total_searched += 1
|
134
|
+
|
135
|
+
# Process final results
|
136
|
+
unique_candidates = self._deduplicate_candidates(all_candidates)
|
137
|
+
ranked_candidates = self._rank_by_constraint_alignment(
|
138
|
+
unique_candidates, constraints, initial_query
|
139
|
+
)
|
140
|
+
final_candidates = ranked_candidates[: self.max_candidates]
|
141
|
+
|
142
|
+
elapsed_time = time.time() - start_time
|
143
|
+
logger.info(
|
144
|
+
f"Constraint-guided exploration completed: {len(final_candidates)} candidates in {elapsed_time:.1f}s"
|
145
|
+
)
|
146
|
+
|
147
|
+
return ExplorationResult(
|
148
|
+
candidates=final_candidates,
|
149
|
+
total_searched=total_searched,
|
150
|
+
unique_candidates=len(unique_candidates),
|
151
|
+
exploration_paths=exploration_paths,
|
152
|
+
metadata={
|
153
|
+
"strategy": "constraint_guided",
|
154
|
+
"constraints_used": len(prioritized_constraints),
|
155
|
+
"early_validation": self.early_validation,
|
156
|
+
"entity_type": entity_type,
|
157
|
+
},
|
158
|
+
elapsed_time=elapsed_time,
|
159
|
+
strategy_used=ExplorationStrategy.CONSTRAINT_GUIDED,
|
160
|
+
)
|
161
|
+
|
162
|
+
def generate_exploration_queries(
|
163
|
+
self,
|
164
|
+
base_query: str,
|
165
|
+
found_candidates: List[Candidate],
|
166
|
+
constraints: Optional[List[Constraint]] = None,
|
167
|
+
) -> List[str]:
|
168
|
+
"""Generate constraint-guided exploration queries."""
|
169
|
+
if not constraints:
|
170
|
+
return [base_query]
|
171
|
+
|
172
|
+
queries = []
|
173
|
+
|
174
|
+
# Generate queries for each constraint
|
175
|
+
for constraint in constraints[:3]: # Limit to avoid too many queries
|
176
|
+
constraint_queries = self._generate_constraint_queries(
|
177
|
+
constraint, base_query
|
178
|
+
)
|
179
|
+
queries.extend(constraint_queries[:2]) # Top 2 per constraint
|
180
|
+
|
181
|
+
# Generate queries combining multiple constraints
|
182
|
+
if len(constraints) > 1:
|
183
|
+
combined_query = self._combine_constraints_query(
|
184
|
+
base_query, constraints[:2]
|
185
|
+
)
|
186
|
+
if combined_query:
|
187
|
+
queries.append(combined_query)
|
188
|
+
|
189
|
+
return queries
|
190
|
+
|
191
|
+
def _prioritize_constraints(
|
192
|
+
self, constraints: List[Constraint]
|
193
|
+
) -> List[Constraint]:
|
194
|
+
"""Prioritize constraints by weight and type."""
|
195
|
+
# Sort by weight (descending) and then by type priority
|
196
|
+
type_priority = {
|
197
|
+
ConstraintType.NAME_PATTERN: 1,
|
198
|
+
ConstraintType.PROPERTY: 2,
|
199
|
+
ConstraintType.EVENT: 3,
|
200
|
+
ConstraintType.LOCATION: 4,
|
201
|
+
ConstraintType.TEMPORAL: 5,
|
202
|
+
ConstraintType.STATISTIC: 6,
|
203
|
+
ConstraintType.COMPARISON: 7,
|
204
|
+
ConstraintType.EXISTENCE: 8,
|
205
|
+
}
|
206
|
+
|
207
|
+
return sorted(
|
208
|
+
constraints,
|
209
|
+
key=lambda c: (c.weight, type_priority.get(c.type, 9)),
|
210
|
+
reverse=True,
|
211
|
+
)
|
212
|
+
|
213
|
+
def _generate_constraint_queries(
|
214
|
+
self,
|
215
|
+
constraint: Constraint,
|
216
|
+
base_query: str,
|
217
|
+
entity_type: Optional[str] = None,
|
218
|
+
) -> List[str]:
|
219
|
+
"""Generate search queries specific to a constraint."""
|
220
|
+
queries = []
|
221
|
+
|
222
|
+
# Base constraint query
|
223
|
+
if entity_type:
|
224
|
+
queries.append(f"{entity_type} {constraint.value}")
|
225
|
+
else:
|
226
|
+
queries.append(f"{base_query} {constraint.value}")
|
227
|
+
|
228
|
+
# Constraint-type specific queries
|
229
|
+
if constraint.type == ConstraintType.NAME_PATTERN:
|
230
|
+
queries.extend(
|
231
|
+
self._name_pattern_queries(constraint, base_query, entity_type)
|
232
|
+
)
|
233
|
+
elif constraint.type == ConstraintType.PROPERTY:
|
234
|
+
queries.extend(
|
235
|
+
self._property_queries(constraint, base_query, entity_type)
|
236
|
+
)
|
237
|
+
elif constraint.type == ConstraintType.EVENT:
|
238
|
+
queries.extend(
|
239
|
+
self._event_queries(constraint, base_query, entity_type)
|
240
|
+
)
|
241
|
+
elif constraint.type == ConstraintType.LOCATION:
|
242
|
+
queries.extend(
|
243
|
+
self._location_queries(constraint, base_query, entity_type)
|
244
|
+
)
|
245
|
+
|
246
|
+
return queries
|
247
|
+
|
248
|
+
def _name_pattern_queries(
|
249
|
+
self,
|
250
|
+
constraint: Constraint,
|
251
|
+
base_query: str,
|
252
|
+
entity_type: Optional[str],
|
253
|
+
) -> List[str]:
|
254
|
+
"""Generate queries for name pattern constraints."""
|
255
|
+
queries = []
|
256
|
+
|
257
|
+
if "body part" in constraint.value.lower():
|
258
|
+
body_parts = [
|
259
|
+
"arm",
|
260
|
+
"leg",
|
261
|
+
"foot",
|
262
|
+
"hand",
|
263
|
+
"eye",
|
264
|
+
"ear",
|
265
|
+
"head",
|
266
|
+
"tooth",
|
267
|
+
"nose",
|
268
|
+
"heart",
|
269
|
+
]
|
270
|
+
for part in body_parts[:3]: # Sample a few
|
271
|
+
if entity_type:
|
272
|
+
queries.append(f"{entity_type} {part}")
|
273
|
+
else:
|
274
|
+
queries.append(f"{base_query} {part} name")
|
275
|
+
|
276
|
+
return queries
|
277
|
+
|
278
|
+
def _property_queries(
|
279
|
+
self,
|
280
|
+
constraint: Constraint,
|
281
|
+
base_query: str,
|
282
|
+
entity_type: Optional[str],
|
283
|
+
) -> List[str]:
|
284
|
+
"""Generate queries for property constraints."""
|
285
|
+
base = entity_type or base_query
|
286
|
+
return [
|
287
|
+
f"{base} with {constraint.value}",
|
288
|
+
f"{base} that {constraint.value}",
|
289
|
+
f"{constraint.value} {base}",
|
290
|
+
]
|
291
|
+
|
292
|
+
def _event_queries(
|
293
|
+
self,
|
294
|
+
constraint: Constraint,
|
295
|
+
base_query: str,
|
296
|
+
entity_type: Optional[str],
|
297
|
+
) -> List[str]:
|
298
|
+
"""Generate queries for event constraints."""
|
299
|
+
base = entity_type or base_query
|
300
|
+
return [
|
301
|
+
f"{base} {constraint.value} incident",
|
302
|
+
f"{base} {constraint.value} accident",
|
303
|
+
f"{constraint.value} at {base}",
|
304
|
+
]
|
305
|
+
|
306
|
+
def _location_queries(
|
307
|
+
self,
|
308
|
+
constraint: Constraint,
|
309
|
+
base_query: str,
|
310
|
+
entity_type: Optional[str],
|
311
|
+
) -> List[str]:
|
312
|
+
"""Generate queries for location constraints."""
|
313
|
+
return [
|
314
|
+
f"{constraint.value} {base_query}",
|
315
|
+
f"{base_query} in {constraint.value}",
|
316
|
+
f"{constraint.value} locations",
|
317
|
+
]
|
318
|
+
|
319
|
+
def _cross_constraint_exploration(
|
320
|
+
self,
|
321
|
+
constraints: List[Constraint],
|
322
|
+
base_query: str,
|
323
|
+
entity_type: Optional[str],
|
324
|
+
) -> List[Candidate]:
|
325
|
+
"""Explore candidates satisfying multiple constraints."""
|
326
|
+
if len(constraints) < 2:
|
327
|
+
return []
|
328
|
+
|
329
|
+
# Combine top 2 constraints
|
330
|
+
combined_query = self._combine_constraints_query(
|
331
|
+
base_query, constraints
|
332
|
+
)
|
333
|
+
|
334
|
+
if (
|
335
|
+
combined_query
|
336
|
+
and combined_query.lower() not in self.explored_queries
|
337
|
+
):
|
338
|
+
results = self._execute_search(combined_query)
|
339
|
+
return self._extract_candidates_from_results(results, entity_type)
|
340
|
+
|
341
|
+
return []
|
342
|
+
|
343
|
+
def _combine_constraints_query(
|
344
|
+
self, base_query: str, constraints: List[Constraint]
|
345
|
+
) -> Optional[str]:
|
346
|
+
"""Combine multiple constraints into a single query."""
|
347
|
+
if len(constraints) < 2:
|
348
|
+
return None
|
349
|
+
|
350
|
+
constraint_values = [c.value for c in constraints[:2]]
|
351
|
+
return f"{base_query} {' AND '.join(constraint_values)}"
|
352
|
+
|
353
|
+
def _early_validate_candidates(
|
354
|
+
self, candidates: List[Candidate], constraint: Constraint
|
355
|
+
) -> List[Candidate]:
|
356
|
+
"""Perform early validation of candidates against constraint."""
|
357
|
+
if not candidates or constraint.type != ConstraintType.NAME_PATTERN:
|
358
|
+
return candidates # Only validate name patterns for now
|
359
|
+
|
360
|
+
validated = []
|
361
|
+
|
362
|
+
for candidate in candidates:
|
363
|
+
if self._quick_name_validation(candidate.name, constraint):
|
364
|
+
validated.append(candidate)
|
365
|
+
|
366
|
+
return validated
|
367
|
+
|
368
|
+
def _quick_name_validation(
|
369
|
+
self, candidate_name: str, constraint: Constraint
|
370
|
+
) -> bool:
|
371
|
+
"""Quick validation of candidate name against constraint."""
|
372
|
+
if "body part" in constraint.value.lower():
|
373
|
+
body_parts = [
|
374
|
+
"arm",
|
375
|
+
"leg",
|
376
|
+
"foot",
|
377
|
+
"hand",
|
378
|
+
"eye",
|
379
|
+
"ear",
|
380
|
+
"head",
|
381
|
+
"tooth",
|
382
|
+
"nose",
|
383
|
+
"heart",
|
384
|
+
]
|
385
|
+
name_lower = candidate_name.lower()
|
386
|
+
return any(part in name_lower for part in body_parts)
|
387
|
+
|
388
|
+
return True # Default to accepting if can't validate
|
389
|
+
|
390
|
+
def _rank_by_constraint_alignment(
|
391
|
+
self,
|
392
|
+
candidates: List[Candidate],
|
393
|
+
constraints: List[Constraint],
|
394
|
+
base_query: str,
|
395
|
+
) -> List[Candidate]:
|
396
|
+
"""Rank candidates by alignment with constraints."""
|
397
|
+
for candidate in candidates:
|
398
|
+
# Simple scoring based on constraint alignment
|
399
|
+
score = 0.0
|
400
|
+
|
401
|
+
# Score based on name pattern constraints
|
402
|
+
for constraint in constraints:
|
403
|
+
if constraint.type == ConstraintType.NAME_PATTERN:
|
404
|
+
if self._quick_name_validation(candidate.name, constraint):
|
405
|
+
score += constraint.weight
|
406
|
+
|
407
|
+
candidate.constraint_alignment_score = score
|
408
|
+
|
409
|
+
# Sort by constraint alignment, then by relevance
|
410
|
+
ranked = self._rank_candidates_by_relevance(candidates, base_query)
|
411
|
+
return sorted(
|
412
|
+
ranked,
|
413
|
+
key=lambda c: getattr(c, "constraint_alignment_score", 0.0),
|
414
|
+
reverse=True,
|
415
|
+
)
|
416
|
+
|
417
|
+
def _basic_exploration(
|
418
|
+
self, initial_query: str, entity_type: Optional[str], start_time: float
|
419
|
+
) -> ExplorationResult:
|
420
|
+
"""Fallback basic exploration when no constraints provided."""
|
421
|
+
candidates = []
|
422
|
+
|
423
|
+
results = self._execute_search(initial_query)
|
424
|
+
candidates = self._extract_candidates_from_results(results, entity_type)
|
425
|
+
|
426
|
+
elapsed_time = time.time() - start_time
|
427
|
+
|
428
|
+
return ExplorationResult(
|
429
|
+
candidates=candidates[: self.max_candidates],
|
430
|
+
total_searched=1,
|
431
|
+
unique_candidates=len(candidates),
|
432
|
+
exploration_paths=[f"Basic search: {initial_query}"],
|
433
|
+
metadata={"strategy": "basic_fallback"},
|
434
|
+
elapsed_time=elapsed_time,
|
435
|
+
strategy_used=ExplorationStrategy.BREADTH_FIRST,
|
436
|
+
)
|